• 沒有找到結果。

C 语言函数与机器人巡航控制

通过对单片机编程可以使机器人完成各种巡航动作。本章所要介绍的这些巡航动作和编 程技术在后面的章节都会用到。与后面章节唯一不同的是:本章机器人在无感觉的情况下巡 航,而在后面的章节中,机器人将根据传感器检测到的信息进行智能巡航。

本章所要完成的主要任务如下:

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 语句