• 沒有找到結果。

多传感器智能机器人

多传感器智能机器人的设计目标

通过前面的学习,你对“导航”已经不陌生了:传感器(触觉或红外线)检测到信息,

传送给机器人大脑——微控制器 AT89S52,决策后发送命令给执行器——电机,使机器人能 够正常行走。

但不管触觉导航也好,红外线导航也好,机器人大脑分析的信息都是单一传感器信息。

在实际的智能机器人系统中,通常不只有一种传感器,而是有多种传感器来检测各种环境信 息,如:用触觉检测是否有物体已经碰到机器人,而红外线传感器则能检测不远的前方是否 有障碍物,而激光传感器则可以检测更远的地方是否有障碍物等。当然,最复杂的机器人传 感器是视觉传感器,这不是本课程讨论的内容。

本章将前面已经学习和实践过的触觉和红外传感器结合——设计一款多传感器智能机 器人,使它能够结合传感器检测到的信息进行综合判断,执行理想的行走方案。如果有更多 的机器人传感器,可以参考本章的处理方法。

任务一 多传感器信息与C语言结构体的使用和编程

在前几章的学习中,你想要显示或存储的一些信息,如“int counter”、“Program Running!”“char Navigation[10]={'F','L','F','F','R','B','L','B','B','Q'};”、

“int Pulses_Left[4]={1700,1300,1700,1300};”等,用了大量不同类型的变量。有没有 可能把这些不同类型的变量全部放在一个变量里呢?在这里,你将学到新的数据类型:

结构体

结构体可以将不同类型的变量放到一起,组成一个复合的复杂变量,以表示某些工程对 象或者系统的多元特征。

在实际问题中,一些对象或者系统的特征往往具有不同的数据类型,而编写程序时,你 肯定希望能够把同一个对象或者系统的特征放到一个数据变量中,以便于阅读、分析和检查。

例如,在学生登记表中,描述一个学生的特征包括姓名、学号、年龄、性别和成绩等,你肯 定希望有一个数据结构能够包括所有这些特性,但这些特征中姓名应为字符型;学号可为整 型或字符型;年龄应为整型;性别应为字符型;成绩可为整型或实型。显然你不能用一个数 组或者其它你已经学习过的数据类型来存放这些数据。前面学过的数组可以存放多个数据,

但这些数据必须是同一个类型。

为了解决这个问题,C语言给出了一种构造数据类型——“结构(structure)”或叫“结 构体”。“结构”是一种构造类型,它是由若干“成员”组成的。结构的每一个成员可以是一 个基本数据类型或者是另一个已经定义好的构造类型。结构既然是一种“构造”而成的数据 类型,那么在说明和使用之前必须先定义它,也就是构造它。如同在说明和调用函数之前要 先定义函数一样。

定义一个结构的一般形式为:

struct 结构名 {成员列表};

成员列表由若干个成员组成,每个成员都是该结构的一个组成部分。对每个成员也必须

作类型说明,其形式为: 变量;第二个成员为name,字符数组;第三个成员为 sex,字符变量;第四个成员为 score,

实型变量。应注意在大括号后的分号是不可少的。

结构定义之后,即可进行变量说明。凡说明为结构stu 的变量都由上述 4 个成员组成。

由此可见,结构是一种复杂的数据类型,是数目固定,类型不同的若干有序变量的集合。

说明结构变量有以下三种方法:

1.先定义结构,再说明结构变量,如:

struct stu {

int num;

char name[20];

char sex;

float score;

};

struct stu boy1,boy2;

说明了两个变量boy1 和 boy2 为 stu 结构类型。

2.在定义结构类型的同时说明结构变量,如:

struct stu {

int num;

char name[20];

char sex;

float score;

}boy1,boy2;

char name[20];

char sex;

float score;

}boy1,boy2;

这种说明形式的一般形式为:

struct {

成员列表 }变量名列表;

第三种方法与第二种方法的区别在于第三种方法中省去了结构名,而直接给出结构变 量。

三种方法中说明的boy1,boy2 变量都具有图 9-1 所示的结构。

9-1 结构变量 boy1,boy2 结构

说明了boy1,boy2 变量为 stu 类型后,即可向这两个变量中的各个成员赋值。在上述 stu 结构定义中,所有的成员都是基本数据类型或数组类型。

成员也可以又是一个结构,即构成了嵌套的结构。例如,图9-2 给出了另一个数据结构。

9-2 嵌套式数据结构

结构变量成员的表示方法

