通过对单片机编程可以使机器人完成各种巡航动作。本章所要介绍的这些巡航动作和编 程技术在后面的章节都会用到。与后面章节唯一不同的是:本章机器人在无感觉的情况下巡 航,而在后面的章节中,机器人将根据传感器检测到的信息进行智能巡航。
本章所要完成的主要任务如下:
1.对单片机编程使机器人做一些基本巡航动作:向前,向后,左转,右转和原地旋转 2.编写程序使机器人由突然启动或停止变为逐步加速或减速运动
3.写一些执行基本巡航动作的函数,每一个函数都能够被多次调用 4.将复杂巡航运动记录在数组中,编写程序执行这些巡航运动
任务一 基本巡航动作
图3-1 定义了机器人的前后左右四个方向:当机器人向前走时,它将走向本页纸的右边;
当向后走时,会走向纸的左边;向左转会使其向纸的顶端移动;向右转它会朝着本页纸的底 端移动。
向前巡航
在前一章实际上你应该已经发现,如果按照图3-1 前进方向的定义,机器人向前走时,
从机器人的左边看,它向前走时轮子是逆时针旋转的;从右边看另一个轮子则是顺时针旋转 的。
回忆一下第二章的内容,发给单片机控制引脚的高电平持续时间决定了伺服电机旋转的 速度和方向。for 循环的参数控制了发送给电机的脉冲数量。由于每个脉冲的时间是相同的,
因而for 循环的参数也控制了伺服电机运行的时间。下面是使机器人向前走三秒钟的程序实 例。
图 3-1 机器人及其前进方向的定义 例程:RobotForwardThreeSeconds.c
z 确保控制器和伺服电机都已接通电源
z 输入、保存、编译、下载并运行程序 RobotForwardThreeSeconds.c
#include<BoeBot.h>
#include<uart.h>
int main(void) {
int counter;
uart_Init();
printf("Program Running!\n");
for(counter=0;counter<130;counter++)//运行 3 秒 {
RobotForwardThreeSeconds.c 是如何工作的?
理解该例程的运行你应该没啥问题:for 循环体中前三行语句使左侧电机逆时针旋转,
z 以新的文件名存储程序 RobotForwardThreeSeconds.c z 运行程序来验证运行的时间和距离是否是刚才的一半
delay_nus(1560);
P1_1=0;
P1_0=1;
delay_nus(1440);
z 输入、保存并运行程序 ForwardLeftRightBackward.c
#include<BoeBot.h>
#include<uart.h>
int main(void) {
int counter;
uart_Init();
printf("Program Running!\n");
for(counter=1;counter<=65;counter++)//向前 {
P1_1=1;
delay_nus(1700);
P1_1=0;
P1_0=1;
delay_nus(1300);
P1_0=0;
delay_nms(20);
}
for(counter=1;counter<=26;counter++)//向左转 {
P1_1=1;
delay_nus(1300);
P1_1=0;
P1_0=1;
delay_nus(1300);
P1_0=0;
delay_nms(20);
}
for(counter=1;counter<=26;counter++)//向右转 {
P1_1=1;
delay_nus(1700);
P1_1=0;
P1_0=1;
delay_nus(1700);
P1_0=0;
delay_nms(20);
}
for(counter=1;counter<=65;counter++)//向后 {
P1_1=1;
delay_nus(1300);
P1_1=0;
P1_0=1;
把ForwardLeftRightBackward.c 另存为 PivotTests.c。
用刚讨论过的代码片段替代前进、左转、右转和后退相应的代码片段,通过更改每个 pulseCount 就增加 1:第一次循环时,变量 pulseCount 的值是 10,此时发给 P1_1、P1_0 的 脉冲的宽度分别为1.51ms、1.49ms;第二次循环时,变量 pulseCount 的值是 11,此时发给 P1_1、P1_0 的脉冲的宽度分别为 1.511ms、1.489ms。随着变量 pulseCount 值的增加,电机 的速度也在逐渐增加。到执行第190 次循环时,变量 pulseCount 的值是 200,此时发给 P1_1、
P1_0 的脉冲的宽度分别为 1.7ms、1.3ms,电机全速运转。
回 顾 第 二 章 任 务 三 ,for 循 环 也 可 以 由 高 向 低 记 数 。 你 可 以 通 过 使 用
int main(void) {
int pulseCount;
uart_Init();
printf("Program Running!\n");
z 输入、保存并运行程序 StartAndStopWithRamping.c
z 验证机器人是否逐渐加速到全速,保持一段时间,然后逐渐减速到停止 z 如果你觉得运行时间太长,更改参数直到你满意为止
该你了
创建一个程序,将加速或减速与其他的运动结合起来。下面是一个逐渐增加速度向后走
而不是向前走的例子。加速向后走与向前走的唯一不同之处在于发给P1_1 的脉冲的宽度由 序。通过逐渐减小程序中两个 pulseCount 的值,可以沿另一个方向匀变速旋转。这是一个 匀变速旋转四分之一周的例子。
for(pulseCount=1;pulseCount<=65;pulseCount++)//匀加速向右转 {
P1_1=1;
delay_nus(1500+pulseCount);
P1_1=0;
P1_0=1;
delay_nus(1500+pulseCount);
P1_0=0;
delay_nus(1500+pulseCount);
P1_1=0;
P1_0=1;
delay_nus(1500+pulseCount);
P1_0=0;
delay_nms(20);
}
从任务一中打开程序 ForwardLeftRightBackward.c,存为 ForwardLeftRightBackward-
Ramping.c。
更改新的程序,使机器人的每一个动作能够匀加速和匀减速。
提示:你可以使用上面的代码片段和StartAndStopWithRamping.c程序中相似的片段。
任务三 用函数调用简化运动程序
在下一章,你的机器人将需要执行各种运动来避开障碍物和完成其它动作。不过,无论
机器人要执行何种动作,都离不开前面讨论的各种基本动作。为了各种应用程序方便使用这
void Forward(void)
{
void Forward(int PulseCount,int Velocity) /* Velocity should be between 0 and 200 */
{
int i;
for(i=1;i<=PulseCount;i++) {
P1_1=1;
delay_nus(1500+Velocity);
P1_1=0;
P1_0=1;
delay_nus(1500-Velocity);
P1_0=0;
z 输入、保存、编译、下载并运行程序MovementsWithFunctions.c
#include<BoeBot.h>
#include<uart.h>
void Forward(int PulseCount,int Velocity) /* Velocity should be between 0 and 200 */
delay_nus(1500+ Velocity);
P1_1=0;
P1_0=1;
delay_nus(1500- Velocity);
P1_0=0;
delay_nms(20);
} }
void Left(int PulseCount,int Velocity) /* Velocity should be between 0 and 200 */
delay_nus(1500-Velocity);
P1_1=0;
P1_0=1;
delay_nus(1500-Velocity);
P1_0=0;
delay_nms(20);
} }
void Right(int PulseCount,int Velocity) /* Velocity should be between 0 and 200 */
{
int i;
for(i=1;i<= PulseCount;i++) {
delay_nms(20);
} }
void Backward(int PulseCount,int Velocity) /* Velocity should be between 0 and 200 */
{
int i;
for(i=1;i<= PulseCount;i++) {
P1_1=1;
delay_nus(1500-Velocity);
P1_1=0;
P1_0=1;
delay_nus(1500+ Velocity);
P1_0=0;
delay_nms(20);
} }
int main(void) {
uart_Init();
printf("Program Running!\n");
Forward(65,200);
#include <BoeBot.h>
#include <uart.h>
void Move(int counter,int PC1_pulseWide,int PC0_pulseWide)
delay_nus(PC1_pulseWide);
P1_1=0;
P1_0=1;
delay_nus(PC0_pulseWide);
P1_0=0;
delay_nms(20);
} }
int main(void) {
uart_Init();
printf("Program Running!\n");
Move(65,1700,1300);
Move(26,1300,1300);
Move(26,1700,1700);
Move(65,1300,1700);
while(1);
}
z 输入、保存并运行程序MovementsWithOneFuntion.c z 你的机器人是否执行了你熟悉的前、左、右、后运动呢?
z 修改MovementsWithOneFuntion.c 使机器人走一个正方形。第一边和第二边向前走,
另外两个边向后走
的单引号只起到定界作用并不表示字符本身。单引号中的字符不能是单引号(‘)和反斜杠
ASCII是美国标准信息交换码(American Standard Code for Information Interchange)
的缩写,用来制订计算机中每个符号对应的代码,这也叫做计算机的内码(code)。
\xhh 任意字符二位十六进制
char Navigation[10]={'F','L','F','F','R','B','L','B','B','Q'};
如何才能把放入数组中的元素引用出来呢?
Navigation[0] (第一个字符:‘F’)
Navigation[5] (第六个字符:‘B’)
字符串和字符串结束标志
空间。
C语言允许用字符串的方式对数组作初始化赋值,如Navigation[10]的初始化赋值可写 为:
char Navigation[10]={“FLFFRBLBBQ”};
或者去掉“{}”,写为:
char Navigation[10]=“FLFFRBLBBQ”;
要特别注意字符与字符串的区别,除了表示形式不同外,其存储性质也不相同,字符‘A’
只占1个字节,而字符串“A”占2个字节。
下面的例程采用字符数组定义一系列复杂的运动。
例程:NavigationWithSwitch.c
z 输入、保存、编译、下载并运行程序NavigationWithSwitch.c
#include<BoeBot.h>
#include<uart.h>
void Forward(void) {
void Left_Turn(void) {
void Right_Turn(void) {
int i;
for(i=1;i<=26;i++)
void Backward(void) {
int main(void) {
char Navigation[10]={'F','L','F','F','R','B','L','B','B','Q'};
int address=0;
uart_Init();
printf("Program Running!\n");
while(Navigation[address]!='Q') {
switch(Navigation[address]) {
while(1);
char Navigation[10]={'F','L','F','F','R','B','L','B','B','Q'};
这个数组中存储的是一些命令:F表示向前运动,L表示向左转,R表示向右转,B表示向
int Pulses_Count[5]={65,26,26,65,0};
int Pulses_Left[4]={1700,1300,1700,1300};
int Pulses_Right[4]={1300,1300,1700,1700};
int 型 变 量 address 作 为 访 问 数 组 的 索 引 值 , 每 次 用 address 提 取 一 组 数 据 : Pulses_Count[address],Pulses_Left[address],Pulses_Right[address],这些变量值被 放在下面的代码块中,作为机器人运动一次的参数。
for(int counter=1;counter<=Pulses_Count[address];counter++)
{
int main(void) {
int Pulses_Count[5]={65,26,26,65,0};
int Pulses_Left[4]={1700,1300,1700,1300};
int Pulses_Right[4]={1300,1300,1700,1700};
int address=0;
int counter;
uart_Init();
printf("Program Running!\n");
while(Pulses_Count[address]!=0)
z 输入、保存并运行程序NavigationWithValues.c
你的机器人否已经做了我们所熟悉的向前,向左,向右,向后的运动呢?现在是不是有 点厌烦了呢?你还想让你的机器人做其它的动作或者创建你自己的程序吗?
该你了――设计你自己的程序
z 以一个新的文件名保存程序NavigationWithValues.c z 用下面的代码代替三个数组
int Pulses_Count[10]={60,80,100,110,110,110,100,80,60,0};
int Pulses_Left[10]={1700,1600,1570,1520,1500,1480,1430,1400,1300,1500};
int Pulses_Right[10]={1300,1400,1430,1480,1500,1520,1570,1600,1700,1500};
z 运行更改后的程序,观察机器人会做些什么
z 输入、保存并运行程序,你的机器人是不是按你的想法运动呢?
工程素质和技能归纳
1. 归纳机器人的基本巡航动作并给 C51 单片机编程实现这些基本动作 2. 用牛顿力学和运动学知识分析机器人的运动行为
3. 采用匀变速运动改善机器人的基本运动行为
4. 用 C 语言的函数实现机器人的基本动作,函数的定义和调用方法
5. 分析机器人基本动作函数的实现特点,用一个函数定义机器人的所有行为 6. 使用不同的数组来建立复杂的机器人运动
7. 分支语句的使用等
科学精神的培养
1. 比较各种实现机器人基本动作程序的优缺点,以及后续程序的可扩展性
2. 比较 C 语言数组与 BASIC 程序 DATA 语句,看看它们是否有共同点,哪个更容易使 用和理解
3. 比较 C 语言的 switch 语句和 PBASIC 的 SELECT 语句