Compare commits

..

22 Commits

Author SHA1 Message Date
7baf7cfede 0.0.70 2018-01-18 12:13:17 -08:00
efd6718ea3 converted lesson to tutorial 2018-01-18 12:09:43 -08:00
057a1d66dc PID support (#242)
* updated block definitions

* updated dependency on common packages
2018-01-18 10:34:06 -08:00
5ddfcd5508 Prototype lesson 'Make it Move" (#241)
* Prototype lesson 'Make it Move"

* Wrong blockq type
2018-01-17 19:45:07 -08:00
00f0922189 Merge pull request #240 from Microsoft/line-detect-lesson
Prototype lesson for 'Line Detection'
2018-01-17 19:07:29 -08:00
41f4b64087 Add to gallerys 2018-01-17 18:52:49 -08:00
ea5ee1c007 Prototype lesson for 'Line Detection' 2018-01-17 17:02:11 -08:00
603e4c0fc1 0.0.69 2018-01-16 17:06:52 -08:00
e50c88008a updated gyrobox 2018-01-16 17:05:57 -08:00
f057964a50 pausing until sound is done in mood 2018-01-16 16:44:32 -08:00
2eda2061cf updated modified gyro boy 2018-01-16 16:26:49 -08:00
a4ebf4c746 moving moods in separate namespace 2018-01-16 16:21:02 -08:00
f1880897d4 0.0.68 2018-01-16 16:08:53 -08:00
ad2e82060d removing BrickLight blockIdenity notations 2018-01-16 15:59:40 -08:00
d1bb19e30e adding a mood block (image+sound+light) 2018-01-16 14:52:49 -08:00
280963d1eb 0.0.67 2018-01-15 23:58:31 -08:00
9fadf49b0e Support for multiple motors in "used" logic. (#238)
* handle registerion of dual / single motors

* updated puppy
2018-01-15 23:57:21 -08:00
3c2be25384 some core set adapted codes 2018-01-15 21:27:19 -08:00
e1f623a94d Added description to timers 2018-01-15 03:41:14 -08:00
cb5f9648f5 fixed sound name 2018-01-14 19:49:40 -08:00
9158cfe4f6 0.0.66 2018-01-13 08:31:38 -08:00
0b763978f2 gyro boy improvements (#236)
gyro boy improvements
2018-01-13 08:31:10 -08:00
51 changed files with 1938 additions and 168 deletions

View File

@ -1,5 +1,11 @@
# @extends
## Lessons #Lessons
* [Lessons](/lessons)
* [Make it move](/lessons/make-it-move)
* [Line detection](/lessons/line-detection)
## Reference #reference
* [Reference](/reference)

View File

@ -0,0 +1,178 @@
# Gyroboy
Work in progress
```blocks
let motorSpeed1 = 0
let motorSpeed2 = 0
let motorSpeed3 = 0
let motorSpeed = 0
let fallen = false
let motorSpeed0 = 0
let oldControlDrive = 0
let controlDrive = 0
let power = 0
let motorAngle = 0
let gyroAngle = 0
let controlSteering = 0
let state = 0
let motorPosition = 0
let temp = 0
let gyroRate = 0
let timestep = 0
sensors.color1.onColorDetected(ColorSensorColor.Red, function () {
music.playTone(2000, 100)
controlDrive = 0
controlSteering = 0
})
// reads the motor angle and computes the motor speed,
// position
function computeMotors() {
temp = motorAngle
// read angle on both motors
motorAngle = motors.largeD.angle() + motors.largeA.angle()
// and estimate speed as angle difference
motorSpeed0 = motorAngle - temp
// average last 4 speed readings
motorSpeed = (motorSpeed0 + motorSpeed1 + motorSpeed2 + motorSpeed3) / 4 / timestep
// shift all previous recorded speeds by one
motorSpeed3 = motorSpeed2
motorSpeed2 = motorSpeed1
motorSpeed1 = motorSpeed0
// compute position from speed
motorPosition = motorPosition + timestep * motorSpeed
}
// read the gyro rate and computes the angle
function computeGyro() {
gyroRate = sensors.gyro2.rate()
gyroAngle = gyroAngle + timestep * gyroRate
}
function reset() {
state = 0
// sleeping
moods.sleeping.show();
// reset counters
motors.largeA.reset()
motors.largeD.reset()
// motors are unregulated
motors.largeA.setRegulated(false)
motors.largeD.setRegulated(false)
// clear the gyro sensor to remove drift
sensors.gyro2.reset()
// fall detection timer
control.timer2.reset()
// timestep computation timer
control.timer3.reset()
motorAngle = 0
motorPosition = 0
motorSpeed = 0
motorSpeed0 = 0
motorSpeed1 = 0
motorSpeed2 = 0
motorSpeed3 = 0
gyroRate = 0
gyroAngle = 0
fallen = false
power = 0
controlSteering = 0
controlDrive = 0
// awake
moods.awake.show();
gyroAngle = -0.25
state = 1;
}
// compute set point for motor position and required
// motor power
function computePower() {
// apply control and compute desired motor position
motorPosition -= timestep * controlDrive;
// estimate power based on sensor readings and control
// values
power = 0.8 * gyroRate + 15 * gyroAngle + (0.08 * motorSpeed + 0.12 * motorPosition) - 0.01 * controlDrive
// ensure that power stays within -100, 100
if (power > 100) {
power = 100
} else if (power < -100) {
power = -100
}
}
// test if the robot has fallen off
function checkFallen() {
if (Math.abs(power) < 100) {
control.timer2.reset()
}
if (control.timer2.seconds() > 2) {
fallen = true
}
}
// stop all motors and wait for touch button to be
// pressed
function stop() {
motors.stopAllMotors()
state = 0
moods.knockedOut.show();
sensors.touch3.pauseUntil(TouchSensorEvent.Pressed)
moods.neutral.show();
}
sensors.ultrasonic4.onEvent(UltrasonicSensorEvent.ObjectNear, function () {
moods.dizzy.show()
controlSteering = 0
oldControlDrive = controlDrive
controlDrive = -10
motors.mediumC.setSpeed(30, 30, MoveUnit.Degrees);
motors.mediumC.setSpeed(-30, 60, MoveUnit.Degrees);
motors.mediumC.setSpeed(30, 30, MoveUnit.Degrees);
if (Math.randomRange(-1, 1) >= 1) {
controlSteering = 70
} else {
controlSteering = -70
}
loops.pause(4000)
music.playTone(2000, 100)
controlSteering = 0
controlDrive = oldControlDrive
moods.neutral.show()
})
// compute the elapsed time since the last iteration
function computeTimestep() {
timestep = control.timer3.seconds()
control.timer3.reset()
}
sensors.color1.onColorDetected(ColorSensorColor.Green, function () {
moods.winking.show()
controlDrive = 150
controlSteering = 0
})
sensors.color1.onColorDetected(ColorSensorColor.Blue, function () {
moods.middleRight.show()
controlSteering = 70
})
// apply power to motors
function controlMotors() {
motors.largeA.setSpeed(power + controlSteering * 0.1)
motors.largeD.setSpeed(power - controlSteering * 0.1)
}
sensors.color1.onColorDetected(ColorSensorColor.Yellow, function () {
moods.middleLeft.show()
controlSteering = -70
})
sensors.color1.onColorDetected(ColorSensorColor.White, function () {
moods.sad.show();
controlDrive = -75
})
timestep = 0.014
// main loop
loops.forever(function () {
reset()
while (!fallen) {
control.timer3.pauseUntil(5)
computeTimestep()
computeGyro()
computeMotors()
computePower()
controlMotors()
checkFallen()
}
stop()
})
```

View File

@ -0,0 +1,215 @@
# Gyroboy LabView
```typescript
let mSum = 0;
let mPos = 0;
let mSpd = 0;
let mD = 0;
let mDP1 = 0;
let mDP2 = 0;
let mDP3 = 0;
let Crdv = 0;
let cLo = 0;
let gAng = 0;
let ok = false;
let pwr = 0;
let Cstr = 0;
let Cdrv = 0;
let gMn = 0;
let gMx = 0;
let gSum = 0;
let gyro = 0;
let gOS = 0;
let gSpd = 0;
let tInt = 0.014;
let lpwr = 0
let rpwr = 0
let tStart = 0
let st = 0
let oldDr = 0
function RST() {
motors.largeA.reset()
motors.largeD.reset()
motors.largeA.setRegulated(false)
motors.largeD.setRegulated(false)
sensors.gyro2.reset()
sensors.gyro2.rate()
control.timer2.reset()
loops.pause(5000)
mSum = 0;
mPos = 0;
mD = 0;
mDP1 = 0;
mDP2 = 0;
mDP3 = 0;
Crdv = 0;
cLo = 0;
gAng = 0;
ok = false;
pwr = 0;
st = 0;
Cstr = 0;
Cdrv = 0;
}
function OS() {
// OSL
do {
gMn = 1000;
gMx = -100;
gSum = 0;
// gChk
for (let i = 0; i < 200; i++) {
gyro = sensors.gyro2.rate()
gSum = gyro;
gMx = Math.max(gMx, gyro)
gMn = Math.min(gMn, gyro)
loops.pause(4);
}
} while (gMx - gMn > 2);
gOS = gSum / 200;
}
function GT() {
if (cLo == 0) {
tInt = 0.014;
control.timer1.reset();
} else {
tInt = control.timer1.seconds() / cLo;
}
cLo++;
}
function GG() {
gyro = sensors.gyro2.rate();
gOS = 0.0005 * gyro + (1 - 0.0005) * gOS
gSpd = gyro - gOS;
gAng = gAng + tInt * gSpd;
}
function GM() {
let temp = mSum
mSum = motors.largeD.angle() + motors.largeA.angle();
mD = mSum - temp;
mPos = mPos + mD;
mSpd = ((mDP1 + mDP2 + mDP3 + mD) / 4) / tInt;
mDP3 = mDP2;
mDP2 = mDP1;
mDP1 = mD;
}
function EQ() {
mPos = mPos - Cdrv * tInt;
pwr = (0.8 * gSpd + 15 * gAng) + (0.08 * mSpd + 0.12 * mPos) - 0.01 * Cdrv
if (pwr > 100) pwr = 100
else if (pwr < -100) pwr = -100
}
function cntrl() {
mPos = mPos - tInt * Cdrv
lpwr = (pwr + Cstr * 0.1)
rpwr = (pwr - Cstr * 0.1)
}
function CHK() {
if (Math.abs(pwr) < 100)
control.timer2.reset();
if (control.timer2.seconds() > 2) {
ok = true;
}
}
// M
loops.forever(function () {
RST();
brick.showImage(images.eyesSleeping)
OS()
gAng = -0.25;
music.playSoundEffect(sounds.movementsSpeedUp)
brick.showImage(images.eyesAwake)
st = 1;
// BALANCE
while (!ok) {
GT();
let t1 = control.timer1.millis()
GG();
GM();
EQ();
cntrl();
motors.largeA.setSpeed(lpwr)
motors.largeD.setSpeed(rpwr)
CHK()
let t2 = control.timer1.millis();
let p = 5 - (t2 - t1);
loops.pause(Math.max(1, p))
}
motors.stopAllMotors()
st = 0;
brick.setLight(BrickLight.RedPulse);
brick.showImage(images.eyesKnockedOut)
music.playSoundEffect(sounds.movementsSpeedDown)
sensors.touch3.pauseUntil(TouchSensorEvent.Pressed)
brick.setLight(BrickLight.Off);
})
// BHV
loops.forever(function () {
switch (st) {
case 0:
Cdrv = 0;
Cstr = 0;
break;
case 1:
Cdrv = 40;
loops.pause(4000);
Cdrv = 0;
music.playTone(1000, 100);
st = 2;
break;
case 2:
switch (sensors.color1.color()) {
case ColorSensorColor.Red:
music.playTone(2000, 100);
Cdrv = 0;
Cstr = 0;
break;
case ColorSensorColor.Green:
music.playTone(2000, 100);
Cdrv = 150;
Cstr = 0;
break;
case ColorSensorColor.Blue:
music.playTone(2000, 100);
Cstr = 70;
break;
case ColorSensorColor.Yellow:
music.playTone(2000, 100);
Cstr = -70;
break;
case ColorSensorColor.White:
music.playTone(2000, 100);
Cdrv = -75;
break;
}
if (sensors.ultrasonic4.distance() < 25) {
Cstr = 0;
oldDr = Cdrv;
Cdrv = -10;
motors.mediumC.setSpeed(30, 30, MoveUnit.Degrees);
motors.mediumC.setSpeed(-30, 60, MoveUnit.Degrees);
motors.mediumC.setSpeed(30, 30, MoveUnit.Degrees);
if (Math.randomRange(-1, 1) >= 1)
Cstr = 70;
else
Cstr = -70;
loops.pause(4000);
music.playTone(2000, 100)
Cstr = 0;
Cdrv = oldDr;
}
break;
}
loops.pause(80);
})
```

View File

@ -0,0 +1,390 @@
# Puppy
```typescript
let P_T = 0;
let ISS = 0;
let F_T = 0;
let P_C = 0;
let F_C = 0;
let DB_S = 0;
let NS = false;
let IBP = 0;
let IAP = 0;
let C = false;
let TC = false;
let OTC = false;
let COL = 0;
let OCOL = 0;
let _C = false;
let GTO = 0;
function DN() {
motors.largeAD.setBrake(true);
motors.largeAD.tank(50, 50, 1, MoveUnit.Seconds);
loops.pause(100);
motors.largeA.clearCounts()
motors.largeD.clearCounts()
}
function MNRH() {
motors.mediumC.setBrake(true)
brick.showImage(images.legoEv3icon)
brick.setLight(BrickLight.OrangePulse)
while (!brick.buttonEnter.wasPressed()) {
if (brick.buttonUp.wasPressed()) {
motors.mediumC.setSpeed(-100);
} else if (brick.buttonDown.wasPressed()) {
motors.mediumC.setSpeed(100);
} else {
motors.mediumC.stop();
}
}
motors.mediumC.stop();
motors.mediumC.clearCounts();
brick.setLight(BrickLight.Green);
}
function IS(t: number) {
ISS = t;
switch (t) {
case 0:
brick.showImage(images.eyesNeutral);
break;
case 1:
brick.showImage(images.eyesSleeping);
break;
case 2:
brick.showImage(images.eyesTear);
// draw rect...
break;
case 3:
brick.showImage(images.eyesHurt);
break;
case 4:
brick.showImage(images.eyesAngry);
break;
case 5:
brick.showImage(images.eyesTiredMiddle);
break;
case 6:
brick.showImage(images.eyesTiredRight);
break;
case 7:
brick.showImage(images.eyesTiredLeft);
break;
case 8:
brick.showImage(images.eyesLove);
break;
}
}
function UP() {
if (motors.largeA.angle() > -50) {
control.runInBackground(function () {
motors.largeD.clearCounts()
motors.largeD.setSpeed(-35);
pauseUntil(() => motors.largeD.angle() < -25);
motors.largeD.stop();
motors.largeD.setRegulated(false)
motors.largeD.setSpeed(-15)
pauseUntil(() => motors.largeD.angle() < -65);
motors.largeD.stop();
})
motors.largeA.clearCounts()
motors.largeA.setSpeed(-35);
pauseUntil(() => motors.largeA.angle() < -25);
motors.largeA.stop();
motors.largeA.setRegulated(false)
motors.largeA.setSpeed(-15)
pauseUntil(() => motors.largeA.angle() < -65);
motors.largeA.stop();
loops.pause(500);
}
}
function RST() {
P_T = Math.randomRange(3, 6);
F_T = Math.randomRange(2, 4);
P_C = 1;
F_C = 1;
control.timer1.reset();
control.timer2.reset();
control.timer3.reset();
CS(0);
}
function CS(db: number) {
if (DB_S != db) {
DB_S = db;
NS = true;
}
}
function MON() {
if (control.timer2.seconds() > 10) {
control.timer2.reset();
P_C--;
if (P_C < 0) {
P_C = 0;
}
}
if (control.timer1.seconds() > 20) {
control.timer1.reset()
F_C--;
if (F_C < 0) {
F_C = 0;
}
}
if (control.timer3.seconds() > 30) {
control.timer3.reset();
CS(1);
}
}
function UIS() {
if (control.timer5.seconds() > IBP) {
control.timer5.reset();
if (ISS == 1) {
ISS = 6;
IBP = Math.randomRange(1, 5);
} else {
ISS = 1;
IBP = 0.25;
}
IS(ISS);
}
if (control.timer6.seconds() > IAP) {
if (ISS != 1) {
control.timer6.reset();
IAP = Math.randomRange(1, 10)
if (ISS != 7) {
ISS = 7
} else {
ISS = 6;
}
IS(ISS);
}
}
}
function UPDB() {
if ((P_T == P_C) && (F_T == F_C)) {
CS(6);
}
if ((P_T > P_C) && (F_T < F_C)) {
CS(3);
}
if ((P_T < P_C) && (F_T > F_C)) {
CS(5);
}
if ((P_C == 0) && (F_C > 0)) {
CS(2)
}
if (F_C == 0) {
CS(4)
}
}
function PTC() {
C = false;
OTC = TC;
TC = sensors.touch1.isPressed()
if (TC != OTC && TC) {
P_C++;
control.timer3.reset();
if (DB_S != 4) {
IS(2);
music.playSoundEffect(sounds.animalsDogSniff);
C = true;
}
}
return C;
}
function FDC() {
OCOL = COL;
COL = sensors.color4.color();
_C = false;
if ((COL != 0) && (OCOL != COL)) {
F_C++;
_C = true;
control.timer3.reset();
IS(2);
music.playSoundEffect(sounds.expressionsCrunching)
}
return _C;
}
function IDL() {
if (NS) {
NS = false;
UP();
}
UIS();
UPDB();
PTC();
FDC();
}
function MHT(Pos: number) {
let _R = Pos - motors.mediumC.angle();
if (_R >= 0) {
motors.mediumC.setSpeed(100, _R, MoveUnit.Degrees);
} else {
motors.mediumC.setSpeed(-100, Math.abs(_R), MoveUnit.Degrees);
}
}
function SLP() {
if (NS) {
NS = false;
IS(5)
DN()
MHT(3000)
IS(1)
music.playSoundEffect(sounds.expressionsSnoring)
}
if (sensors.touch1.isPressed() || brick.buttonEnter.isPressed()) {
music.stopAllSounds();
control.timer3.reset();
CS(7);
}
}
function PLF() {
if (NS) {
NS = false
IS(0)
UP()
music.playSoundEffect(sounds.animalsDogBark2)
control.timer4.reset()
GTO = Math.randomRange(4, 8);
}
if(PTC()) {
CS(0);
}
if (control.timer4.seconds() > GTO) {
music.playSoundEffect(sounds.animalsDogBark2)
control.timer4.reset();
GTO = Math.randomRange(4, 8);
}
}
function NGR() {
NS = false
IS(4)
music.playSoundEffect(sounds.animalsDogGrowl);
UP();
loops.pause(1500);
music.stopAllSounds()
music.playSoundEffect(sounds.animalsDogBark1)
P_C--;
CS(0);
}
function HNG() {
if (NS) {
NS = false;
IS(3)
DN();
music.playSoundEffect(sounds.animalsDogWhine);
}
if(FDC()) {
CS(0)
}
if (PTC()) {
CS(3);
}
}
function PPP() {
NS = false;
IS(2);
UP();
loops.pause(100)
motors.largeA.setSpeed(-30, 70, MoveUnit.Degrees);
loops.pause(800);
music.playSoundEffect(sounds.mechanicalHorn1);
loops.pause(1000);
for(let i = 0; i < 3; ++i) {
motors.largeA.setSpeed(-30, 20, MoveUnit.Degrees);
motors.largeA.setSpeed(30, 20, MoveUnit.Degrees);
}
motors.largeA.setSpeed(30, 70, MoveUnit.Degrees);
F_C = 1;
CS(0);
}
function HPY() {
IS(8)
MHT(0);
motors.largeAD.setSpeed(10, 0.8, MoveUnit.Seconds);
for(let i = 0; i < 3; ++i) {
music.playSoundEffect(sounds.animalsDogBark1);
motors.largeAD.setSpeed(-100, 0.2, MoveUnit.Seconds);
loops.pause(300)
motors.largeAD.setSpeed(10, 0.3, MoveUnit.Seconds)
}
loops.pause(500);
music.stopAllSounds();
DN();
RST();
}
function STL() {
UP();
motors.largeAD.setSpeed(-20, 60, MoveUnit.Degrees);
music.playSoundEffect(sounds.animalsDogWhine);
motors.largeAD.setSpeed(20, 60, MoveUnit.Degrees);
}
function WKU() {
let stateC = false;
IS(5);
music.playSoundEffect(sounds.animalsDogWhine)
MHT(0)
DN()
STL()
loops.pause(1000);
UP()
CS(0;)
}
DN();
MNRH();
// compare button state???
IS(1);
UP();
RST();
loops.forever(function () {
MON();
switch (DB_S) {
case 0:
IDL();
break;
case 1:
SLP();
break;
case 2:
PLF();
break;
case 3:
NGR();
break;
case 4:
HNG();
break;
case 5:
PPP();
break;
case 6:
HPY();
break;
case 7:
WKU();
break;
}
})
```

View File

@ -0,0 +1,51 @@
# Robot Arm
```typescript
function INI() {
motors.largeB.setBrake(true)
motors.largeC.setBrake(true)
motors.mediumA.setBrake(true)
motors.largeB.setSpeed(-50)
pauseUntil(() => sensors.color3.light(LightIntensityMode.Reflected) > 25);
motors.largeB.stop();
motors.mediumA.setSpeed(30, 1, MoveUnit.Seconds);
motors.mediumA.setSpeed(-50, 90, MoveUnit.Degrees);
motors.largeC.setSpeed(50)
sensors.touch1.pauseUntil(TouchSensorEvent.Pressed);
motors.largeC.setSpeed(-50, 0.86, MoveUnit.Rotations);
}
INI()
let down = false;
loops.forever(function () {
brick.showImage(images.informationQuestionMark)
brick.setLight(BrickLight.OrangePulse);
pauseUntil(() => (down = brick.buttonDown.wasPressed()) || brick.buttonUp.wasPressed())
brick.setLight(BrickLight.Off)
music.playSoundEffect(sounds.mechanicalAirRelease)
brick.showImage(images.informationAccept)
if (down) {
brick.showImage(images.informationForward)
motors.largeC.setSpeed(65, 0.85, MoveUnit.Rotations);
} else {
brick.showImage(images.informationBackward)
motors.largeC.setSpeed(-65, 0.85, MoveUnit.Rotations);
}
motors.largeB.setSpeed(20, 275, MoveUnit.Degrees)
motors.mediumA.setSpeed(30, 1, MoveUnit.Seconds)
motors.largeB.setSpeed(-55)
pauseUntil(() => sensors.color3.light(LightIntensityMode.Reflected) > 25);
motors.largeB.stop();
if (down) {
motors.largeC.setSpeed(-65, 0.86, MoveUnit.Rotations);
} else {
motors.largeC.setSpeed(65, 0.85, MoveUnit.Rotations);
}
motors.largeB.setSpeed(20, 275, MoveUnit.Degrees);
motors.mediumA.setSpeed(-30, 90, MoveUnit.Degrees);
motors.largeB.setSpeed(-55)
pauseUntil(() => sensors.color3.light(LightIntensityMode.Reflected) > 25);
motors.largeB.stop()
})
```

View File

@ -0,0 +1,51 @@
# Crane LabView
```blocks
function INI() {
motors.largeB.setBrake(true)
motors.largeC.setBrake(true)
motors.mediumA.setBrake(true)
motors.largeB.setSpeed(-50)
pauseUntil(() => sensors.color3.light(LightIntensityMode.Reflected) > 25);
motors.largeB.stop();
motors.mediumA.setSpeed(30, 1, MoveUnit.Seconds);
motors.mediumA.setSpeed(-50, 90, MoveUnit.Degrees);
motors.largeC.setSpeed(50)
sensors.touch1.pauseUntil(TouchSensorEvent.Pressed);
motors.largeC.setSpeed(-50, 0.86, MoveUnit.Rotations);
}
INI()
let down = false;
loops.forever(function () {
brick.showImage(images.informationQuestionMark)
brick.setLight(BrickLight.OrangePulse);
pauseUntil(() => (down = brick.buttonDown.wasPressed()) || brick.buttonUp.wasPressed())
brick.setLight(BrickLight.Off)
music.playSoundEffect(sounds.mechanicalAirRelease)
brick.showImage(images.informationAccept)
if (down) {
brick.showImage(images.informationForward)
motors.largeC.setSpeed(65, 0.85, MoveUnit.Rotations);
} else {
brick.showImage(images.informationBackward)
motors.largeC.setSpeed(-65, 0.85, MoveUnit.Rotations);
}
motors.largeB.setSpeed(20, 275, MoveUnit.Degrees)
motors.mediumA.setSpeed(30, 1, MoveUnit.Seconds)
motors.largeB.setSpeed(-55)
pauseUntil(() => sensors.color3.light(LightIntensityMode.Reflected) > 25);
motors.largeB.stop();
if (down) {
motors.largeC.setSpeed(-65, 0.86, MoveUnit.Rotations);
} else {
motors.largeC.setSpeed(65, 0.85, MoveUnit.Rotations);
}
motors.largeB.setSpeed(20, 275, MoveUnit.Degrees);
motors.mediumA.setSpeed(-30, 90, MoveUnit.Degrees);
motors.largeB.setSpeed(-55)
pauseUntil(() => sensors.color3.light(LightIntensityMode.Reflected) > 25);
motors.largeB.stop()
})
```

View File

@ -0,0 +1,215 @@
# Gyro Boy LabView
```blocks
let mSum = 0;
let mPos = 0;
let mSpd = 0;
let mD = 0;
let mDP1 = 0;
let mDP2 = 0;
let mDP3 = 0;
let Crdv = 0;
let cLo = 0;
let gAng = 0;
let ok = false;
let pwr = 0;
let Cstr = 0;
let Cdrv = 0;
let gMn = 0;
let gMx = 0;
let gSum = 0;
let gyro = 0;
let gOS = 0;
let gSpd = 0;
let tInt = 0.014;
let lpwr = 0
let rpwr = 0
let tStart = 0
let st = 0
let oldDr = 0
function RST() {
motors.largeA.reset()
motors.largeD.reset()
motors.largeA.setRegulated(false)
motors.largeD.setRegulated(false)
sensors.gyro2.reset()
sensors.gyro2.rate()
control.timer2.reset()
loops.pause(5000)
mSum = 0;
mPos = 0;
mD = 0;
mDP1 = 0;
mDP2 = 0;
mDP3 = 0;
Crdv = 0;
cLo = 0;
gAng = 0;
ok = false;
pwr = 0;
st = 0;
Cstr = 0;
Cdrv = 0;
}
function OS() {
// OSL
do {
gMn = 1000;
gMx = -100;
gSum = 0;
// gChk
for (let i = 0; i < 200; i++) {
gyro = sensors.gyro2.rate()
gSum = gyro;
gMx = Math.max(gMx, gyro)
gMn = Math.min(gMn, gyro)
loops.pause(4);
}
} while (gMx - gMn > 2);
gOS = gSum / 200;
}
function GT() {
if (cLo == 0) {
tInt = 0.014;
control.timer1.reset();
} else {
tInt = control.timer1.seconds() / cLo;
}
cLo++;
}
function GG() {
gyro = sensors.gyro2.rate();
gOS = 0.0005 * gyro + (1 - 0.0005) * gOS
gSpd = gyro - gOS;
gAng = gAng + tInt * gSpd;
}
function GM() {
let temp = mSum
mSum = motors.largeD.angle() + motors.largeA.angle();
mD = mSum - temp;
mPos = mPos + mD;
mSpd = ((mDP1 + mDP2 + mDP3 + mD) / 4) / tInt;
mDP3 = mDP2;
mDP2 = mDP1;
mDP1 = mD;
}
function EQ() {
mPos = mPos - Cdrv * tInt;
pwr = (0.8 * gSpd + 15 * gAng) + (0.095 * mSpd + 0.13 * mPos) - 0.01 * Cdrv
if (pwr > 100) pwr = 100
else if (pwr < -100) pwr = -100
}
function cntrl() {
mPos = mPos - tInt * Cdrv
lpwr = (pwr + Cstr * 0.1)
rpwr = (pwr - Cstr * 0.1)
}
function CHK() {
if (Math.abs(pwr) < 100)
control.timer2.reset();
if (control.timer2.seconds() > 2) {
ok = true;
}
}
// M
loops.forever(function () {
RST();
brick.showImage(images.eyesSleeping)
OS()
gAng = -0.25;
music.playSoundEffect(sounds.movementsSpeedUp)
brick.showImage(images.eyesAwake)
st = 1;
// BALANCE
while (!ok) {
GT();
let t1 = control.timer1.millis()
GG();
GM();
EQ();
cntrl();
motors.largeA.setSpeed(lpwr)
motors.largeD.setSpeed(rpwr)
CHK()
let t2 = control.timer1.millis();
let p = 5 - (t2 - t1);
loops.pause(Math.max(1, p))
}
motors.stopAllMotors()
st = 0;
brick.setLight(BrickLight.RedPulse);
brick.showImage(images.eyesKnockedOut)
music.playSoundEffect(sounds.movementsSpeedDown)
sensors.touch3.pauseUntil(TouchSensorEvent.Pressed)
brick.setLight(BrickLight.Off);
})
// BHV
loops.forever(function () {
switch (st) {
case 0:
Cdrv = 0;
Cstr = 0;
break;
case 1:
Cdrv = 40;
loops.pause(4000);
Cdrv = 0;
music.playTone(1000, 100);
st = 2;
break;
case 2:
switch (sensors.color1.color()) {
case ColorSensorColor.Red:
music.playTone(2000, 100);
Cdrv = 0;
Cstr = 0;
break;
case ColorSensorColor.Green:
music.playTone(2000, 100);
Cdrv = 150;
Cstr = 0;
break;
case ColorSensorColor.Blue:
music.playTone(2000, 100);
Cstr = 70;
break;
case ColorSensorColor.Yellow:
music.playTone(2000, 100);
Cstr = -70;
break;
case ColorSensorColor.White:
music.playTone(2000, 100);
Cdrv = -75;
break;
}
if (sensors.ultrasonic4.distance() < 25) {
Cstr = 0;
oldDr = Cdrv;
Cdrv = -10;
motors.mediumC.setSpeed(30, 30, MoveUnit.Degrees);
motors.mediumC.setSpeed(-30, 60, MoveUnit.Degrees);
motors.mediumC.setSpeed(30, 30, MoveUnit.Degrees);
if (Math.randomRange(-1, 1) >= 1)
Cstr = 70;
else
Cstr = -70;
loops.pause(4000);
music.playTone(2000, 100)
Cstr = 0;
Cdrv = oldDr;
}
break;
}
loops.pause(80);
})
```

27
docs/lessons.md Normal file
View File

@ -0,0 +1,27 @@
# Lessons
Learning activities for LEGO Mindstorms with MakeCode.
## Motors and motion
```codecard
[{
"name": "Make it Move",
"imageUrl":"/static/lessons/make-it-move.jpg",
"url": "/lessons/make-it-move",
"cardType": "project",
"description": "Make a robot that moves itself without wheels."
}, {
"name": "Make it Move TUTORIAL",
"imageUrl":"/static/lessons/make-it-move.jpg",
"url": "/lessons/make-it-move-tutorial",
"cardType": "tutorial",
"description": "Make a robot that moves itself without wheels."
}, {
"name": "Line Detection",
"imageUrl":"/static/lessons/line-detection.jpg",
"url": "/lessons/line-detection",
"cardType": "project",
"description": "Make your robot drive itself by following lines."
}]
```

View File

@ -0,0 +1,266 @@
# Line Detection
## Objective
Design ways to improve driving safety by helping to prevent drivers from falling asleep and causing an accident.
![Car driving on highway](/static/lessons/line-detection/car-driving.jpg)
## Connect
Make sure that you can answer the following questions:
* Can autonomous cars react to different traffic light signals?
* What can happen if a driver falls asleep while driving?
* How can we detect when a driver is falling asleep?
Think about what you have learned, then document it. Describe the problem in your own words. Creatively record your ideas and findings.
## Construct
Start by constructing this model. Read the building instructions [here](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-rem-color-sensor-down-driving-base-d30ed30610c3d6647d56e17bc64cf6e2.pdf) first.
![Color sensor on the driving base](/static/lessons/line-detection/color-sensor-driving.jpg)
## Program
Autonomous cars need to recognize and respond to traffic lights automatically.
First, create a program that will make your robot stop at red lights.
Make sure your robot is only responding to the color red.
Once you have succeeded, program your robot to drive forward again when the light changes from red to green.
There are two coding tasks for this lesson:
1. Create a program that will make your robot stop at red lights.
2. Create a program that drives the robot forward until the Color Sensor sees red. The robot then stops.
## Coding task 1 - Stop at red lights
**Goal:** Create a program that will make your robot stop at red lights.
### Step 1
Create a program that drives the robot forward until the Color Sensor sees red. The robot then stops.
Place a ``||motors:steer large B+C||`` block from ``||motors:Motors||`` under ``||loops:on start||``. Change the speed to 20%.
```blocks
motors.largeBC.steer(0, 20)
```
### Step 2
Place a ``||loops:while||`` loop block under ``||motors:steer large B+C||``.
```blocks
motors.largeBC.steer(0, 20)
while (true) {
}
```
### Step 3
Place a ``||sensors:pause for color||`` from ``||sensors:Sensors||`` inside the ``||loops:while||`` loop block. Change the color to red.
```blocks
motors.largeBC.steer(0, 20)
while (true) {
sensors.color3.pauseForColor(ColorSensorColor.Red)
}
```
### Step 4
Place a ``||motors:stop all motors||`` block under the ``||sensors:pause for color||`` block.
Study the program...what do you think the program will do?
**Hint:** The motors will run until the Color Sensor senses the color red, then all motors will stop. The motors will run until the sensor reading in the while block is true.
```blocks
motors.largeBC.steer(0, 20)
while (true) {
sensors.color3.pauseForColor(ColorSensorColor.Red)
motors.stopAllMotors()
}
```
### Step 5
Click `|Download|` and follow the instructions to get your code onto your EV3 Brick. Press the **center** button on the EV3 Brick to run the program.
## Coding task 2 - Detect light changes
**Goal:** Program your robot to drive forward again when the light changes from red to green.
### Step 1
Place a ``||loops:while||`` loop block under ``||loops:on start||``.
```blocks
while (true) {
}
```
### Step 2
Place a ``||motors:steer large B+C||`` block from ``||motors:Motors||`` inside the ``||loops:while||`` loop block. Change the speed to 20%.
```blocks
while (true) {
motors.largeBC.steer(0, 20)
}
```
### Step 4
Place a ``||loops:while||`` loop block under the ``||motors:steer large B+C||`` block.
```blocks
while (true) {
motors.largeBC.steer(0, 20)
while (true) {
}
}
```
### Step 5
Place a ``||sensors:pause for color||`` block from ``||sensors:Sensors||`` inside the ``||loops:while||`` loop block. Change the color to red.
```blocks
while (true) {
motors.largeBC.steer(0, 20)
while (true) {
sensors.color3.pauseForColor(ColorSensorColor.Red)
}
}
```
### Step 6
Place a ``||motors:stop all motors||`` block under the ``||sensors:pause for color||`` block.
```blocks
while (true) {
motors.largeBC.steer(0, 20)
while (true) {
sensors.color3.pauseForColor(ColorSensorColor.Red)
motors.stopAllMotors()
}
}
```
### Step 7
Place a ``||loops:while||`` loop block under the second ``||loops:while||`` loop block.
```blocks
while (true) {
motors.largeBC.steer(0, 20)
while (true) {
sensors.color3.pauseForColor(ColorSensorColor.Red)
motors.stopAllMotors()
}
while (true) {
}
}
```
### Step 8
Place a ``||sensors:pause for color||`` block inside the new ``||loops:while||`` loop block. Change the color to red.
What do you think the program will do?
**Hint:** The motors will run until the Color Sensor detects the color red, then it will stop all motors. The motors will also run and not stop when the color sensor detects the color green.
```blocks
while (true) {
motors.largeBC.steer(0, 20)
while (true) {
sensors.color3.pauseForColor(ColorSensorColor.Red)
motors.stopAllMotors()
}
while (true) {
sensors.color3.pauseForColor(ColorSensorColor.Red)
}
}
```
### Step 9
Click `|Download|` and follow the instructions to get your code onto your EV3 Brick. Press the **center** button on the EV3 Brick to run the program.
## Contemplate
To simulate what could happen if a driver falls asleep while driving, your robot could sound an alarm signal when it crosses the line. This feature is often available in new cars.
Program your robot to perform this function.
Think about what you have learned, then document it. Describe your pseudocode for this task. Creatively record your ideas, and findings.
### Programming hint
```blocks
motors.largeBC.steer(0, 20)
while (true) {
sensors.color3.pauseForColor(ColorSensorColor.Yellow)
music.playSoundEffect(sounds.systemGeneralAlert)
}
while (true) {
while (true) { sensors.color3.pauseForLight(LightIntensityMode.Reflected, LightCondition.Bright)
motors.largeB.setSpeed(10)
motors.largeC.setSpeed(-10)
}
while (true) {
sensors.color3.pauseForLight(LightIntensityMode.Reflected, LightCondition.Bright)
motors.largeA.setSpeed(-10)
motors.largeA.setSpeed(10)
}
}
```
## Continue
Program your robot to drive on “autopilot” along a given route. You will need to create a program that recognizes and responds to a dark line (or white line). You will create a line-following program and your robot will need to travel along the line without losing contact with it.
You will need to constantly debug your program in order to make your robot travel as smoothly as possible along the line.
### Programming hint
```blocks
while (true) {
while (true) { sensors.color3.pauseForLight(LightIntensityMode.Reflected, LightCondition.Bright)
motors.largeB.setSpeed(10)
motors.largeC.setSpeed(-10)
}
while (true) {
sensors.color3.pauseForLight(LightIntensityMode.Reflected, LightCondition.Bright)
motors.largeB.setSpeed(-10)
motors.largeC.setSpeed(10)
}
}
```
## Share
Consider the following questions:
1. What challenged you?
2. Where there any surprises?
3. How could you improve your program?
4. Could your program have been more streamlined?
5. Have you used too many blocks?
6. Is there a more efficient way of building your program?
7. How could your program be used in real-world scenarios?
Think about what you have learned, then document it. Creatively record and present your ideas, creations, and findings.

View File

@ -0,0 +1,72 @@
# Make It Move Without Wheels
## Objective @fullscreen
Design, build and program a robot that can move itself:
Your robot will:
* Go a distance of at least 30cm
* Use at least one motor
* Use NO wheels for locomotion
![LECG Mindstorms brick with parts](/static/lessons/make-it-move/locomotion-no-wheels.jpg)
## Construct @fullscreen
Build a Walker Bot!
The Walker Bot is one example of many possible solutions for making a robot move without wheels.
The Walker Bot combines an EV3 Frame and two legs that are mirror-images to create left and right legs.
The legs in the Walker Bot are designed to show how to change the rotary motion of a motor to reciprocating motion.
Start by reading [these](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/ev3-dep/building%20instructions/walker-bot-bi-180fc24f9298e1dd6201099627d43903.pdf) instructions first.
![LEGO Mindstorms Walker Bot](/static/lessons/make-it-move/walker-bot.jpg)
## Program 1 @fullscreen
In nature, creatures use many methods to get around. None of them, however, use wheels to move. Can we copy the method of animal locomotion with our robot? Using motors and legs, make the robot move without using any wheels.
Place a ``||motors:tank large B+C||`` block from ``||motors:Motors||`` under ``||loops:on start||``.
Change the speed to `-60%` (for motor B) and `+60%` (for motor C).
Change the rotations to `9`.
The ``||motors:tank large B+C||`` block will run for `9` rotations when the **center** button is pressed on the EV3 brick. The motors are set for the reverse direction because they are mounted upside down in this model.
```blocks
motors.largeBC.tank(-60, 60, 9, MoveUnit.Rotations)
```
## Program 2 @fullscreen
Place a ``||motors:stop all motors||`` block under ``||motors:tank large B+C||``.
The ``||motors:tank large B+C||`` block will run for `9` rotations when the **center** button is pressed on the EV3 brick then stop.
```blocks
motors.largeBC.tank(-60, 60, 9, MoveUnit.Rotations)
motors.stopAllMotors()
```
## Program 3 @fullscreen
Place a ``||brick:show string||`` block under ``||motors:stop all motors||``.
Change the `"Hello World"` text to `"30 cm"`.
The ``||motors:tank large B+C||`` will run for `9` rotations when the **center** button is pressed on the EV3 brick then stop and display "30 cm" on the EV3 Bricks screen.
```blocks
motors.largeBC.tank(-60, 60, 9, MoveUnit.Rotations)
motors.stopAllMotors()
brick.showString("30 cm", 1)
```
## Program 4 @fullscreen
Click `|Download|` and follow the instructions to get your code onto your EV3 Brick. Press the **center** button on the EV3 Brick to run the program.

View File

@ -0,0 +1,73 @@
# Make It Move Without Wheels
## Objective
Design, build and program a robot that can move itself:
Your robot will:
* Go a distance of at least 30cm
* Use at least one motor
* Use NO wheels for locomotion
![LECG Mindstorms brick with parts](/static/lessons/make-it-move/locomotion-no-wheels.jpg)
## Construct
Build a Walker Bot!
The Walker Bot is one example of many possible solutions for making a robot move without wheels.
The Walker Bot combines an EV3 Frame and two legs that are mirror-images to create left and right legs.
The legs in the Walker Bot are designed to show how to change the rotary motion of a motor to reciprocating motion.
Start by reading [these](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/ev3-dep/building%20instructions/walker-bot-bi-180fc24f9298e1dd6201099627d43903.pdf) instructions first.
![LEGO Mindstorms Walker Bot](/static/lessons/make-it-move/walker-bot.jpg)
## Program
In nature, creatures use many methods to get around. None of them, however, use wheels to move. Can we copy the method of animal locomotion with our robot? Using motors and legs, make the robot move without using any wheels.
### Step 1
Place a ``||motors:tank large B+C||`` block from ``||motors:Motors||`` under ``||loops:on start||``.
Change the speed to `-60%` (for motor B) and `+60%` (for motor C).
Change the rotations to `9`.
The ``||motors:tank large B+C||`` block will run for `9` rotations when the **center** button is pressed on the EV3 brick. The motors are set for the reverse direction because they are mounted upside down in this model.
```typescript-ignore
motors.largeBC.tankFor(-60, 60, 9, MoveUnit.Rotations)
```
### Step 2
Place a ``||motors:stop all motors||`` block under ``||motors:tank large B+C||``.
The ``||motors:tank large B+C||`` block will run for `9` rotations when the **center** button is pressed on the EV3 brick then stop.
```typescript-ignore
motors.largeBC.tankFor(-60, 60, 9, MoveUnit.Rotations)
motors.stopAllMotors()
```
### Step 3
Place a ``||brick:show string||`` block under ``||motors:stop all motors||``.
Change the `"Hello World"` text to `"30 cm"`.
The ``||motors:tank large B+C||`` will run for `9` rotations when the **center** button is pressed on the EV3 brick then stop and display "30 cm" on the EV3 Bricks screen.
```typescript-ignore
motors.largeBC.tankFor(-60, 60, 9, MoveUnit.Rotations)
motors.stopAllMotors()
brick.showString("30 cm", 1)
```
### Step 4
Click `|Download|` and follow the instructions to get your code onto your EV3 Brick. Press the **center** button on the EV3 Brick to run the program.

BIN
docs/static/lessons/line-detection.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
docs/static/lessons/make-it-move.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,25 @@
{
"automation": "Automation, process control and robotic controllers\n\nProcess control, automation, robotics AI",
"automation.Behavior": "A behavior",
"automation.Behavior.update": "Called on each behavior iteration even for suppresed behaviors",
"automation.Behavior.update|param|elapsed": "milli seconds since last call",
"automation.BehaviorManager": "A manager for behaviors",
"automation.BehaviorManager.add": "Adds a new behavior to the behavior manager",
"automation.BehaviorManager.add|param|behavior": "the behavior to add",
"automation.BehaviorManager.start": "Starts the behavior control loop",
"automation.BehaviorManager.stop": "Stops the execution loop",
"automation.PIDController.compute": "Computes the output based on the system state",
"automation.PIDController.setControlSaturation": "Sets the control saturation values",
"automation.PIDController.setControlSaturation|param|high": "highest control value, eg: 100",
"automation.PIDController.setControlSaturation|param|low": "lowest control value, eg: -100",
"automation.PIDController.setDerivativeFilter": "Sets the derivative filter gain",
"automation.PIDController.setDerivativeFilter|param|N": "the filter gain, eg:10",
"automation.PIDController.setGains": "Sets the PID gains",
"automation.PIDController.setGains|param|b": "setpoint weight, eg: 0.9",
"automation.PIDController.setGains|param|kd": "derivative gain",
"automation.PIDController.setGains|param|ki": "integral gain",
"automation.PIDController.setGains|param|kp": "proportional gain",
"automation.PIDController.setPoint": "Updates the desired setpoint",
"automation.addBehavior": "Adds the behavior and starts it",
"automation.addBehavior|param|behavior": "a behavior"
}

View File

@ -0,0 +1,12 @@
{
"automation.PIDController.compute|block": "%pid|compute for timestep %timestep|(s) at state %y",
"automation.PIDController.setControlSaturation|block": "set %pid|control saturation from %low|to %high",
"automation.PIDController.setDerivativeFilter|block": "set %pid|derivative filter %N",
"automation.PIDController.setGains|block": "set %pid|gains kp %kp|ki %ki|kd %kd",
"automation.PIDController.setPoint|block": "set %pid|point to %ysp",
"automation.addBehavior|block": "add behavior %behavior",
"automation|block": "automation",
"{id:category}Automation": "Automation",
"{id:group}Behaviors": "Behaviors",
"{id:group}PID": "PID"
}

View File

@ -1,6 +1,6 @@
{
"additionalFilePath": "../../node_modules/pxt-common-packages/libs/behaviors",
"additionalFilePath": "../../node_modules/pxt-common-packages/libs/automation",
"dependencies": {
"core": "file:../ev3"
"ev3": "file:../ev3"
}
}

View File

@ -1,14 +0,0 @@
{
"behaviors": "Behavior drive blocks",
"behaviors.Behavior": "A behavior",
"behaviors.BehaviorManager": "A manager for behaviors",
"behaviors.BehaviorManager.add": "Adds a new behavior to the behavior manager",
"behaviors.BehaviorManager.add|param|behavior": "the behavior to add",
"behaviors.BehaviorManager.start": "Starts the behavior control loop",
"behaviors.BehaviorManager.stop": "Stops the execution loop",
"behaviors.addBehavior": "Adds the behavior and starts it",
"behaviors.addBehavior|param|behavior": "a behavior",
"behaviors.avoidCrash": "A behavior that stops all motors if the sensor distance get too short",
"behaviors.driveForward": "A behavior that turns on the motors to the specified speed",
"behaviors.driveForward|param|motors": "@param speed the desired speed, eg: 50"
}

View File

@ -1,7 +0,0 @@
{
"behaviors.addBehavior|block": "add behavior %behavior",
"behaviors.avoidCrash|block": "avoid crash using %ultrasonic",
"behaviors.driveForward|block": "drive %motors|forward at %speed=motorSpeedPicker|%",
"behaviors|block": "behaviors",
"{id:category}Behaviors": "Behaviors"
}

View File

@ -1,56 +0,0 @@
namespace behaviors {
class AvoidCrashBehavior extends behaviors.Behavior {
private ultrasonic: sensors.UltraSonicSensor;
constructor(ultrasonic: sensors.UltraSonicSensor) {
super();
this.ultrasonic = ultrasonic;
}
shouldRun(): boolean {
return this.ultrasonic.distance() < 5;
}
run(): void {
motors.stopAllMotors();
this.active = false;
}
}
/**
* A behavior that stops all motors if the sensor distance get too short
*/
//% blockId=behaviorsAvoidCrash block="avoid crash using %ultrasonic"
export function avoidCrash(ultrasonic: sensors.UltraSonicSensor) : behaviors.Behavior {
return new AvoidCrashBehavior(ultrasonic);
}
class DriveForwardBehavior extends behaviors.Behavior {
private motors: motors.MotorBase;
private speed: number;
constructor(motors: motors.MotorBase, speed: number) {
super();
this.motors = motors;
this.speed = speed;
}
shouldRun(): boolean {
return true;
}
run(): void {
this.motors.setSpeed(this.speed);
pauseUntil(() => !this.active);
this.motors.setSpeed(0);
}
}
/**
* A behavior that turns on the motors to the specified speed
* @param motors
* @param speed the desired speed, eg: 50
*/
//% blockId=behaviorsDriveForward block="drive %motors|forward at %speed=motorSpeedPicker|%"
export function driveForward(motors: motors.MotorBase, speed: number): behaviors.Behavior {
return new DriveForwardBehavior(motors, speed);
}
}

View File

@ -52,9 +52,12 @@
"console.logValue|param|value": "to write",
"console.sendToScreen": "Sends the log messages to the brick screen and uses the brick up and down buttons to scroll.",
"control": "Program controls and events.",
"control.Timer.millis": "Gets the elapsed time in millis",
"control.Timer": "A timer",
"control.Timer.millis": "Gets the elapsed time in millis since the last reset",
"control.Timer.pauseUntil": "Pauses until the timer reaches the given amount of milliseconds",
"control.Timer.pauseUntil|param|ms": "how long to pause for, eg: 5, 100, 200, 500, 1000, 2000",
"control.Timer.reset": "Resets the timer",
"control.Timer.seconds": "Gets the elapsed time in seconds",
"control.Timer.seconds": "Gets the elapsed time in seconds since the last reset",
"control.allocateNotifyEvent": "Allocates the next user notification event",
"control.deviceFirmwareVersion": "Determine the version of system software currently running.",
"control.dmesg": "Write data to DMESG debugging buffer.",

View File

@ -48,6 +48,7 @@
"console.sendToScreen|block": "send console to screen",
"console|block": "console",
"control.Timer.millis|block": "%timer|millis",
"control.Timer.pauseUntil|block": "%timer|pause until (ms) %ms",
"control.Timer.reset|block": "%timer|reset",
"control.Timer.seconds|block": "%timer|seconds",
"control.raiseEvent|block": "raise event|from %src|with value %value",

View File

@ -4,34 +4,24 @@
*/
const enum BrickLight {
//% block=off enumval=0
//% blockIdentity=brick.lightPattern
Off = 0,
//% block=green enumval=1
//% blockIdentity=brick.lightPattern
Green = 1,
//% block=red enumval=2
//% blockIdentity=brick.lightPattern
Red = 2,
//% block=orange enumval=3
//% blockIdentity=brick.lightPattern
Orange = 3,
//% block="green flash" enumval=4
//% blockIdentity=brick.lightPattern
GreenFlash = 4,
//% block="red flash" enumval=5
//% blockIdentity=brick.lightPattern
RedFlash = 5,
//% block="orange flash" enumval=6
//% blockIdentity=brick.lightPattern
OrangeFlash = 6,
//% block="green pulse" enumval=7
//% blockIdentity=brick.lightPattern
GreenPulse = 7,
//% block="red pulse" enumval=8
//% blockIdentity=brick.lightPattern
RedPulse = 8,
//% block="orange pulse" enumval=9
//% blockIdentity=brick.lightPattern
OrangePulse = 9,
}

View File

@ -312,6 +312,7 @@ namespace sensors.internal {
reset() {
if (this.isActive()) uartReset(this._port);
this.realmode = 0;
}
}

View File

@ -1,4 +1,7 @@
namespace control {
/**
* A timer
*/
//% fixedInstances
export class Timer {
start: number;
@ -8,7 +11,7 @@ namespace control {
}
/**
* Gets the elapsed time in millis
* Gets the elapsed time in millis since the last reset
*/
//% blockId=timerMillis block="%timer|millis"
millis(): number {
@ -16,7 +19,7 @@ namespace control {
}
/**
* Gets the elapsed time in seconds
* Gets the elapsed time in seconds since the last reset
*/
//% blockId=timerSeconds block="%timer|seconds"
seconds(): number {
@ -30,6 +33,16 @@ namespace control {
reset() {
this.start = control.millis();
}
/**
* Pauses until the timer reaches the given amount of milliseconds
* @param ms how long to pause for, eg: 5, 100, 200, 500, 1000, 2000
*/
//% blockId=timerPauseUntil block="%timer|pause until (ms) %ms"
pauseUntil(ms: number) {
const remaining = this.millis() - ms;
loops.pause(Math.max(0, remaining));
}
}
//% whenUsed fixedInstance block="timer 1"

View File

@ -3,6 +3,9 @@
"datalog.addValue": "Adds a cell to the row of data",
"datalog.addValue|param|name": "name of the cell, eg: \"x\"",
"datalog.addValue|param|value": "value of the cell, eg: 0",
"datalog.flush": "Commits any buffered row to disk",
"datalog.setEnabled": "Turns on or off datalogging",
"datalog.setFile": "Starts a new data logger for the given file",
"datalog.setFile|param|filename": "the filename, eg: \"datalog.csv\"",
"datalog.setStorage": "* @param storage custom storage solution"
}

View File

@ -1,6 +1,7 @@
{
"datalog.addRow|block": "datalog add row",
"datalog.addValue|block": "datalog add %name|=%value",
"datalog.setEnabled|block": "datalog %enabled",
"datalog|block": "datalog",
"{id:category}Datalog": "Datalog"
}

View File

@ -3,13 +3,16 @@ namespace datalog {
let _headers: string[] = undefined;
let _headersLength: number;
let _values: number[];
let _buffer: string = "";
let _start: number;
let _filename = "data.csv";
let _filename = "datalog.csv";
let _storage: storage.Storage = storage.temporary;
let _enabled = true;
function clear() {
_headers = undefined;
_values = undefined;
_buffer = "";
}
function init() {
@ -31,7 +34,10 @@ namespace datalog {
_headersLength = _storage.size(_filename);
}
// commit row data
_storage.appendCSV(_filename, _values);
_buffer += storage.toCSV(_values, _storage.csvSeparator);
// buffered writes
if (_buffer.length > 1024)
flush();
}
// clear values
@ -44,6 +50,8 @@ namespace datalog {
//% weight=100
//% blockId=datalogAddRow block="datalog add row"
export function addRow(): void {
if (!_enabled) return;
commit();
init();
const s = (control.millis() - _start) / 1000;
@ -65,16 +73,16 @@ namespace datalog {
i = _headers.length - 1;
}
_values[i] = value;
if (i > 0) // 0 is time
console.logValue(name, value)
}
/**
* Starts a new data logger for the given file
* @param filename the filename, eg: "datalog.csv"
*/
//%
export function setFile(fn: string) {
_filename = fn;
export function setFile(filename: string) {
flush();
_filename = filename;
clear();
}
@ -84,7 +92,31 @@ namespace datalog {
*/
//%
export function setStorage(storage: storage.Storage) {
flush();
_storage = storage;
clear();
}
/**
* Commits any buffered row to disk
*/
//%
export function flush() {
if (_buffer) {
const b = _buffer;
_buffer = "";
_storage.append(_filename, b);
}
}
/**
* Turns on or off datalogging
* @param enabled
*/
//% blockId=datalogEnabled block="datalog %enabled"
//% enabled.fieldEditor=fieldonoff
export function setEnabled(enabled: boolean) {
flush();
_enabled = enabled;
}
}

View File

@ -13,7 +13,8 @@
"color-sensor": "file:../color-sensor",
"touch-sensor": "file:../touch-sensor",
"ultrasonic-sensor": "file:../ultrasonic-sensor",
"gyro-sensor": "file:../gyro-sensor"
"gyro-sensor": "file:../gyro-sensor",
"mood": "file:../mood"
},
"public": true
}

View File

@ -1,5 +1,7 @@
{
"sensors.GyroSensor.angle": "Get the current angle from the gyroscope.",
"sensors.GyroSensor.drift": "Gets the computed rate drift",
"sensors.GyroSensor.rate": "Get the current rotation rate from the gyroscope.",
"sensors.GyroSensor.reset": "Forces a calibration of the gyro. Must be called when the sensor is completely still."
"sensors.GyroSensor.reset": "Forces a calibration of the gyro. Must be called when the sensor is completely still.",
"sensors.GyroSensor.setDriftCorrection": "Enables or disable drift correction"
}

View File

@ -8,16 +8,27 @@ namespace sensors {
//% fixedInstances
export class GyroSensor extends internal.UartSensor {
private calibrating: boolean;
private _drift: number;
private _drifting: boolean;
constructor(port: number) {
super(port)
this.calibrating = false;
this._drift = 0;
this._drifting = true;
this.setMode(GyroSensorMode.Rate);
}
_deviceType() {
return DAL.DEVICE_TYPE_GYRO
}
_query(): number {
return this.getNumber(NumberFormat.Int16LE, 0);
}
setMode(m: GyroSensorMode) {
if (m == GyroSensorMode.Rate && this.mode != m)
this._drift = 0;
this._setMode(m)
}
@ -37,8 +48,8 @@ namespace sensors {
if (this.calibrating)
pauseUntil(() => !this.calibrating, 2000);
this.setMode(GyroSensorMode.Angle)
return this.getNumber(NumberFormat.Int16LE, 0)
this.setMode(GyroSensorMode.Angle);
return this._query();
}
/**
@ -57,8 +68,14 @@ namespace sensors {
if (this.calibrating)
pauseUntil(() => !this.calibrating, 2000);
this.setMode(GyroSensorMode.Rate)
return this.getNumber(NumberFormat.Int16LE, 0)
this.setMode(GyroSensorMode.Rate);
let curr = this._query();
if (Math.abs(curr) < 20) {
const p = 0.0005;
this._drift = (1 - p) * this._drift + p * curr;
curr -= this._drift;
}
return curr;
}
/**
@ -76,23 +93,45 @@ namespace sensors {
if (this.calibrating) return; // already in calibration mode
this.calibrating = true;
// may be triggered by a button click, give time to settle
loops.pause(500);
// may be triggered by a button click,
// give time for robot to settle
loops.pause(700);
// send a reset command
super.reset();
// we need to switch mode twice to perform a calibration
if (this.mode == GyroSensorMode.Rate)
this.setMode(GyroSensorMode.Angle);
else
this.setMode(GyroSensorMode.Rate);
// switch back and wait
if (this.mode == GyroSensorMode.Rate)
this.setMode(GyroSensorMode.Angle);
else
this.setMode(GyroSensorMode.Rate);
// give it more time to settle
loops.pause(500);
this.calibrating = false;
// switch back to the desired mode
this.setMode(this.mode);
// wait till sensor is live
pauseUntil(() => this.isActive());
// give it a bit of time to init
loops.pause(1000)
// compute drift
this._drift = 0;
if (this.mode == GyroSensorMode.Rate) {
for (let i = 0; i < 200; ++i) {
this._drift += this._query();
loops.pause(4);
}
this._drift /= 200;
}
// and we're done
this.calibrating = false;
}
/**
* Gets the computed rate drift
*/
//%
drift(): number {
return this._drift;
}
/**
* Enables or disable drift correction
* @param enabled
*/
//%
setDriftCorrection(enabled: boolean) {
this._drifting = enabled;
}
}

3
libs/mood/README.md Normal file
View File

@ -0,0 +1,3 @@
# mood
A package to put the EV3 in various moods.

View File

@ -0,0 +1,17 @@
{
"brick.Mood": "A mood",
"brick.Mood.show": "Shows the mood on the EV3",
"brick.showMood": "Shows a mood",
"moods.angry": "An angry mood",
"moods.awake": "A awake mood",
"moods.dizzy": "A dizzy mood",
"moods.knockedOut": "A knocked out mood",
"moods.love": "In love mood",
"moods.middleLeft": "Looking around left",
"moods.middleRight": "Looking around right",
"moods.neutral": "In a neutral mood",
"moods.sad": "A sad mood",
"moods.sleeping": "A sleeping mood",
"moods.tired": "A tired mood",
"moods.winking": "In laughing mood"
}

View File

@ -0,0 +1,8 @@
{
"brick.showMood|block": "show mood %mood=mood_image_picker",
"brick|block": "brick",
"moods|block": "moods",
"{id:category}Brick": "Brick",
"{id:category}Moods": "Moods",
"{id:group}Screen": "Screen"
}

125
libs/mood/mood.ts Normal file
View File

@ -0,0 +1,125 @@
namespace brick {
/**
* Shows a mood
*/
//% weight=90
//% blockId=moodShow block="show mood %mood=mood_image_picker"
//% weight=101 group="Screen" blockGap=8
export function showMood(mood: Mood) {
if(mood)
mood.show();
}
/**
* A mood
*/
//% fixedInstances
export class Mood {
private image: Image;
private sound: Sound;
private light: BrickLight;
constructor(image: Image, sound: Sound, light: BrickLight) {
this.image = image;
this.sound = sound;
this.light = light;
}
/**
* Shows the mood on the EV3
*/
show() {
brick.setLight(this.light);
brick.showImage(this.image);
music.playSoundEffectUntilDone(this.sound);
loops.pause(20);
}
}
/**
* An image
* @param image the image
*/
//% blockId=mood_image_picker block="%image" shim=TD_ID
//% image.fieldEditor="images"
//% image.fieldOptions.columns=4
//% image.fieldOptions.width=400
//% group="Screen" weight=0 blockHidden=1
export function __moodImagePicker(mood: Mood): Mood {
return mood;
}
}
namespace moods {
/**
* A sleeping mood
*/
//% fixedInstance jres=images.eyesSleeping
export const sleeping = new brick.Mood(images.eyesSleeping, sounds.expressionsSnoring, BrickLight.OrangePulse);
/**
* A awake mood
*/
//% fixedInstance jres=images.eyesAwake
export const awake = new brick.Mood(images.eyesAwake, sounds.informationActivate, BrickLight.Orange);
/**
* A tired mood
*/
//% fixedInstance jres=images.eyesTiredMiddle
export const tired = new brick.Mood(images.eyesTiredMiddle, sounds.expressionsSneezing, BrickLight.OrangeFlash);
/**
* An angry mood
*/
//% fixedInstance jres=images.eyesAngry
export const angry = new brick.Mood(images.eyesAngry, sounds.animalsDogGrowl, BrickLight.RedPulse);
/**
* A sad mood
*/
//% fixedInstance jres=images.eyesTear
export const sad = new brick.Mood(images.eyesTear, sounds.animalsDogWhine, BrickLight.Red);
/**
* A dizzy mood
*/
//% fixedInstance jres=images.eyesDizzy
export const dizzy = new brick.Mood(images.eyesDizzy, sounds.expressionsUhOh, BrickLight.OrangeFlash);
/**
* A knocked out mood
*/
//% fixedInstance jres=images.eyesKnockedOut
export const knockedOut = new brick.Mood(images.eyesKnockedOut, sounds.informationError, BrickLight.RedFlash);
/**
* Looking around left
*/
//% fixedInstance jres=images.eyesMiddleLeft
export const middleLeft = new brick.Mood(images.eyesMiddleLeft, sounds.informationAnalyze, BrickLight.Off);
/**
* Looking around right
*/
//% fixedInstance jres=images.eyesMiddleRight
export const middleRight = new brick.Mood(images.eyesMiddleRight, sounds.informationAnalyze, BrickLight.Off);
/**
* In love mood
*/
//% fixedInstance jres=images.eyesLove
export const love = new brick.Mood(images.eyesLove, sounds.expressionsMagicWand, BrickLight.GreenPulse);
/**
* In laughing mood
*/
//% fixedInstance jres=images.eyesWinking
export const winking = new brick.Mood(images.eyesWinking, sounds.expressionsLaughing1, BrickLight.GreenFlash);
/**
* In a neutral mood
*/
//% fixedInstance jres=images.eyesNeutral
export const neutral = new brick.Mood(images.eyesNeutral, undefined, BrickLight.Green);
}

15
libs/mood/pxt.json Normal file
View File

@ -0,0 +1,15 @@
{
"name": "mood",
"description": "The EV3 mood library",
"files": [
"README.md",
"mood.ts"
],
"testFiles": [
],
"public": true,
"dependencies": {
"core": "file:../core",
"music": "file:../music"
}
}

View File

@ -23,7 +23,7 @@
"music.setTempo": "Set the tempo a number of beats per minute (bpm).",
"music.setTempo|param|bpm": "The new tempo in beats per minute, eg: 120",
"music.setVolume": "Set the output volume of the sound synthesizer.",
"music.setVolume|param|volume": "the volume 0...256, eg: 128",
"music.setVolume|param|volume": "the volume 0...100, eg: 50",
"music.stopAllSounds": "Play a tone through the speaker for some amount of time.",
"music.tempo": "Return the tempo in beats per minute (bpm).\nTempo is the speed (bpm = beats per minute) at which notes play. The larger the tempo value, the faster the notes will play."
}

View File

@ -121,7 +121,7 @@
"sounds.mechanicalMotorStart|block": "mechanical motor start",
"sounds.mechanicalMotorStop|block": "mechanical motor stop",
"sounds.mechanicalRatchet|block": "mechanical ratchet",
"sounds.mechanicalSonar|block": "\"mechanical sonar\"",
"sounds.mechanicalSonar|block": "mechanical sonar",
"sounds.mechanicalTickTack|block": "mechanical tick tack",
"sounds.mechanicalWalk|block": "mechanical walk",
"sounds.movementsArm1|block": "movements arm1",

View File

@ -13,7 +13,7 @@
namespace music {
uint8_t currVolume = 2;
uint8_t currVolume = 50;
uint8_t *lmsSoundMMap;
int writeDev(void *data, int size) {
@ -25,16 +25,16 @@ int writeDev(void *data, int size) {
/**
* Set the output volume of the sound synthesizer.
* @param volume the volume 0...256, eg: 128
* @param volume the volume 0...100, eg: 50
*/
//% weight=96
//% blockId=synth_set_volume block="set volume %volume"
//% parts="speaker" blockGap=8
//% volume.min=0 volume.max=256
//% volume.min=0 volume.max=100
//% help=music/set-volume
//% weight=1
void setVolume(int volume) {
currVolume = max(0, min(100, volume * 100 / 256));
currVolume = max(0, min(100, volume));
}
#define SOUND_CMD_BREAK 0

View File

@ -3,12 +3,12 @@ declare namespace music {
/**
* Set the output volume of the sound synthesizer.
* @param volume the volume 0...256, eg: 128
* @param volume the volume 0...100, eg: 50
*/
//% weight=96
//% blockId=synth_set_volume block="set volume %volume"
//% parts="speaker" blockGap=8
//% volume.min=0 volume.max=256
//% volume.min=0 volume.max=100
//% help=music/set-volume
//% weight=1 shim=music::setVolume
function setVolume(volume: int32): void;

View File

@ -175,7 +175,7 @@ namespace sounds {
export const mechanicalMotorStop = music.fromWAV(hex``);
//% fixedInstance jres block="mechanical ratchet"
export const mechanicalRatchet = music.fromWAV(hex``);
//% fixedInstance jres block='"mechanical sonar"'
//% fixedInstance jres block="mechanical sonar"
export const mechanicalSonar = music.fromWAV(hex``);
//% fixedInstance jres block="mechanical tick tack"
export const mechanicalTickTack = music.fromWAV(hex``);

View File

@ -7,7 +7,7 @@ namespace storage {
//% fixedInstances
export class Storage {
csvSeparator: string;
constructor() {
constructor() {
this.csvSeparator = ",";
}
@ -80,18 +80,13 @@ namespace storage {
}
/**
* Append a row of CSV data
* @param filename the file name to append data, eg: "data.csv"
* @param data the data to append
*/
* Append a row of CSV data
* @param filename the file name to append data, eg: "data.csv"
* @param data the data to append
*/
//% blockId=storageAppendCSV block="storage %source|%filename|append CSV %data"
appendCSV(filename: string, data: number[]) {
let s = ""
for (const d of data) {
if (s) s += this.csvSeparator;
s = s + d;
}
s += "\r\n"
let s = toCSV(data, this.csvSeparator);
this.append(filename, s)
}
@ -167,6 +162,16 @@ namespace storage {
}
}
export function toCSV(data: number[], sep: string) {
let s = ""
for (const d of data) {
if (s) s += sep;
s = s + d;
}
s += "\r\n"
return s;
}
class TemporaryStorage extends Storage {
constructor() {
super();

View File

@ -1,6 +1,6 @@
{
"name": "pxt-ev3",
"version": "0.0.65",
"version": "0.0.70",
"description": "LEGO Mindstorms EV3 for Microsoft MakeCode",
"private": true,
"keywords": [
@ -44,7 +44,7 @@
"webfonts-generator": "^0.4.0"
},
"dependencies": {
"pxt-common-packages": "0.15.5",
"pxt-common-packages": "0.15.7",
"pxt-core": "3.0.11"
},
"scripts": {

View File

@ -16,11 +16,12 @@
"libs/infrared-sensor",
"libs/gyro-sensor",
"libs/chassis",
"libs/mood",
"libs/ev3",
"libs/storage",
"libs/datalog",
"libs/tests",
"libs/behaviors"
"libs/automation"
],
"simulator": {
"autoRun": true,

View File

@ -97,28 +97,21 @@ namespace pxsim {
return this.brickNode;
}
motorUsed(port: number, large: boolean) {
motorUsed(ports: number, large: boolean): boolean {
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
const p = 1 << i;
if (port & p) {
const motorPort = this.motorMap[p];
if (!this.outputNodes[motorPort])
this.outputNodes[motorPort] = new MotorNode(motorPort, large);
}
}
}
hasMotor(port: number) {
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
const p = 1 << i;
if (port & p) {
if (ports & p) {
const motorPort = this.motorMap[p];
const outputNode = this.outputNodes[motorPort];
if (outputNode)
return true;
if (!outputNode) {
this.outputNodes[motorPort] = new MotorNode(motorPort, large);
continue;
}
if (outputNode && outputNode.isLarge() != large)
return false;
}
}
return false;
return true;
}
getMotor(port: number, large?: boolean): MotorNode[] {

View File

@ -3,15 +3,23 @@
import lf = pxsim.localization.lf;
namespace pxsim.motors {
export function __motorUsed(port: number, large: boolean) {
//console.log("MOTOR INIT " + port);
if (!ev3board().hasMotor(port)) {
ev3board().motorUsed(port, large);
runtime.queueDisplayUpdate();
} else {
U.userError(`${lf("Multiple motors are connected to Port")} ${String.fromCharCode('A'.charCodeAt(0) + ev3board().motorMap[port])}`);
function portsToString(out: number): string {
let r = "";
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
if (out & (1 << i)) {
if (r.length > 0) r += "+";
r += "ABCD"[i];
}
}
return r;
}
export function __motorUsed(ports: number, large: boolean) {
//console.log("MOTOR INIT " + port);
if (ev3board().motorUsed(ports, large))
runtime.queueDisplayUpdate();
else
U.userError(`${lf("Multiple motors are connected to Port")} ${portsToString(ports)}`);
}
}

View File

@ -70,6 +70,10 @@ namespace pxsim {
this.rotationsPerMilliSecond = (large ? 170 : 250) / 60000;
}
isLarge(): boolean {
return this.id == NodeType.LargeMotor;
}
setPolarity(polarity: number) {
// Either 1 or 255 (reverse)
/*

View File

@ -9,6 +9,7 @@
},
"galleries": {
"Maker Activities": "maker",
"Coding Activites": "coding"
"Coding Activites": "coding",
"Lessons": "lessons"
}
}