在程序中使用结构变量时,往往不把它作为一个整体来使用。一般对结构变量的使用,

包括赋值、输入、输出、运算等都是通过结构变量的成员来实现的。

表示结构变量成员引用的一般形式是:

结构变量名.成员名 例如:

boy1.num 即第一个人的学号 boy2.sex 即第二个人的性别

如果成员本身又是一个结构,则必须逐级找到最低级的成员才能使用。

例如:

boy1.birthday.month 即第一个人出生的月份成员

结构变量的成员可以在程序中单独使用,与普通变量完全相同。

结构变量的赋值

结构变量的赋值就是给各成员赋值。可用输入语句来完成。如:

boy1.num=102;

boy2.sex=’M’;

结构变量的初始化

和其他类型变量一样,对结构变量可以在定义时进行初始化赋值。如:

struct stu

{

int num;

char *name;

char sex;

float score;

}boy2,boy1={102,"Zhang ping",'M',78.5};

掌握结构的基本知识之后,下面的例程你将不难理解。

例程:IRRoamingWithStructLCDDisplay.c

#include <AT89X52.h>

#include <BoeBot.h>

#include <IR.h>

#include <Move.h>

#include <LCD.h>

int main(void) {

Display_List_Char(0,0,"Both IR Detected");

Display_List_Char(1,0,"Turn Left Twice ");

delay_nms(500);

}

else if(irDetectLeft==0)//只有左边接收到红外线 {

Right_Turn();

Display_List_Char(0,0,"L IR Detected ");

Display_List_Char(1,0,"Turn Right Once ");

delay_nms(500);

}

else if(irDetectRight==0)//只有右边接收到红外线 {

Left_Turn();

Display_List_Char(0,0,"R IR Detected ");

Display_List_Char(1,0,"Turn Left Once ");

delay_nms(500);

} else

{

For_Ward();

Display_List_Char(0,0,"No IR Detected ");

Display_List_Char(1,0,"Forward Directly");

delay_nms(500);

} } }

IRRoamingWithWithStructLCDDisplay.c 是如何工作的?

与第五章例程RoamingWithIr.c 相比,该例程将添加了多个头文件。其中头文件 LCD.h 与第八章所用一样,用来在LCD 上显示机器人相关信息,具体内容见上章。

头文件IR.h 整合了红外发射的相关函数,具体内容如下:

#include <intrins.h>

#define LeftIR P1_2 //左边红外接收连接到 P1_2

#define RightIR P3_5 //右边红外接收连接到 P3_5

#define LeftLaunch P1_3 //左边红外发射连接到 P1_3

#define RightLaunch P3_6 //右边红外发射连接到 P3_6 int irDetectLeft,irDetectRight;

