第 4 章 能力风暴编程
4.5 JC 程序的高级编程
}
这个例子实现了跟随前方物体。能力风暴可以跟随前方移动的人或物;如果撞上前方的 物体,就停一停;如果前方红外系统探测范围内没有物体,它就停下来。可以修改上面的程 序,让能力风暴避开障碍物。给一个能力风暴下载避让程序,其他的能力风暴下载跟踪程序,
在适当的条件下能看到,能力风暴一个跟着一个排成长龙,鱼贯前进。
光敏的使用和红外系统类似,所不同的是光敏只能感知左右两侧的明暗,和距离没有直 接关系。调用“photo(l); photo(2);”就能返回两侧光敏的测量值。
要用好能力风暴传感器,需要对测量、采样原理有所了解。通常在使用传感器测量之前 都有个标定过程,设置测量值的参考点。能力风暴在出厂前,已经对所有的传感器进行了检 测,但是器件偏差和环境干扰是不可避免的。比如可能会出现左右光敏对同样光强的测量值 不一样,或采样出现异常值。所以在编程中,使用一些偏移量校正、去除测量噪声和避免误 触发的方法还是很有用的。
4.5 JC 程序的高级编程
JC 支持多任务。多任务允许能力风暴同时做多件事情,如检测光源的同时避开障碍。
在 JC 中实现多任务是很简单的,只要调用 start_process(),任何一个函数都能作为一个 进程来运行。在 JC 中进程的内存空间不是独立的,所有的进程共用一个内存空间,因此全 局变量在任何进程里都可以访问。
4.5.1
第一个多进程程序
下面我们把第四节“台球”程序改成多进程的程序。
int bill_trans=0;
int bill_rot=0;
void billiards () {
int bmpr=0;
while(1) /*无限循环检测*/
{
bmpr=bumper(); /*检测碰撞传感器*/
if(bmpr!=0) {
if(bmpr==0b0011) /*正前方发生碰撞*/
{
bill_trans=-80; /*后退*/
bill_rot=0;
}
else if(bmpr==0b1100) /*正后方发生碰撞*/
{
bill_trans=80; /*前进*/
bill_rot=0;
}
else if(bmpr & 0b0101) /*左侧发生碰撞*/
{
bill_trans=0;
bill_rot=-80;
wait(0.5); /*顺时针转一个角度*/
bill_trans=80; /*前进*/
bill_rot=0;
}
else if(bmpr & 0b1010) /*右侧发生碰撞*/
{
bill_trans=0;
bill_rot=80;
wait(0.5); /*逆时针转一个角度*/
bill_trans=80; /*前进*/
bill_rot=0;
} } }
}
void billiards_drive() {
while(1)
drive(bill_trans,bill_rot); /*驱动电机*/
}
void main() {
start_process(billiards_drive()); /*创建电机驱动进程*/
start_process(billiards()); /*创建碰撞处理进程*/
}
“台球”改成多进程后,运行的效果没变,但结构已经完全不一样了。电机驱动进程 billiards_drive()专门设置电机速度,碰撞处理进程 billiards()判断碰撞并改变电机速 度。两个进程之间通过全局变量 bill_trans 和 bill_rot 进行通讯。能力风暴的操作系统自 动调度两个进程,给它们分配时间片。从执行效果来讲,就相当于这两个进程并列运行。这 样的结构非常方便增加新的进程,同时处理更多的外部信息。
4.5.2
添加一个新进程
我们给前面的程序增加一个红外避障进程,改变它的行为,而其它部分不需要做大的变 动。改动后的程序如下。
int bill_trans=0;
int bill_rot=0;
int bmpr=0;
int forward=0;
int running=0; /*能力风暴初始值处于静止状态*/
void billiards () {
while(1) /*无限循环检测*/
{
bmpr=bumper(); /*检测碰撞传感器*/
if(bmpr!=0)
{if(bmpr==0b0011) /*正前方发生碰撞*/
{forward=0;
bill_trans=-80; /*后退*/
bill_rot=0;
}
else if(bmpr==0b1100) /*正后方发生碰撞*/
{forward=1;
bill_trans=80; /*前进*/
bill_rot=0;
}
else if(bmpr & 0b0101) /*左侧发生碰撞*/
{bill_trans=0;
bill_rot=-80;
wait(0.5); /*顺时针转一个角度*/
forward=1;
bill_trans=80; /*前进*/
bill_rot=0;
}
else if(bmpr & 0b1010) /*右侧发生碰撞*/
{bill_trans=0;
bill_rot=80;
wait(0.5); /*逆时针转一个角度*/
forward=1;
bill_trans=80; /*前进*/
bill_rot=0;
}}
} }
void billiards_ir() {
int ir;
while(1) {
if(running) /*能力风暴没有开始运动,不检测障碍*/
{ ir=ir_detector(); /*检测红外传感器*/
if(bmpr==0 && forward) /*后退或发生碰撞时,不避障*/
{
if(ir==2) /*右侧有障碍,向左绕*/
{ bill_trans=20;
bill_rot=80; /*逆时针转*/
}
else if(ir==1) /*左侧有障碍,向右绕*/
{ bill_trans=20;
bill_rot=-80; /*顺时针转*/
}
else if(ir==0) /*前方没有障碍,恢复直行*/
{ bill_trans=80;
bill_rot=0;
}}
wait(0.1);
}}
}
void billiards_drive() {
while(1)
{ running = bill_trans; /*能力风暴正在运动*/
drive(bill_trans,bill_rot); /*驱动电机*/
} }
void main() {
start_process(billiards_drive()); /*创建电机驱动进程*/
start_process(billiards_ir()); /*创建避障进程*/
start_process(billiards()); /*创建碰撞处理进程*/
}
改动以后的程序增加了避开侧面障碍物的行为。比较一下,增加的代码除了红外避障进 程外,就是用于进程之间的通讯。进程间的通讯和同步是多进程编程的难点,不解决好这个 问题,多进程程序可能会产生一些奇怪的行为。在这个程序里,由于碰撞处理进程和红外避 障进程都要修改设置电机速度的两个全局变量(bill_trans,bill_rot),这是进程间发生冲 突的根源。如果不加以限制,两个进程同时修改电机速度,必然会出现一片混乱,能力风暴 下一步的运动方向将无法预知。
本程序中把 bmpr 改为全局变量,通过 bmpr 来划分两个进程生效的时间。即发生碰撞时,
只有碰撞处理进程可以修改电机速度;在其他时间里碰撞处理进程只是在不断检测碰撞传感 器,只有红外避障进程才有可能修改电机速度。我们可以看到,在本程序里碰撞处理进程的 优先级高于红外避障进程。增加的另两个全局变量是出于红外避障行为逻辑的需要。全局变 量 running 是能力风暴开始运动的标志,红外避障进程要等待这一事件发生后才能起作用。
forward 反映能力风暴当前运动方向,用于避免红外避障进程在后退时候处理前方障碍。