M
R
O
ST
X-
C
R
M R Auteur(s)
Description de la Version : - version initiale, origine, libellÊ du chapitre et du sous-chapitre, indication d’ajout/suppression, version finale.
Date
-
./0120.11.
ST
& & ' ()#*+,
O
Version
3
,
3
4
4 5
6
2
X-
&
-
&
.
-
R
C
&
(
!
"#$
!
"
#$ %
% & '(
) 7
2
*
22
2'
)+
+
2.
+
2.
8
M
+ .
4
9
<
%=
>
%3
4
%
>
(D &
&
,
2?
$
2A
ST
>
# '
2A 2B
, ?
6 ,
2>
2>
E
>
2<
)5
O
;
R
: .
)
$
2/
X-
?
+@
-
C
#
5
2/ .1
B
7
.
0 +1
B ,
+
R
F A
G
.1
C +
.1
C
/
0 si #
)1
&
21 22
.2 ..
& '
++
*
H
.< .>
*
.? 8
=
.A .B
%3
+
I
./ D&
I
.C
&
M
+ G
.C <1
9)
;
O
<2 8
<.
<<
ST
+
<>
*
95
*
<>
&
R
C
X-
8
R
8
<?
<A
2 * : # % * ;
* <
& ;
$ ; =
= % >
<
8
1 !"# $ ' '$
M
& '(
%
&
9@ &
%
J
ST
,
'
O
& ( & !"# $ '
)* ' + ,-. % ' ''
R
(
$
9
J
& 700KKK
0L
J
E 7
X-
&
G 5
O
3
C
K J$
$:
$
R
0 50
5 M N
+
+
0
J
5 &J
5 P5
Q
;
F * R#-% *+ ,
+I*
* @
)
8
) * $J
=G
M
&
> 3
4
J
% 8
8
8
8 5 <CE>A = RCX =
*
+ J 5
8
&D O
9
5
%
9
5 -
C
X-
ST
O
7
>
&
J
? constructopedia
4
4
R
G
J
R
+
9E " P-
5
DĂŠmarrez avec le 8 RcxCC PF K P
5 QJ S *
V
58
RIS 5
9
9 @ 9E + 9 8
8
;8@ RCX
9E
!
Q
8 9#T
5
+
5 J & & =Q
86 E 5
U
R W
9E RCX , 5
E
P
4 G Q
8 &
= J
&
J8
&
J J *
& H
&
5 5 8
8
= -
9
5 9
G
J
A
(
9
7 task main() { OnFwd(OUT_A); OnFwd(OUT_C); Wait(400);
M
OnRev(OUT_A+OUT_C); Wait(400); Off(OUT_A+OUT_C); }
D& 5 (D &
J H main P 5 = 5 5 58 58 O 5 5 7
&4
D& J
O 7 J
D&
D& $H
D&
&5
5
O
E
NQC incipale). & 5 9
R
5 @ main q I D&
D&
task main() {
ST
statement1; statement2; …
…
}
=
3
OnFwd(OUT_A);
RCXJ
+
&
X-
5 5
=
7
+J 8 E E $
+ =
J
5
8
OnFwd(OUT_C); G
+ 4
=4
J
=
Wait(400);
C
* 8
8
J8 E E
&4 J
8 &X &
X
9 4
> H $
20211 8 >
OnRev(OUT_A+OUT_C); 8
R
E
9 4
J
95 =
Wait(400);
5
G
=
>
5
J
4
$ J
task
#UT_A+OUT_C O
J8 E
= $
9
H
>
Off(OUT_A+OUT_C); % 8
= 4
5
8 8
9
8 & NQC $ H 8
5 5
>
J
5
5
8 9
8
* ' I
5
9 Q
= Q $H 5
G
P
=
P
5 & H9
X Q +
E
8
X J
J 9 Z G P
9 J
P 8 E E H
5
P,8 H * ,
J H
9
VI W
Y
&
E H9 E
Q VI W E
Q
J 5
9[
J
*
-
%
H
&
5
95 5
5 7
$
5
O
R
M
G J
5
4 8 8
9 5 8
8
8 $H
5 =
5
P
3 8
5
J
8
Q
H
ST
4
mal tapé
P
=
J
H
J
95 + 9
= 5
4
#
#
OUT_B
,
8 H 5 5 5 &
5 O
Q +
-
9
5 J
3 & 1 B B
9
X-
J1
6
3
H
P
@
5
58
SetPower () Q -
7
task main() {
R
C
SetPower(OUT_A+OUT_C,2); OnFwd(OUT_A+OUT_C); Wait(400); OnRev(OUT_A+OUT_C); Wait(400); Off(OUT_A+OUT_C);
} ;'
'
& 9
-
9 RCX
= 9 -
5 9
5 5 D&
RCX 5
NQCJ J
NQC ( main Pprincipale) 5 @
5
RCX
H & & NQC 9 8
3
J
8= J =
9
5
&5 9+
7 OnFwd ()JOnRev ()JSetPower () Wait()
5
Off() 3
J
9
#
' 8
5 5
4 5 5
J
6 -
%
H
NQC
-
9 -
=
(
G J
9E
& &
4
8
9E
9E
=
$
C1
-
9
H
4
/?
H
H
J
9
Wait() 7
=4
5
3 65 NQC
R
C1 &
M
task main() { OnFwd(OUT_A+OUT_C); Wait(100); OnRev(OUT_C); Wait(85); Off(OUT_A+OUT_C); }
5
100
#define TURN_TIME
85
O
#define MOVE_TIME
task main() {
ST
OnFwd(OUT_A+OUT_C); Wait(MOVE_TIME); OnRev(OUT_C); Wait(TURN_TIME); Off(OUT_A+OUT_C);
}
=
=constantes constantes 5 95
4
&
&
5
8
H &
J
8 C1 5
J
7
(D &
H
RCX
=
E
J
9
' ' 5
@
&
I J
G
R
C
%
G =
constantes
X-
8
=E
,
C1
J
@
7
repeat.
#define MOVE_TIME #define TURN_TIME
100 85
task main() { repeat(4) { OnFwd(OUT_A+OUT_C); Wait(MOVE_TIME); OnRev(OUT_C); Wait(TURN_TIME); }
M
Off(OUT_A+OUT_C); } repeatJ
4 5
J J
5 5
&
G
&4 J
95 J =
5
9E
8
21
-
R
D&
&4 J
G
#define MOVE_TIME #define TURN_TIME
7
100 85
O
task main() {
J
ST
repeat(10) { repeat(4) {
OnFwd(OUT_A+OUT_C); Wait(MOVE_TIME); OnRev(OUT_C);
X-
Wait(TURN_TIME);
}
}
Off(OUT_A+OUT_C);
C
}
R
$H X 5 X= &4
8
8
9
5 D& =4 4
H 4
=4 J H 9J
5
5
& 4
9 , H9 &4 5 4 5 4 &4
&4 &4
&4
=C 3
//
J J G
/*
8 H@ G
5 5 H
*/
RCX
7
B
&5
5 I
9
/*
10 CARRES par Mark Overmars
Ce programme fait faire 10 carrées au robot */ #define MOVE_TIME mouvement droit #define TURN_TIME virage de 90 degrés
100
// Temps pour un
85
// Temps pour
task main()
M
{ repeat(10) {
// Faire 10 carrés
repeat(4)
R
{
OnFwd(OUT_A+OUT_C);
O
Wait(MOVE_TIME); OnRev(OUT_C);
Wait(TURN_TIME);
ST
}
}
Off(OUT_A+OUT_C); Maintenant les moteurs
// éteint
}
'
X-
;'
& 9 @ 58 5 U
9
5
8
8
9 5 5
+ 8
&
+ 9
5 =
&
&
-
C
#
R
repeat
8 &4
4 5
&5 R &
H
9E
8
H
=
2' , G
95
O 5 &5
8
&5 de MOVE_TIME * constantes G & G NQC -
8 E E E
[MOVE_TIME
8 9
.
<.
9
&
#define TURN_TIME
85
int move_time; variable
// définit une
task main() { move_time = 20; initiale
// fixe la valeur
repeat(50) { OnFwd(OUT_A+OUT_C); Wait(move_time); //emploit la variable pour la temporisation
M
OnRev(OUT_C); Wait(TURN_TIME);
// incrémente la
R
move_time += 5; variable }
}
P
G Q , .1
=4
%
J 5
8 P H @ constantesJ 8 & PM \Q # =constantesJ D& J Q nt 8 HG R Z =4 + J&5 5 H9 5 H 5 ? 4 <1J
.?J
4
8
H
* =J
& 5 5
- =
Q-
9
XC
-
R
&
ST
H P
5 5
nt
E
O
Off(OUT_A+OUT_C);
=
7
/ = P
@
Q
J .1
.1 , J & J
95 =
5
int aaa; int bbb, ccc; task main() { aaa = 10; bbb = 20 * 5; ccc = bbb; ccc /= aaa; ccc -= 5; // aaa est maintenant
M
aaa = 10 * (ccc + 3); égal à 80 } 9
= 8
4 =
='
R
<
5
E
=
&
5
5
5 5
&
8 & 9
NQC
*
O
$
O
8
5
5
int move_time, turn_time;
ST
task main() { while(true) {
move_time = Random(60); turn_time = Random(40);
X-
OnFwd(OUT_A+OUT_C); Wait(move_time); OnRev(OUT_A); Wait(turn_time);
R
C
}
} RandomP60)
= 1 A1P
G 1
8 -
H9
H
whilePvrai). &4 @
,
J
5
A1Q
&5 =
P
Wait (Random(60)).)
3 65 H !tant que " 4 true @ J 9
95
P E
Q 5 &4
while
6
;' & J *
' 9
8 D&
4 9 8
<.
D
J
J R
=
-
9
J 5 3
Y
9
while
8
/
0 while
& 5
=
6X
&
E
5 5
X 6
si
'
3
6
%
95
4 H 9E @ 58J
if
5
= =
& 5
8
& &
1
2 ,
1
Y
#define MOVE_TIME #define TURN_TIME
3 1 2J 8 E E & -
7
100 85
M
task main() { while(true)
R
{ OnFwd(OUT_A+OUT_C); Wait(MOVE_TIME);
{
O
if (Random(1) == 0)
ST
OnRev(OUT_C); }
else {
OnRev(OUT_A);
X-
}
Wait(TURN_TIME);
}
R
C
}
if
while ,
&4 = random(1Q do G H ]] 65 ] 9 ]] ^
3
=
+
J
5
H
$
1 7 O
-
5
^] 3
5
]
)1
&4 4 random(1Q == 0. C 9 5 7
&4 else = 5 5
J
_] 9 =
5 5
8
X XJou ||J 5
``J 5
true
@
false
Faux @
ttt != 3
5
ttt 8
<
(ttt >= 5) && (ttt <= 10)
5
ttt
?
P
21
aaa
(aaa == 10) || (bbb == 10) =Q 95
X X-
7
bbb P
= 21 if Q
= 4 elseJ5
4 =
5 9
4
E
'
J5
= E 5
8 8 H
5 else
2
$H
6J
# $
7
M
do { statements; } while (condition);
O
R
Faire { DĂŠclarations; } Tant que (condition); &4 5
4
$
.1
# = = 8 G
E
5 8
G
int move_time, turn_time, total_time;
R
C
X-
ST
task main() { total_time = 0; do { move_time = Random(100); turn_time = Random(100); OnFwd(OUT_A+OUT_C); Wait(move_time); OnRev(OUT_C); Wait(turn_time); total_time += move_time; total_time += turn_time; } while (total_time < 2000); Off(OUT_A+OUT_C); }
9
=
5
= 9
5 8 9
DO
5 while
3
P
5 8H
5 G 8= tant queJ
O 5
9 Q *
E while * J
5
# @
G
=
=
;'
'
& % 5 8=
= =
$
if
6 7 while 4
5
E G 5
))
5 9
58
do O
6 +
H9
J
2' Lego 5 + 5 @ Z J 9 9 4
I =
5
9 &
>
./
J 5
7
9
8
=
'
O 7
-
RCX
2
O
F
R
M
@ constructopedia -
9
4
5
J
58
5 5
&
ST
task main() { SetSensor(SENSOR_1,SENSOR_TOUCH); OnFwd(OUT_A+OUT_C); until (SENSOR_1 == 1); Off(OUT_A+OUT_C); }
$H
=
H
4
SENSOR_1
8
SENSOR_2
X-
3
H
4
5 H +
%
H
8
4
$
SENSOR_1
HJ
5
&4 G ] 2J 5
]1 D&
5 5
H
'
J
H
=
&5 -
R
C
=
=
SENSOR_3 SENSOR_TOUCH 5 5 8 SENSOR_LIGHT + 4 5
J
5
(
5 5
)+
5 7
@J
task main() { SetSensor(SENSOR_1,SENSOR_TOUCH); OnFwd(OUT_A+OUT_C); while (true) { if (SENSOR_1 == 1) { OnRev(OUT_A+OUT_C); Wait(30); OnFwd(OUT_A); Wait(30); OnFwd(OUT_A+OUT_C); } } } 8=
J while P J
20<
H
J 4 4
& 5 5
&
J8
20< 9
5
H4 4
O 8 &
8
8 @ 5 58
8
MindStorms, 4 4
8= J
8
9E
4 4 4 = 8
.
R
4 J
7
X-
ST
O
=
%
E
%
3
8
M
2'
5 5 Q
! 5
5
8 -
4
"
R RIS P
5 8
5
J 4
Q 4
4 5
8
R
C
8
)9
E
&5
#define SEUIL 40 task main() { SetSensor(SENSOR_2,SENSOR_LIGHT); OnFwd(OUT_A+OUT_C); while (true) { if (SENSOR_2 > SEUIL) { OnRev(OUT_C); until (SENSOR_2 <= SEUIL); OnFwd(OUT_A+OUT_C); } } } 8
5 5
. 5
&5 5
G
4 J
5 >1P 5
95
=
wait(10)
9 until
J
9
until P@ 5 8Q 5 8 5 5 9 J & H9 5
F
E G
-
5
95
3
=
4
3
=
J
9 H9
5
= 4
4
ST
%3
H 9 8@
H
O
%
%
R
'
&
H
8 5
&
;'
H
M
Q
58
%
4 5
F 58
@
8
NQC 5
D& *
$
E D&
&
D& 9
E =
%3
I
NQC
= 21 D & & 5 D& I D& D& = D& = 8 G = H = 4 P 4 D& Q I D& H 8 G 3 D& G Y S G 8 D& * 9 5 J H $ D& J 5 P 8 E E G = 8 H = D& J D& 5 Y8
main(principal)
X-
D&
D&
=
9E
7
-
R
C
&
)5
5 J G
= D& J
*
5 = Q
5
=
task main() { SetSensor(SENSOR_1,SENSOR_TOUCH); start verif_capteur; start circuit_carre; } task circuit_carre() { while (true) { OnFwd(OUT_A+OUT_C); Wait(100); OnRev(OUT_C); Wait(85); } }
R
M
task verif_capteur() { while (true) { if (SENSOR_1 == 1) { stop circuit_carre; OnRev(OUT_A+OUT_C); Wait(50); OnFwd(OUT_A); Wait(85); start circuit_carre; } } } = H J D & circuit_carre verif_capteur G D & circuit_carre. C8 4 % circuit_carre $ 4 5 D& 5
O
D&
ST /
3
D& D& 7(
8 6
+
9
G = 5
4
4
9
G
E
* D & NQC P
8
/
+ 45
=
9
@5
D&
@
, Verif_capteur 4
&
5 5
=
E
9= RCXQ 4
=
R
C
X-
sub tourne_autour() { OnRev(OUT_C); Wait(340); OnFwd(OUT_A+OUT_C); } task main() { OnFwd(OUT_A+OUT_C); Wait(100); tourne_autour(); Wait(200); tourne_autour(); Wait(100); tourne_autour(); Off(OUT_A+OUT_C); } E E
&4 4 4 J
5
D&
95 = 8 H
E
G
5 &4
),
95
8 H
5
E G
8
D&
8 G = J RCXJ
E + 5
&9 5
E
J
5
E 9 0
J/
E RCX * 5
J8 =E J
a
=
H 1
J G
4
E
5
D& D& =
4 J E R
J 5 '
G 8 J
+ 2
4
=
8 =
H9 G J
5
void P 8
E U
65
G sub Q+
58
RCX 8
5
J &5 8=
J
8 H 8 , 5
H
4
=3
> ' '
R
+
$ 4 =
5
6
H E =
E
=
H S
H 5
O 58 !! =
J8 E void H
P J
7
M
void tourne_autour() { OnRev(OUT_C); Wait(340); OnFwd(OUT_A+OUT_C); }
O
R
task main() { OnFwd(OUT_A+OUT_C); Wait(100); tourne_autour(); Wait(200); tourne_autour(); Wait(100); tourne_autour(); Off(OUT_A+OUT_C); }
E
$
H
3
ST
G
=
J
J
=
J
=
7
void tourne_autour(int tempsvirage) { OnRev(OUT_C); Wait(tempsvirage); OnFwd(OUT_A+OUT_C); }
R
C
X-
task main() { OnFwd(OUT_A+OUT_C); Wait(100); tourne_autour(200); Wait(200); tourne_autour(50); Wait(100); tourne_autour(300); Off(OUT_A+OUT_C); }
95 5
5 J
&4 8
4
8 P H
& =Q
5
tempsvirage
H
9
2'( $H NQC P
O G 8
5
constantesJ
macros H b G
-
RCXQ
J
)B
macros 5
* J
8
9
8
#define tourne_autour OnRev(OUT_C);Wait(340);OnFwd(OUT_A+OUT_C); task main() { OnFwd(OUT_A+OUT_C); Wait(100); tourne_autour; Wait(200); tourne_autour; Wait(100); tourne_autour; Off(OUT_A+OUT_C); }
R
M
Après la déclaration #define il y a le mot tourne_autour. Maintenant partout où vous taperez tourne_autour, ce sera remplacé par ce texte. Notez que le texte doit être sur une ligne. (En réalité il y a des façons de mettre une déclaration #define sur des lignes multiples, mais on ne recommande pas cela.) Les définitions de déclarations sont en réalité beaucoup plus puissantes. Elles peuvent aussi avoir des arguments. Par exemple, nous pouvons mettre la durée pour le virage comme un argument dans la déclaration. Voici un exemple dans lequel nous définissons quatre ; un pour se déplacer en avant, un pour se déplacer en arrière, un pour tourner gauche et un pour tourner à droite. Chacun a deux arguments : la vitesse et la durée. #define tourne_droit(s,t) SetPower(OUT_A+OUT_C,s);OnFwd(OUT_A);OnRev(OUT_C);Wait(t);
O
#define tourne_gauche(s,t) SetPower(OUT_A+OUT_C,s);OnRev(OUT_A);OnFwd(OUT_C);Wait(t); #define en_avant(s,t) SetPower(OUT_A+OUT_C,s);OnFwd(OUT_A+OUT_C);Wait(t);
ST
#define en_arriere(s,t) SetPower(OUT_A+OUT_C,s);OnRev(OUT_A+OUT_C);Wait(t);
X-
task main() { en_avant(3,200); tourne_gauche(7,85); en_avant(7,100); en_arriere(7,200); en_avant(7,100); tourne_droit(7,85); en_avant(3,200); Off(OUT_A+OUT_C); }
R
C
Il est très utile de définir de telles . Il rend votre code plus compact et lisible. Aussi, vous pouvez effectuer plus facilement les modifications de votre code quand vous changez par exemple les connexions aux moteurs. ;'
'
Dans ce chapitre vous avez vu l'utilisation de tâches, des sous-programmes, des fonctions intégrées et des . Elles ont des utilisations différentes. Les tâches peuvent tourner en même temps et s'occuper de choses différentes qui doivent être faits en même temps. Les sous-programmes sont utiles quand les plus grands blocs de code doivent être employées à des places différentes dans une même tâche. Les fonctions intégrées sont utiles quand les blocs de code doivent être employées à beaucoup d’endroits différents dans des tâches différentes, mais ils emploient plus de mémoire. Finalement les sont très utile pour
).
les petits blocs de code qui doivent être employé à plusieurs endroits. Ils peuvent aussi avoir des paramètres, les rendant même plus utile. Maintenant que vous avez suivi tous les chapitres jusqu'ici, vous avez toutes les connaissances pour faire faire à votre robot des choses compliquées. Les autres chapitres de ce tutoriel vous apprennent d'autres aspects qui sont importants seulement dans certaines applications.
6 Le : a un haut-parleur incorporé qui peut faire des sons et même jouer des morceaux de musique simples. C'est en particulier utile quand vous voulez faire dire au : que quelque chose arrive. Mais il peut aussi être drôle d'avoir un robot faisant de la musique tandis qu'il se déplace. >
'
Il y a six sons incorporés dans le 0 Clic clef
:, numéroté de 0 à 5. Ils « sonnent » comme suit :
M
/
Signal sonore de type “bip-bip”
2
Diminution de champ de fréquence
3
Augmentation de champ de fréquence
4
'
R
1
O
&&&' son d'Erreur
5 augmentation rapide de champ de fréquence (). Voici un petit programme Vous pouvez les jouer employant les commandes 3 H, qui les joue tous.
ST
task main() { PlaySound(0); PlaySound(1); PlaySound(2); PlaySound(3); PlaySound(4); PlaySound(5); }
X-
Wait(100); Wait(100); Wait(100); Wait(100); Wait(100); Wait(100);
R
C
Vous pourriez vous demander pourquoi il y a les commandes wait. La raison est que la commande qui joue le son n'attend pas que la précédente soit finie pour commencer. Il exécute immédiatement la commande suivante. Le : a une petite mémoire tampon dans laquel il peut stocker quelques sons, mais après un moment cette mémoire devient pleine et les sons sont se perdent. Ce n'est pas si important pour des sons mais ça l'est pour la musique, comme nous le verrons ci-dessous. Notez que l'argument de 3 H, () doit être une constante. Vous ne pouvez pas mettre une variable ici! F
Pour une musique plus intéressante, a la commande 3 H( (). Il a deux arguments. Le premier est la fréquence et le deuxième la durée (avec un cycle de 1/100ème de seconde, comme dans la commande wait). Voici une table de fréquences utiles : Son
1
2
3
4
5
6
7
Sol#
52
104
208
415
831
1661
3322
)
8
49
98
196
392
784
1568
3136
Fa#
46
92
185
370
740
1480
2960
Fa
44
87
175
349
698
1397
2794
Mi
41
82
165
330
659
1319
2637
Ré#
39
78
156
311
622
1245
2489
Ré
37
73
147
294
587
1175
2349
Do#
35
69
139
277
554
1109
2217
Do
33
65
131
262
523
1047
2093
4186
SI
31
62
123
247
494
988
1976
3951
La#
29
58
117
233
466
932
1865
3729
La
28
55
110
220
M
Sol
440
880
1760
3520
{
O
task main()
R
Comme nous l’avons noté ci-dessus pour les sons, ici aussi le : n'attend pas la fin de la note pour commencer. Ainsi si vous en employez beaucoup à la suite mieux vaut ajouter des commandes wait au milieu légèrement plus longue. Voici un exemple :
PlayTone(262,40); PlayTone(294,40); PlayTone(330,40); PlayTone(294,40); PlayTone(262,160);
ST
}
Wait(50); Wait(50); Wait(50); Wait(50); Wait(200);
R
C
X-
Vous pouvez créer des morceaux de musique en employant très facilement le Piano : qui fait partie du Centre de Commande de :. Si vous voulez jouer de la musique du : en se déplaçant, mieux vaut employer une tâche séparée pour cela. Ici vous avez un exemple d'un programme plutôt stupide où le : roule dans les deux sens, en faisant constamment de la musique.
)D
task music() { while (true) { PlayTone(262,40); PlayTone(294,40); PlayTone(330,40); PlayTone(294,40); } }
Wait(50); Wait(50); Wait(50); Wait(50);
;'
M
task main() { start music; while(true) { OnFwd(OUT_A+OUT_C); Wait(300); OnRev(OUT_A+OUT_C); Wait(300); } }
'
R
Dans ce chapitre vous avez appris comment laisser le : faire des sons et de la musique. Vous avez aussi vu comment employer une tâche séparée pour la musique.
7
0
/G
O
Il y a quelques commandes complémentaires pour l’usage des moteurs que vous pouvez employer pour les contrôler plus précisément. Dans ce chapitre nous allons les étudier.
ST
Quand vous employez la commande Off (), le moteur s’arrête immédiatement en employant le frein. Dans il est aussi possible d'arrêter les moteurs d'une façon plus douce, sans employer le frein. Pour cela vous employez la commande Float () [débrayer]. Il y a des cas où cela est préférable. Voici un exemple. D'abord le robot s’arrête en employant les freins; ensuite sans employer les freins. Notez la différence. (En réalité la différence est minime pour ce type de robot. Mais il peut faire une grande différence pour d’autres robots.)
R
C
X-
task main() { OnFwd(OUT_A+OUT_C); Wait(200); Off(OUT_A+OUT_C); Wait(100); OnFwd(OUT_A+OUT_C); Wait(200); Float(OUT_A+OUT_C); }
8
=-
'
La commande # K () fait en réalité deux choses : il démarre le moteur et il met la direction en marche avant. La commande #nRev P) ait aussi deux choses : il démarre le moteur et inverse la direction. a aussi des commandes pour faire ces deux choses séparément. Si vous seulement voulez changer une des deux choses, il est plus efficace d'employer ces commandes séparément; cela emploie moins de mémoire dans le :, c'est plus rapide. Les deux commandes séparées sont SetDirection () qui met la direction (OUT_FWD, OUT_REV ou OUT_TOGGLE qui donne un petit coup à la direction actuelle)
+1
et SetOutput () qui met le mode (OUT_ON, OUT_OFF ou OUT_FLOAT). Voici un programme simple qui dirige le robot en avant, en arrière et en avant de nouveau. task main() { SetPower(OUT_A+OUT_C,7); SetDirection(OUT_A+OUT_C,OUT_FWD); SetOutput(OUT_A+OUT_C,OUT_ON); Wait(200); SetDirection(OUT_A+OUT_C,OUT_REV); Wait(200); SetDirection(OUT_A+OUT_C,OUT_TOGGLE); Wait(200); SetOutput(OUT_A+OUT_C,OUT_FLOAT); }
M
Notez que, au début de chaque programme, tous les moteurs sont, par défaut, avec la direction en avant et la vitesse est mise à 7. Ainsi dans l’exemple précédent, les deux premières commandes ne sont pas nécessaires. Il y a quelques autres commandes du moteur, qui sont des raccourcis pour les combinaisons des commandes ci-dessus. Voici une liste complète : démarre les moteurs
R
On(‘port de sortie’)
arrête les moteurs
Off(‘port de sortie’)
débraye les moteurs sans à-coup
O
Float(‘port de sortie’) Fwd(‘port de sortie’)
marche avant (mais ne les fait pas rouler) Rev(‘port de sortie’)
ST
marche arrière (mais ne les fait pas rouler)
Commute les moteurs en
Toggle(‘port de sortie’)
inverse la direction des moteurs
OnFwd(‘port de sortie’)
Commute les moteurs en marche avant
et les démarre
Commute les moteurs en marche
X-
OnRev(‘port de sortie’)
arrière et les démarre
démarre les moteurs pour un cycle de
OnFor(‘port de sortie’,’durée’)
temps
C
SetOutput(‘port de sortie’,’mode’)
sortie (OUT_ON, OUT_OFF ou OUT_FLOAT) SetDirection(‘port de sortie’,’dir’)
R
Commute les moteurs en
(OUT_FWD, OUT_REV ou OUT_TOGGLE) SetPower(‘port de sortie’,’puissance’)
sortie (0-9) 8
fixe le mode des ports de fixe la direction des ports de sortie fixe la puissance des port de
-
Comme vous l’avez probablement remarqué, changer la vitesse des moteurs n'a pas beaucoup d'effet. La raison en est que vous changez principalement le couple de démarrage,
+)
pas la vitesse. Vous verrez seulement un effet si le moteur a une charge lourde. Et même alors, la différence entre 2 et 7 est très petite. Si vous voulez avoir de meilleurs effets le truc est de faire démarrer/arrêter les moteurs dans un enchaînement rapide. Voici un programme simple qui fait cela. Il a une tâche, appelée ctrl_moteur qui contrôle les moteurs. Il vérifie constamment la vitesse variable pour voir quelle est la vitesse actuelle. Si la valeur est Positive alors le robot est en marche avant, si négatif en marche arrière. Il met les moteurs dans la bonne direction et attend ensuite pendant quelque temps, selon la vitesse, avant de débrancher les moteurs de nouveau. La tâche principale fixe simplement les vitesses et attend. int vitesse, __vitesse; task ctrl_moteur() { while (true) { __vitesse = vitesse; if (__vitesse > 0) {OnFwd(OUT_A+OUT_C);}
M
if (__vitesse < 0) {OnRev(OUT_A+OUT_C); __vitesse = -__vitesse;} Wait(__vitesse);
R
Off(OUT_A+OUT_C); }
O
}
ST
task main() { vitesse = 0; start ctrl_moteur; vitesse = 1; Wait(200); vitesse = -10; Wait(200); vitesse = 5; Wait(200); vitesse = -2; Wait(200); stop ctrl_moteur; Off(OUT_A+OUT_C); }
X-
Ce programme peut être rendu beaucoup plus puissant, en tenant compte des rotations et aussi en incorporant probablement un temps d’attente après la commande off(). Expérimentez le vous-même. ;'
'
R
C
Dans ce chapitre vous avez appris les commandes supplémentaires du moteur qui sont () qui fixe la disponibles : Float () qui débraye le moteur doucement, , direction (OUT_FWD, OUT_REV ou OUT_TOGGLE qui inverse la direction actuelle) et , # () qui met le mode (OUT_ON, OUT_OFF ou OUT_FLOAT). Vous avez vu la liste complète de commandes disponibles du moteur. Vous avez aussi appris un truc pour contrôler la vitesse du moteur d'une meilleure façon.
&
'
Dans le Chapitre V nous avons discuté des aspects de base pour employer des détecteurs. Mais vous pouvez en faire beaucoup plus avec des détecteurs. Dans ce chapitre nous verrons la différence entre le mode de détecteur et le type de détecteur, nous verrons comment employer le détecteur de rotation (un type de détecteur qui n’est pas fournit avec
++
le $,, mais qui peut être acheté séparément et qui est très utile) et nous verrons que quelques trucs et astuces pour employer plus de trois détecteurs et faire un détecteur de proximité. 2'
H
R
C
X-
ST
O
R
M
La commande , , ()que nous avons vu précédemment fait en réalité deux choses : elle fixe le type du détecteur et lui met le mode dans lequel le détecteur fonctionne. En fixant le mode et le type d'un détecteur séparément, vous pouvez contrôler le comportement du détecteur plus précisément, ce qui est utile pour des applications particulières. Le type du détecteur est mis avec la commande , , (H (). Il y a quatre types différents : SENSOR_TYPE_TOUCH, qui est le détecteur de contact, SENSOR_TYPE_LIGHT, qui est le détecteur de lumière, SENSOR_TYPE_TEMPERATURE, qui est le détecteur de température (ce type de détecteur ne fait pas partie du $,, mais peut être acheté séparément) et SENSOR_TYPE_ROTATION, qui est le détecteur de rotation (ne fait aussi pas partie du $,, mais est disponible séparément). Fixer le type du détecteur est en particulier important pour indiquer si le détecteur a besoin d’un niveau de puissance ou de sensibilité (comme par exemple pour la lumière du détecteur de lumière). Nous n’avons pas encore expérimenté l’utilisation d’un détecteur avec un type différent de ce qu’il est en réalité. Le mode du détecteur est mis avec la commande , , * (). Il y a huit modes différents. Le plus important est SENSOR_MODE_RAW. Dans ce mode, la valeur que vous obtenez en vérifiant le détecteur est un nombre entre 0 et 1023. C'est la valeur brut produite par le détecteur. Ce que cela signifie dépend du détecteur réel. Par exemple, pour un détecteur de contact, quand le détecteur n'est pas poussé la valeur est près de 1023. Quand il est entièrement poussé, elle est près de 50. Quand il est poussé partiellement les gammes de valeur varient entre 50 et 1000. Ainsi si vous mettez un détecteur de contact au mode brut vous pouvez en réalité découvrir si on le touche partiellement. Quand le détecteur est un détecteur de lumière, les gammes de valeur d'environ 300 (très clair) à 800 (très sombre). Cela donne une valeur beaucoup plus précise que l'utilisation la commande , , (). Le deuxième mode de détecteur est SENSOR_MODE_BOOL. Dans ce mode la valeur est 0 ou 1. Quand la valeur brute est d’environ 550 la valeur est = 0, autrement il est = 1. SENSOR_MODE_BOOL est le mode par défaut pour un détecteur de contact. Les modes SENSOR_MODE_CELSIUS et SENSOR_MODE_FAHRENHEIT sont utiles avec seulement des détecteurs de température et rendent la température la voie indiquée. SENSOR_MODE_PERCENT transforment la valeur brute dans une valeur entre 0 et 100. Chaque valeur brute de 400 ou plus bas est dressée la carte à 100 pour cent. Si la valeur brute arrive plus haut, le pourcentage va tendre vers 0. SENSOR_MODE_PERCENT est le mode de défaut pour un détecteur de lumière. SENSOR_MODE_ROTATION semble être utile seulement pour le détecteur de rotation (voir ci-dessous). Il y a deux autres modes intéressants : SENSOR_MODE_EDGE et SENSOR_MODE_PULSE. Ils comptent des transitions, qui sont des changements d'une valeur basse à une valeur haute et inversement. Par exemple, quand vous touchez un détecteur de contact cela cause une transition d’une valeur haute à une valeur basse brute. Quand vous le relachez vous obtenez une transition l'autre direction. Quand vous mettez le mode de détecteur à SENSOR_MODE_PULSE, seule les transitions de bas vers haut sont comptées. Donc chaque contact et relâchement du détecteur de contact compte pour un. Quand vous mettez le mode de détecteur à SENSOR_MODE_EDGE, les deux transitions sont comptées. Donc chaque contact et relâchement du détecteur de contact compte pour deux. Donc vous pouvez employer cela pour compter combien de fois un détecteur de contact est poussé. Ou vous pouvez l'employer dans une combinaison avec un détecteur de lumière pour compter combien de fois une lampe (puissante) est allumée et éteinte. Bien sûr, quand vous comptez des choses comme cela, vous devez être capables de remettre le compteur à 0. Pour cela vous employez la commande , (). Il initialise le compteur pour le(s) détecteur(s) indiqué.
+9
Regardez un exemple. Le programme suivant emploie un détecteur de contact pour diriger le robot. Connectez le détecteur de contact avec un long fil à l'entrée un. Si le contact du détecteur s’effectue rapidement deux fois le robot se déplace en avant. Si vous le touchez une fois, il arrête de se déplacer. task main() { SetSensorType(SENSOR_1,SENSOR_TYPE_TOUCH); SetSensorMode(SENSOR_1,SENSOR_MODE_PULSE); while(true) { ClearSensor(SENSOR_1); until (SENSOR_1 >0); Wait(100); if (SENSOR_1 == 1) {Off(OUT_A+OUT_C);} if (SENSOR_1 == 2) {OnFwd(OUT_A+OUT_C);} } }
E
M
Notez que nous d'abord mettons le type du détecteur et ensuite le mode. Il semble que c'est essentiel parce que le changement du type effectue aussi le mode. '
R
C
X-
ST
O
R
Le détecteur de rotation est un type de détecteur très utile qui ne fait pas partie malheureusement de la boite $,. Il peut être acheté séparément par internet. Le détecteur de rotation contient un trou par lequel vous pouvez mettre un axe. Le détecteur de rotation mesure l’angle de rotation de l'axe. Une pleine rotation de l'axe est 16 pas (ou-16 si vous le faites tourner dans l’autre sens). Un pas est donc égale à (360/16) 22,5 degrés. Les détecteurs de rotation sont très utiles pour faire faire des mouvements précis au robot. Vous pouvez le faire se déplacer selon l’angle exact que vous voulez. Si vous avez besoin du contrôle plus précis que 16 pas, vous pouvez toujours employer des engrenages pour le connecter à l’axe. L’engrenage qui peut nous être utile ici est l’engrenage avec 24 dents. L’effet démultiplicateur de l’engrenage nous permet d’obtenir 24*16 soit 384 pas pour une rotation soit à peu près un degré pour un pas. Cela peu être utile pour la triangulation, mais il est encore trop tôt pour en parler. Une application standard doit avoir deux détecteurs de rotation connectés aux deux roues du robot que vous contrôlez avec les deux moteurs. Pour un mouvement droit vous voulez que les deux roues tournent à la même vitesse. Malheureusement, les moteurs ne tournent pas exactement à la même vitesse. En employant les détecteurs de rotation vous pouvez voir qu'une roue tourne plus rapidement que l’autre. Vous pouvez alors temporairement arrêter ce moteur (le mieux avec l'utilisation de Float( )) en attendant que les deux détecteurs donnent la même valeur de nouveau. Le programme suivant fait cela. Il a fait simplement se déplacer en ligne droite le robot. Pour l’utiliser, modifier votre robot en connectant les deux détecteurs de rotation aux deux roues. Branchez les détecteurs à l'entrée 1 et 3.
+5
task main() { SetSensor(SENSOR_1,SENSOR_ROTATION); ClearSensor(SENSOR_1); SetSensor(SENSOR_3,SENSOR_ROTATION); ClearSensor(SENSOR_3); while (true) { if (SENSOR_1 < SENSOR_3) {OnFwd(OUT_A); Float(OUT_C);} else if (SENSOR_1 > SENSOR_3) {OnFwd(OUT_C); Float(OUT_A);} else {OnFwd(OUT_A+OUT_C);} }
M
}
O
'
R
Le programme indique d'abord que les deux détecteurs sont des détecteurs de rotation et remet les valeurs à zéro. Ensuite il commence une boucle infinie. Dans la boucle nous vérifions si les deux lectures de détecteur sont égales. S'elles le sont le robot se déplace simplement en avant. Si on a une différence, le moteur droit est arrêté jusqu’à ce que les deux lectures ne soient de nouveau égales. C'est un programme très simple. Vous pouvez prolonger l’expérience pour faire se diriger le robot sur des distances exactes, ou le laisser faire des virages très précis.
X-
ST
Le : a seulement trois entrées donc vous ne pouvez y connecter seulement trois détecteurs. Quand vous voulez faire des robots plus compliqués (et que vous avez acheté quelques détecteurs supplémentaires) cela pourrait ne pas être assez pour vous. Heureusement, avec quelques astuces, vous pouvez connecter deux (ou même plus) détecteurs à une entrée. Le plus facile est de connecter deux détecteurs de contact à une entrée. Si un d'entre eux (ou les deux) est touché, la valeur est 1, autrement elle est 0. Vous ne pouvez pas distinguer les deux mais parfois ce n'est pas nécessaire. Par exemple, quand vous mettez un détecteur de contact au front et un à l'arrière du robot, vous savez lequel on touche basé sur la direction dans laquelle le robot roule. Mais vous pouvez aussi mettre le mode de l'entrée en mode brut (voir ci-dessus : SENSOR_MODE_RAW). Maintenant vous pouvez obtenir beaucoup plus d'information. Si vous avez de la chance, la valeur quand le détecteur est appuyé n'est pas le même pour les deux détecteurs. Si c'est le cas vous pourrez en réalité faire la distinction entre les deux détecteurs. Et quand les deux sont appuyés vous obtenez une valeur beaucoup plus basse (autour de 30) donc vous pouvez aussi détecter cela. Vous pouvez aussi connecter un détecteur de contact et un détecteur de lumière à une entrée. Fixez le type à SENSOR_TYPE_LIGHT (autrement le détecteur de lumière ne fonctionnera pas). Mettez le mode brut (raw). Dans ce cas, quand le détecteur de contact est poussé vous obtenez une valeur brute inférieur à 100. S'il n'est pas poussé vous obtenez la valeur du détecteur de lumière qui n'est jamais inférieur à 100. Le programme suivant emploie cette idée. Le robot doit être équipé d'un détecteur de lumière dirigé en bas et un butoir au front connecté à un détecteur de contact. Joignez les tous les deux à l'entrée 1. Le robot se déplacera aléatoirement dans un secteur éclairé. Quand le détecteur de lumière voit une ligne sombre (la valeur brute > 750) il recule un peu. Quand le détecteur de contact touche quelque chose (la valeur brute ci-dessous 100) il fait de même. Voici le programme :
C R
'
+,
int ttt,tt2; task mvtaleatoire() { while (true) { ttt = Random(50) + 40; tt2 = Random(1); if (tt2 > 0) { OnRev(OUT_A); OnFwd(OUT_C); Wait(ttt); } else { OnRev(OUT_C); OnFwd(OUT_A);Wait(ttt); } ttt = Random(150) + 50; OnFwd(OUT_A+OUT_C);Wait(ttt); } }
O
R
M
task main() { start mvtaleatoire; SetSensorType(SENSOR_1,SENSOR_TYPE_LIGHT); SetSensorMode(SENSOR_1,SENSOR_MODE_RAW); while (true) { if ((SENSOR_1 < 100) || (SENSOR_1 > 750)) { stop mvtaleatoire; OnRev(OUT_A+OUT_C);Wait(30); start mvtaleatoire; } } }
X-
ST
J'espère que le programme est clair. Il y a deux tâches. La tâche mvtaleatoire fait le mouvement de robot autour d'une façon aléatoire. La tâche principale mvtaleatoire commence d'abord, positionne le détecteur et attend ensuite que quelque chose arrive. Si la lecture de détecteur obtient une valeur trop basse (le contact) ou trop haute (hors du secteur blanc) il arrête les mouvements aléatoires, recule un peu et recommence ses mouvements aléatoires. Il est aussi possible de connecter deux détecteurs de lumière à la même entrée. La valeur brute est de quelque façon liée à la quantité combinée de lumière reçue par les deux détecteurs. Mais c'est plutôt peu clair et semble difficile à employer. La connexion d'autres détecteurs comme la rotation ou des détecteurs de température semble ne pas être utile. 6
?
'
'
R
C
En employant des détecteurs de contact, votre robot peut réagir quand il frappe quelque chose. Mais il serait beaucoup plus pratique si le robot pouvait réagir juste avant qu’il ne frappe quelque chose. Il doit savoir qu'il est près de quelque obstacle. Malheureusement il n'y a aucun détecteur de ce type disponible pour le moment. Il y a bien un truc qui puisse nous permettre cela. Le robot a un port infrarouge avec lequel il peut communiquer avec l'ordinateur, ou avec d'autres robots. (Nous verrons plus tard la communication entre des robots dans le Chapitre XI.) Il s'avère que le détecteur de lumière qui vient avec le robot est très sensible à la lumière infrarouge. Nous pouvons construire un détecteur de proximité basé sur cela. L'idée consiste en ce qu'une des tâches envoie des messages infrarouges. Une autre tâche mesure les fluctuations dans l'intensité de lumière qui est reflétée par les objets. Plus haut est la fluctuation, plus près nous sommes d’un objet. Pour employer cette idée, placez le détecteur de lumière au-dessus du port infrarouge sur le robot, pointant en avant. De cette façon il mesure seulement la lumière infrarouge reflétée.
+B
Joignez-le à l'entrée 2. Nous employons le mode brut pour le détecteur de lumière pour voir les fluctuations le mieux possible. Voici un programme simple qui laisse le robot se diriger en avant, avant qu'il n'arrive près d'un objet et fasse ensuite un virage à 90 degrés à droite. int derniernivo; niveau précédent
// Pour stocker le
task envoi_signal() { while(true) {SendMessage(0); Wait(10);} }
M
task verif_signal() { while(true) { derniernivo = SENSOR_2; if(SENSOR_2 > derniernivo + 200) {OnRev(OUT_C); Wait(85); OnFwd(OUT_A+OUT_C);} } }
O
R
task main() { SetSensorType(SENSOR_2, SENSOR_TYPE_LIGHT); SetSensorMode(SENSOR_2, SENSOR_MODE_RAW); OnFwd(OUT_A+OUT_C); start envoi_signal; start verif_signal; }
R
C
X-
ST
La tâche envoi_signal fait sortir 10 signaux $ chaque secondes, en employant la commande , * (0). La tâche verif_signal sauvegarde à plusieurs reprises la valeur du détecteur de lumière. Alors il vérifie (légèrement plus tard) si elle est devenu au moins > 200 plus haut, indiquant une grande fluctuation. S'il en est ainsi il laisse le robot faire un virage à 90 degrés à droite. La valeur de 200 est plutôt arbitraire. Si vous la faites plus petite, les virages du robot se feront plus loin des obstacles. Si vous la faites plus grande il le fera tout près d'eux. Mais cela dépend aussi du type de matériel et la quantité de lumière disponible dans la pièce. Vous devez expérimenter ou utiliser un outil de mesure de la lumière pour trouver la valeur correcte. Un inconvénient de cette technique est qu'il travaille seulement dans une direction. Vous avez probablement toujours besoin de détecteurs de contact sur les côtés pour éviter des collisions. Mais cette technique est très utile pour les robots qui doivent se déplacer dans des labyrinthes. Un autre inconvénient est que vous ne pouvez plus communiquer de l'ordinateur au robot parce qu'il se heurtera aux commandes infrarouges qui servent au mesure du robot. (Aussi la télécommande de votre télévision ne pourrait pas fonctionner.) ;'
'
Dans ce chapitre nous avons vu quelques questions complémentaires sur les détecteurs. Nous avons vu comment mettre séparément le type et le mode d'un détecteur et comment cela pourrait être employé pour obtenir des compléments d'information. Nous avons appris comment employer le détecteur de rotation. Et nous avons vu comment de multiples détecteurs peuvent être connectés à une entrée du :. Finalement, nous avons vu une astuce pour employer la connexion infra rouge du robot dans une combinaison avec un détecteur de lumière pour créer un détecteur de proximité. Tous ces trucs sont extrêmement utiles pour construire des robots plus compliqués. Les détecteurs jouent toujours un rôle crucial à ce moment là.
+.
%3 Comme nous l’avons indiqué auparavant, les tâches dans sont exécutées simultanément, ou en parallèle comme disent les gens d'habitude . C'est extrêmement utile. Une tâche vous permet d'observer des détecteurs tandis qu'une autre tâche se charge du déplacement du robot et encore une autre tâche vous joue de la musique. Mais des tâches parallèles peuvent aussi causer des problèmes. Une tâche peut se interférer avec une autre. #
-
Examinez le programme suivant. Ici une tâche déplace le robot autour de carrés (comme nous l’avons fait si souvent auparavant) et la deuxième tâche contrôle le détecteur de contact. Quand on touche le détecteur, le robot se déplace un peu en arrière et fait un virage à 90 degrés. task main()
M
{ SetSensor(SENSOR_1,SENSOR_TOUCH); start check_sensors;
R
while (true) {
} }
O
OnFwd(OUT_A+OUT_C); Wait(100); OnRev(OUT_C); Wait(85);
X-
ST
task check_sensors() { while (true) { if (SENSOR_1 == 1) { OnRev(OUT_A+OUT_C); Wait(50); OnFwd(OUT_A); Wait(85); OnFwd(OUT_C); } } } }
R
C
Cela ressemble probablement à un programme parfaitement valable. Mais si vous l'exécutez vous trouverez très probablement quelque comportement inattendu. Essayez de faire la chose suivante : Faites toucher au robot quelque chose tandis qu'il tourne. Il commencera à retourner, mais se déplacera immédiatement en avant de nouveau, frappant l'obstacle. La raison en est que les tâches peuvent s’interférer. La chose suivante arrive. Le robot tourne à droite, c'est-à-dire la première tâche est dans sa deuxième déclaration d’attente. Maintenant le robot frappe le détecteur. Il commence à aller en arrière, mais à ce moment-là, la tâche principale est prête avec l’attente et déplace le robot en avant de nouveau; dans l'obstacle. La deuxième tâche est en attente à ce moment donc il ne remarquera pas la collision. Ce n'est pas tout à fait le comportement que nous voudrions voir. Le problème consiste en ce que, tandis que la deuxième tâche attend nous ne nous sommes pas rendus compte que la première tâche tournait toujours , et que ses actions se heurtent aux actions de la deuxième tâche.
+
=
3
Une façon de résoudre ce problème est de s'assurer qu'à tout moment seulement une tâche dirige le robot. C'était l'approche prise dans le Chapitre VI. Voici le programme corrigés. task main() { SetSensor(SENSOR_1,SENSOR_TOUCH); start check_sensors; start move_square; }
M
task move_square() { while (true) { OnFwd(OUT_A+OUT_C); Wait(100); OnRev(OUT_C); Wait(85); } }
ST
O
R
task check_sensors() { while (true) { if (SENSOR_1 == 1) { stop move_square; OnRev(OUT_A+OUT_C); Wait(50); OnFwd(OUT_A); Wait(85); start move_square; } } }
X-
le problème est que la tâche check_sensors déplace seulement le robot après l'arrêt de la tâche move_square. Donc cette tâche ne peut pas se heurter le s'éloigné de l'obstacle. Une fois que la procédure d’évitement est finie, il recommence move_square. Bien que ce soit une bonne solution pour notre problème, il reste quand même un problème. Quand nous reprenons move_square, il commence de nouveau au commencement. C'est excellent pour notre petite tâche, mais souvent ce n'est pas le comportement exigé. Nous préférerions arrêter la tâche où elle est et la continuer plus tard de ce point. Malheureusement cela ne peut pas être fait facilement. #
'
R
C
Une technique standard pour résoudre ce problème est d’utiliser une variable pour indiquer quelle tâche contrôle les moteurs. On ne permet pas aux autres tâches de contrôler les moteurs avant que la première tâche n'indique, employant la variable, que c'est prêt. Une telle variable est souvent appelée un sémaphore. Laissez être un tel sémaphore. Nous supposons qu'une valeur de 0 indique qu'aucune tâche ne contrôle les moteurs. Maintenant, chaque fois qu'une tâche veut faire quelque chose avec les moteurs il exécute les commandes suivantes : until (sem == 0); sem = 1; // Faire quelque chose avec les moteurs sem = 0;
Donc d'abord nous attendons que personne n'ait besoin des moteurs. Alors nous prenons le à 1. Maintenant nous pouvons contrôler les moteurs. Quand cela est contrôle en mettant
+D
faits nous mettons de nouveau à 0. Ici vous trouverez le programme ci-dessus, l'utilisation mise en oeuvre une sémaphore. Quand le détecteur de contact touche quelque chose, le sémaphore est mis et la procédure de contournement est exécutée. Pendant cette procédure la tâche move_square doit attendre. À ce moment le contournement est fait, le sémaphore est mis à 0 et move_square peut continuer. int sem;
R
M
task main() { sem = 0; start move_square; SetSensor(SENSOR_1,SENSOR_TOUCH); while (true) { if (SENSOR_1 == 1) { until (sem == 0); sem = 1; OnRev(OUT_A+OUT_C); Wait(50); OnFwd(OUT_A); Wait(85); sem = 0; } } }
ST
O
task move_square() { while (true) { until (sem == 0); sem = 1; OnFwd(OUT_A+OUT_C); sem = 0; Wait(100); until (sem == 0); sem = 1; OnRev(OUT_C); sem = 0; Wait(85); } }
R
C
X-
Vous pourriez soutenir que ce n'est pas nécessaire dans move_square de mettre le sémaphore de 1 à 0. C’est toujours utile. La raison est que la commande # K ()est en fait deux commandes (voir le Chapitre VIII). Vous ne voulez pas que cet ordre de commande soit interrompu par une autre tâche. Les sémaphores sont très utiles et, quand vous écrivez des programmes compliqués avec des tâches parallèles, ils sont presque toujours nécessaire. (Il y a toujours une légère chance qu'ils pourraient laisser échouer. Essayez de devinez pourquoi. ?) ;'
'
Dans ce chapitre nous avons étudié certains des problèmes qui peuvent arriver quand vous employez des tâches différentes. Soyez toujours très prudent à cause des effets secondaires. Beaucoup de comportement inattendu sont dû à cela. Nous avons vu deux façons différentes de résoudre de tels problèmes. La première solution est d'arrêter et de reprendre les tâches et de s'assurer que seulement une tâche critique tourne à chaque moment. La deuxième approche utilise des sémaphores pour contrôler l'exécution des tâches. Cela garantit qu'à chaque moment seulement la partie critique d'une tâche est exécutée.
91
8 Si vous possédez plus d'un : ce chapitre est pour vous. Les robots peuvent communiquer de l'un à l'autre par le port infrarouge. vous pouvez avoir besoin de cela pour faire collaborer de multiples robots (ou les faire combattre l'un avec l'autre). Vous pouvez aussi construire un grand robot en employant deux :, de tel façon que vous pouvez avoir six moteurs et six détecteurs (ou même plus avec l'utilisation des trucs du Chapitre IX). Communication entre robots fonctionne généralement, comme suit. Un robot peut utiliser la commande , * () pour envoyer une valeur (0-255) sur le port infrarouge. Tous les autres robots reçoivent ce message et le stockent. Le programme du robot peut demander la valeur du dernier message reçu en utilisant Message(). A partir de cette valeur le programme peut faire exécuter au robot certaines actions. 2
O
R
M
Souvent, quand vous avez deux robots ou plus, l’un d’eux sera le leader. Nous l'appelons le maître. Les autres robots seront des esclaves. Le robot maître envoie des ordres aux esclaves et les esclaves exécutent ceux-ci. Parfois les esclaves pourraient envoyer l'information en retour au maître, par exemple la valeur d'un détecteur. Donc vous devez écrire deux programmes, un pour le maître et un pour l'esclave . Dorénavant nous supposons que nous avons juste un esclave. Laissez-nous commencer par un exemple très simple. Ici l'esclave peut exécuter trois ordres différents : déplacement en avant, le mouvement en arrière et l'arrêt. Son programme consiste en une boucle simple. Dans cette boucle il met la valeur du message actuel à 0 avec l’utilisation de la commande * (Q. Ensuite il attend que le message devienne différent de 0. en fonction de la valeur du message il exécute un des trois ordres. Voici le programme. task main()
while (true) { ClearMessage(); until (Message() if (Message() == if (Message() == if (Message() == }
ST
{
// ESCLAVE
!= 1) 2) 3)
0); {OnFwd(OUT_A+OUT_C);} {OnRev(OUT_A+OUT_C);} {Off(OUT_A+OUT_C);}
X-
}
Le maître a un programme beaucoup plus simple. Il envoie simplement les messages correspondant aux ordres et attend ensuite. Dans le programme ci-dessous il commande à l'esclave de se déplacer en avant, puis, après deux secondes, d’aller en arrière et ensuite, de nouveau après deux secondes, de s'arrêter.
R
C
task main()
// MAÎTRE
{ SendMessage(1); Wait(200); SendMessage(2); Wait(200); SendMessage(3); }
Après que vous ayez écrit ces deux programme, vous devez les télécharger aux robots. Chaque programme doit aller à un des deux robots. Assurez-vous que vous avez arrêté l'autre en attendant (voir aussi les avertissements ci-dessous). Allumez maintenant les deux robots et démarrez les programmes : d'abord celui de l'esclave et ensuite celui du maître. Si vous avez des esclaves multiples, vous devez télécharger le programme esclave à chacun d'entre eux séparément (et non simultanément; voir ci-dessous). Maintenant tous les esclaves exécuteront exactement les mêmes actions.
9)
Pour permettre aux robots de communiquer l'un avec l'autre nous avons défini un protocole : Nous avons décidé que 1 signifie « se déplacer en avant », 2 pour « se déplacer en arrière » et 3 pour « s’arrêter ». Il est très important de définir clairement de tels protocoles, en particulier quand vous traitez plusieurs communications. Par exemple, quand il y a plus d'un esclave, vous pourriez définir un protocole dans lequel deux numéros sont envoyé (avec une petite temporisation au milieu) : le premier numéro est le numéro de l'esclave et le deuxième est l'ordre réel. L'esclave contrôle que le premier numéro et exécute seulement l'action si c'est son numéro. (Cela exige que chaque esclave ait son propre numéro, ce qui peut être réalisé en laissant chaque esclave avoir un programme légèrement différent dans lequel par exemple une constante sera différente.) A
?
R
C
X-
ST
O
R
M
Comme nous l’avons vu précédemment, qu’en utilisant des robots multiples, chaque robot doit avoir son propre programme. Il serait beaucoup plus facile si nous pouvions télécharger juste un programme à tous les robots. Mais alors la question est : qui sera le maître ? La réponse est facile : laissez les robots décider !. Laissez-les élire un leader que les autres suivront. Mais comment faisons-nous cela ? L'idée est plutôt simple. Nous laissons chaque robot attendre un temps aléatoire et envoyer ensuite un message. Celui qui envoie un message en premier sera le leader. Cet « élection » pourrait échouer si deux robots attendent exactement le même temps pour envoyer leur message en même temps mais c'est plutôt peu probable. (Vous pouvez construire des arrangements plus compliqués qui détectent cela et essayent une deuxième élection dans un tel cas.) Voici le programme qui le fait :
9+
task main() { ClearMessage(); Wait(200); // s'assurent que tous les robots sont branchés Wait(Random(400)); et 4 secondes
// attendent entre 0
if (Message() > 0) était premier
// quelqu'un d'autre
{
start slave; } else { SendMessage(1); maintenant
M
// je suis le maître
Wait(400); que chacun sait
// sinon s'assurent
R
start master; } }
O
task master() { SendMessage(1); Wait(200); SendMessage(2); Wait(200); SendMessage(3); }
X-
ST
task slave() { while (true) { ClearMessage(); until (Message() if (Message() == if (Message() == if (Message() == } }
!= 1) 2) 3)
0); {OnFwd(OUT_A+OUT_C);} {OnRev(OUT_A+OUT_C);} {Off(OUT_A+OUT_C);}
R
C
Téléchargez ce programme à tous les robots (un à un, non tous à la fois; voir ci-dessous). démarrez les robots au même moment et regardez ce qui arrive. L’un d'entre eux doit prendre les commande et les autres doivent suivre les ordres. Dans de rares occasions, aucun d'eux ne devient le leader. Comme indiqué ci-dessus, cela exige que des protocoles plus prudents résolvent ce cas. =-
Vous devez être un peu prudents quand traitant avec des robots multiples. Il y a deux problèmes : si deux robots (ou un robot et l'ordinateur) envoient l'information en même temps cela pourrait être perdu. Le deuxième problème consiste en ce que, quand l'ordinateur envoie un programme aux robots multiples en même temps, cela cause des problèmes. Commençons par le deuxième problème. Quand vous téléchargez un programme au robot, le robot dit à l'ordinateur s'il reçoit correctement (les parties du ou ) le programme. L'ordinateur y réagit en envoyant de nouveaux « blocs » ou en renvoyant les blocs de
99
do { SendMessage(1); ClearMessage(); Wait(10);
R
} while (Message() != 255);
M
données. Quand deux robots sont branchés, tous les deux commenceront à dire l'ordinateur s'ils reçoivent correctement le programme. L'ordinateur ne comprendra pas cette double réponse (il ne sait pas qu'il y a deux robots!). En conséquence, les choses mal tournent et le programme est corrompu. Les robots ne feront pas les choses justes. Assurez-vous toujours que, tandis que vous téléchargez des programmes, seulement un robot est allumé! L'autre problème est que seul un robot peut envoyer un message à tout moment. Si deux messages sont envoyé au même moment, ils pourraient être perdu. Aussi, un robot ne peut pas à la fois envoyer et recevoir des messages. Aucun problème quand un robot seulement envoie des messages (il y a seulement un maître) mais autrement cela pourrait être un sérieux problème. Par exemple, vous pouvez imaginer écrire un programme dans lequel un esclave envoie un message quand il se heurte dans quelque chose, tel que le maître peut prendre des mesures. Mais si le maître envoie un ordre en même temps, le message se perdra. Pour résoudre cela, il est important de définir votre protocole de communication tel que, dans le cas où une communication échoue, ce sera corrigé. Par exemple, quand le maître envoie une commande, il doit obtenir une réponse de l'esclave. Si il n'obtient pas de réponse assez tôt, il renvoie la commande. Cela aboutirait à un bloc de code qui ressemble à cela :
ST
O
Ici 255 est employé pour l’accusé de réception. Parfois, quand vous traitez avec des robots multiples, vous pourriez vouloir que seulement un robot qui est très près de l’émetteur reçoit le signal. Cela peut être réalisé en ajoutant la commande , (=3 K (TX_POWER_LO) au programme du maître. Dans ce cas le signal $ envoyé sera très bas et seule un robot qui sera près de la tour deviendra maître. C'est en particulier utile si on construit un robot plus grand avec deux :. Employez SetTxPower (TX_POWER_HI) pour remettre le robot dans le mode de transmission longue portée. ;'
'
C
X-
Dans ce chapitre nous avons étudié certains des aspects de base de la communication entre des robots. La communication emploie les commandes pour envoyer, purifier et vérifier des messages. Nous avons vu qu'est est important de définir un protocole pour comment la communication travaille. Tels protocoles jouent un rôle crucial dans n'importe quelle forme de communication entre des ordinateurs. Nous avons aussi vu qu'il y a quelques restrictions dans la communication entre les robots qui le font même plus important de définir de bons protocoles.
R
* a quelques commandes complémentaires. Dans ce chapitre nous en présenterons trois types : l'utilisation de minuteurs, la commande pour contrôler l’affichage et l'utilisation de du :.
Le : a quatre minuteurs incorporés. Ces minuteurs comptent par incréments de 1/10 de seconde. Les minuteurs sont numérotés de 0 à 3. Vous pouvez remettre à zero la valeur d'un minuteur avec la commande ( () et obtenir la valeur actuelle du minuteur avec Timer(). Voici un exemple de l'utilisation d'un minuteur. Le programme suivant laisse le robot divaguer de façon aléatoire pendant 20 secondes.
95
task main() { ClearTimer(0); do { OnFwd(OUT_A+OUT_C); Wait(Random(100)); OnRev(OUT_C); Wait(Random(100)); } while (Timer(0)<200); Off(OUT_A+OUT_C); }
R
M
Vous pourriez vouloir comparer ce programme avec celui donné dans le Chapitre IV qui fait exactement la même chose. Celui avec minuteurs est certainement plus simple. Les minuteurs sont très utiles pour remplacer la commande Wait (). Vous pouvez laisser « dormir » votre robot pour une durée donnée en remettant à zéro le minuteur et en attendant ensuite qu'il atteigne une valeur particulière. Mais vous pouvez aussi faire réagir votre robot sur d'autres événements (par exemple les détecteurs) en attendant. Le programme suivant est un exemple simple de cela. Il laisse le robot se diriger en avant pendant 10 secondes, ou bien jusqu’à ce que le détecteur de contact touche quelque chose.
O
task main() { SetSensor(SENSOR_1,SENSOR_TOUCH); ClearTimer(3); OnFwd(OUT_A+OUT_C); until ((SENSOR_1 == 1) || (Timer(3) >100));
ST
Off(OUT_A+OUT_C);
}
N'oubliez pas que le minuteur travaille par impulsion de 1/10 de seconde, tandis que ,par exemple, les utilisations des autres commandes s'attendent à des cycles de 1/100 de seconde. E? ((
R
C
X-
Il est possible de contrôler l'affichage du : de deux façons différentes. Tout d'abord, vous pouvez indiquer la restitution des informations suivantes : l'horloge de système, un des détecteurs, ou un les moteurs. C'est équivalent de l'utilisation du bouton de vue noir sur le :. Pour mettre le type d'affichage, employez la commande , H(). Le programme suivant montre toutes les sept possibilités, l'un après l'autre.
9,
task main() { SelectDisplay(DISPLAY_SENSOR_1); Wait(100); // Entrée 1 SelectDisplay(DISPLAY_SENSOR_2); Wait(100); // Entrée 2 SelectDisplay(DISPLAY_SENSOR_3); Wait(100); // Entrée 3 Wait(100);
SelectDisplay(DISPLAY_OUT_B); // Port de sortie B
Wait(100);
SelectDisplay(DISPLAY_OUT_C); // Port de sortie C
Wait(100);
SelectDisplay(DISPLAY_WATCH); // horloge du Système
Wait(100);
M
SelectDisplay(DISPLAY_OUT_A); // Port de sortie A
R
}
O
Notez que vous ne devez pas employer , H(SENSOR_1). La deuxième façon pour vous de contrôler l'affichage est de fixer la valeur de l'horloge système. Vous pouvez employer cela pour afficher une information de diagnostic par exemple. Pour cela utiliser la commande , N & (). Voici un programme minuscule qui emploie cela :
ST
task main() { SetWatch(1,1); Wait(100); SetWatch(2,4); Wait(100); SetWatch(3,9); Wait(100); SetWatch(4,16); Wait(100); SetWatch(5,25); Wait(100); }
X-
Notez que les arguments de , N
&() doivent être
.
2
R
C
Le : peut stocker des valeurs de variables, des lectures de détecteur et de minuteurs, dans un emplacement de sa mémoire appelée le . Les valeurs du ne peuvent pas être employées à l'intérieur du :, mais ils peuvent être lus par votre ordinateur. C'est utile de vérifier par exemple ce qui se passe dans votre robot. le Centre de Commande : a une fenêtre spéciale dans laquelle vous pouvez voir le contenu actuel du . L'utilisation du consiste en trois étapes : D'abord, le programme doit définir la taille du , en employant la commande (). Cela remet à zero aussi le contenu actuel du . Ensuite, les valeurs peuvent être écrites dans le avec l'utilisation de la commande AddToDatalog (). Les valeurs seront écrites l'une après l'autre. (Si vous regardez l'affichage du : vous verrez que l'une après l'autre, quatre parties d'un « disque » apparaissent. Quand le disque est complet, le est plein.) si la fin du est atteinte, rien arrive. Les nouvelles valeurs ne sont plus stockées. La troisième étape doit télécharger le au PC. Pour cela, choisissez dans le Centre de Commande : la commande Datalog dans le menu Outils. Ensuite appuyez le bouton
9B
étiqueté Télécharger " et toutes les valeurs apparaissent. Vous pouvez les observer ou les sauvegarder dans un fichier pour en faire quelque chose plus tard. Certains ont employé cette particularité pour faire un scanner avec le : par exemple. Voici un exemple simple d'un robot avec un détecteur de lumière. Le robot roule pendant 10 secondes et cinq fois par seconde la valeur du détecteur de lumière est écrite dans le .
R
C
X-
ST
O
R
M
task main() { SetSensor(SENSOR_2,SENSOR_LIGHT); OnFwd(OUT_A+OUT_C); CreateDatalog(50); repeat (50) { AddToDatalog(SENSOR_2); Wait(20); } Off(OUT_A+OUT_C); }
9.