int IRLaunch(unsigned char IR) {

int counter;

if(IR=='L')

for(counter=0;counter<1000;counter++)//左边发射 {

for(counter=0;counter<1000;counter++)//右边发射 {

void Launch(void) {

IRLaunch('R');

irDetectRight = RightIR;//右边接收 IRLaunch('L');

irDetectLeft = LeftIR;//左边接收 }

头文件 Move.h,顾名思义,保存机器人行走子函数,与以往例程不同的是,该头文件 保存时用到了结构体变量:

struct {

int pulseLeft;

int pulseRight;

char counter;

}Forward={1700,1300}, LeftTurn={1300,1300,26}, RightTurn={1700,1700,26},

Backward={1300,1700,26};

void For_Ward(void) {

void Left_Turn(void) {

void Right_Turn(void)

{

int i;

for(i=1;i<=RightTurn.counter;i++) {

P1_1=1;

delay_nus(RightTurn.pulseLeft);

P1_1=0;

P1_0=1;

delay_nus(RightTurn.pulseRight);

P1_0=0;

delay_nms(20);

} }

void Back_Ward(void) {

int i;

for(i=1;i<= Backward.counter;i++) {

P1_1=1;

delay_nus(Backward.pulseLeft);

P1_1=0;

P1_0=1;

delay_nus(Backward.pulseRight);

P1_0=0;

delay_nms(20);

} }

该头文件定义一个结构体的同时定义了四变量 Forward、LeftTurn、RightTurn 和 Backward,分别存储了机器人行走的相关信息:左轮脉冲数——int pulseLeft;右轮脉冲数—

—int pulseRight 和循环次数——char counter。

对结构变量成员的引用按照规则结构变量名.成员名,如:delay_nus(Backward.pulseLeft);

说明

头文件虽然定义了后退子函数,但由例程的行为控制策略可以看出,当机器人两边红外 传感器均检测到障碍物时,机器人左拐两次避开后再前进,并没有使用后退子函数。当然,

你可以改变行为控制策略。

下图9-3 显示机器人在前进及左拐时截图。

9-3 机器人前进及左拐图 该你了

z 用结构体及头文件的方式更改第四章例程 RoamingWithWhiskers.c z 使用后退子函数,更改机器人行为控制

任务二 智能机器人的行为控制策略和编程

本章的学习目的是基于多传感器信息的机器人导航。其实,通过前面几章的学习,你已 经分别掌握了基于单一传感器信息的机器人导航,本任务的目的是将触觉和红外传感器信息 进行综合判断处理,最后将传感器及导航信息在LCD 上显示。

z 按图 9-4 所示搭建机器人。触觉及红外电路分别参考图 4-3 和图 5-3

z 搭建 IR LED 时,可适当将红外 LED 向两边偏移,减少两只红外 LED 检测区域的重 叠

9-4 多传感器智能机器人

“优先级”这个概念你一定不会陌生。人们在工作和生活中往往要处理各种事务,这些 事务有轻重急缓之分,时间紧、迫在眉睫的事情要先处理,它们的优先级要高,等这些事情 处理完之后再处理其他事情,其他事情当中也是先处理优先级高的。如果一样重要,就按时 间的先后顺序完成。

在智能机器人里,引入了两个传感器——胡须及红外。那么它们的优先级谁高谁低呢?

你不妨想象一下:你是先处理手头上,近在眼前的事情,还是处理等会要做的事情?

同理,胡须和红外同时检测到了障碍物,机器人应该怎样做?胡须检测到的障碍物近在 眼前,红外检测到的障碍物还有一定的距离,理所当然先处理胡须事件,所以胡须的优先级 要比红外的优先级要高。两个传感器检测区域示意如图9-5。

9-5 传感器检测区域示意图

胡须传感器检测区域为A 和 B,检测距离虽然短,但优先级比红外传感器高。

红外传感器检测区域为C 和 D,呈喇叭形发散,检测距离远,范围广;区域 E 为两者 共同检测区;F 和 G 分别为两者的盲区。

根据传感器的优先级,可以方便地制作机器人行为控制策略。左右胡须状态分别由P2_3 和P1_4 获得;左右红外检测器状态由 P1_2 和 P3_5 获得。假如这四个状态值分别是某一变 量的高低四位,则根据这一变量的值就可判断机器人的状态,并做出相应行为策略,见下表:

9-1 传感器状态组合及行为控制 左胡须

P2_3

右胡须 P1_4

左红外 P1_2

右红外 P3_5

状态值

state 行为策略

1 1 1 1 15 前进

0 0 x x 0~3 后退左拐两次

0 1 x x 4~7 后退再右拐

1 0 x x 8~11 后退再左拐

1 1 0 0 12 右拐两次

1 1 0 1 13 右拐

1 1 1 0 14 左拐

注:x 表示 0 或 1 判断state 的大小就可以知道是哪个传感器检测到信息。

例程:NavigationWithSensors.c

#include <AT89X52.h>

#include <BoeBot.h>

#include <IR.h>

#include <Whisker.h>

#include <Move.h>

#include <LCD.h>

int main(void) {

case 15: Display_List_Char(0, 0, "No Sensor Detect");

Display_List_Char(1, 0, "Forward ");

case 7: Display_List_Char(0, 0, "L Whisker Detect");

Display_List_Char(1, 0, "Back and Right ");

case 10:

case 11: Display_List_Char(0, 0, "R Whisker Detect");

Display_List_Char(1, 0, "Back and Left ");

Back_Ward(); //右胡须检测到,后退再左拐 Left_Turn();

break;

case 12: Display_List_Char(0, 0, "Both IRs Detect ");

Display_List_Char(1, 0, "Turn Right Twice");

Right_Turn(); //两边红外均检测到,右拐两次 Right_Turn();

break;

case 13: Display_List_Char(0, 0, "L IR Detect ");

Display_List_Char(1, 0, "Turn Right ");

Right_Turn(); //左边红外检测到,右拐 break;

NavigationWithSensors.c 是如何工作的?

例程又多加了一个头文件Whisker.h 用于保存胡须状态,内容如下:

例程又多加了一个头文件Whisker.h 用于保存胡须状态,内容如下: