前言
本篇博文介绍的是用51单片机的最终项目《智能语音小车》【中】,包含循迹小车基本原理和方案,根据循迹原理实现循迹功能代码编写,解决冲出赛道不转弯问题,优化转弯平滑。加入电机调速,跟随小车,摇头测距小车01_舵机和超声波封装,摇头测距小车02_实现疯狂摇头,摇头测距小车03_测距摇头,摇头测距小车04_摇头测距和行驶bug调试。看到这篇博文的朋友,可以先赞再看吗?
预备知识
一、需要我之前写的所有博文的知识,如果还没看我之前的的博文,可以去看看后再看本篇博文
二、C变量
三、基本输入输出
四、流程控制
五、函数
六、指针
七、字符串
如果以上知识不清楚,请自行学习后再来浏览。如果我有没例出的,请在评论区写一下。谢谢啦!
1.循迹小车基本原理和方案
1.1循迹模块简介
循迹模块
也叫TCRT5000传感器
,他的红外发射二极管不断发射红外线
,当发射出的红外线没有被反射回来
或被反射回来但强度不够大
时, 红外接收管一直处于关断状态
,此时模块的输出端为高电平
,指示二极管一直处于熄灭状态
。被检测物体出现在检测范围内
时,红外线被反射回来且强度足够大
,红外接收管饱和
,此时模块的输出端为低电平
,指示二极管被点亮
总结:没反射回来,D0输出高电平,灭灯
1.2循迹模块的接线方式
接线方式
VCC:
接电源正极(3-5V
)
GND:
接电源负极
DO
:TTL开关信号输出0、1
AO:
模拟信号输出(不同距离输出不同的电压,此脚一般可以不接
)
1.3循迹小车原理
由于黑色具有较强的吸收能力,当循迹模块发射的红外线照射到黑线时,红外线将会被黑线吸收,导致循迹模块上光敏三极管处于关闭状态,此时模块上一个LED熄灭。在没有检测到黑线时,模块上两个LED常亮
总结:有感应到黑线,D0输出高电平 ,灭灯
循迹模块安装在小车车头两侧
下方小车两个模块都能反射回来红外,输出低电平,灯亮,直走
上方小车左模块遇到黑线,红外被吸收,左模块输出高电平,右模块输出低电平,左转,反之右转
2.根据循迹原理实现循迹功能代码编写
2.1根据循迹原理实现循迹功能代码编写核心思路
-
在主C文件中声明循迹模块所需引脚
-
在主C文件while(1)死循环内进行循迹模块返回数据判断,并执行相应代码
-
通过智能小车赛道验证代码可信性
注:此程序基于电机相关代码封装_分文件工程
开发
2.2在主C文件中声明循迹模块所需引脚
- 左轮循迹模块接单片机
P2.7
口,右轮循迹模块接单片机P2.6
口。 - 代码体现
sbit leftTracking = P2^7; //左边轮子的循迹模块接单片机P2.7口
sbit rightTracking = P2^6; //右边轮子的循迹模块接单片机P2.6口
2.3在主C文件while(1)死循环内进行循迹模块返回数据判断,并执行相应代码
- 根据1.3的结论进行对循迹模块的判断。
- 代码体现
f(leftTracking==0 && rightTracking==0)
{
goFront();
}
//上方小车左模块遇到黑线,红外被吸收,左模块输出高电平,右模块输出低电平,左转,反之右转
if(leftTracking==1 && rightTracking==0)
{
goLeft();
}
if(leftTracking==0 && rightTracking==1)
{
goRight();
}
if(leftTracking==1 && rightTracking==1)
{
stop();
}
- 在motor.c文件中封装停止函数
封装思路:将左右轮变量赋0即可停止
代码体现:
void stop()
{
RightControA = 0;
RightControB = 0;
LeftControA = 0;
LeftControB = 0;
}
- 在motor.h文件中声明stop函数
void goLeft();
void goRight();
void goBack();
void goFront();
void stop();
- 主C文件代码为
#include "motor.h"
#include "delay.h"
#include "reg52.h3"
sbit leftTracking = P2^7; //左边轮子的循迹模块接单片机P2.7口
sbit rightTracking = P2^6; //右边轮子的循迹模块接单片机P2.6口
void main()
{
while(1)
{
//下方小车两个模块都能反射回来红外,输出低电平,灯亮,直走
if(leftTracking==0 && rightTracking==0)
{
goFront();
}
//上方小车左模块遇到黑线,红外被吸收,左模块输出高电平,右模块输出低电平,左转,反之右转
if(leftTracking==1 && rightTracking==0)
{
goLeft();
}
if(leftTracking==0 && rightTracking==1)
{
goRight();
}
if(leftTracking==1 && rightTracking==1)
{
stop();
}
}
}
2.4通过智能小车赛道验证代码可信性
- 赛道建设建议
材料可以准备A4纸(可用作业本没写过的背面替代),黑色电工胶布,白色纸胶布或者白色电工胶布。
将A4纸纵向拼接在一起,纸与纸之间用白色纸脚布或者白色电工胶布粘住。最后构成一个矩形即可。
在构成的矩形边中间位置贴黑色电工胶布,拐弯处注意圆角处理。这样赛道就完工了。
使用时找重物按住即可
- 智能小车赛道验证结果
当把小车放在赛道上时,小车能走直线,但拐弯会跑出赛道。原因有两个:
一、小车速度过快
二、循迹模块太灵敏,很容易接收地面反光。
解决办法:
一、使用PWM调速使小车减速通过弯道
二、通过调节循迹模块上的十字电位器,调低灵敏度
3.解决冲出赛道不转弯问题,优化转弯平滑。加入电机调速
3.1解决冲出赛道不转弯问题,优化转弯平滑。加入电机调速核心思路
- 在主C文件中添加左右轮循迹模块声明
- 在主C文件主函数内添加判断左右循迹模块返回参数判断并设置左右轮转速。
- 调试优化,使小车循迹
注:此程序基于左右电机的各自调速管理工程
开发
3.2在主C文件中添加左右轮循迹模块声明
- 代码声明
sbit leftTracking = P2^7; //左边轮子的循迹模块接单片机P2.7口
sbit rightTracking = P2^6; //右边轮子的循迹模块接单片机P2.6口
3.3在主C文件主函数内添加判断左右循迹模块返回参数判断并设置左右轮转速。
- 判断循迹模块返回参数思路和1.3一样
- 代码体现
while(1)
{
//下方小车两个模块都能反射回来红外,输出低电平,灯亮,直走
if(leftTracking==0 && rightTracking==0)
{
speedRight = 25;
speedLeft = 25;
}
//上方小车左模块遇到黑线,红外被吸收,左模块输出高电平,右模块输出低电平,左转,反之右转
if(leftTracking==1 && rightTracking==0)
{
speedLeft = 10;
speedRight = 40;
}
if(leftTracking==0 && rightTracking==1)
{
speedLeft = 40;
speedRight = 10;
}
if(leftTracking==1 && rightTracking==1)
{
speedLeft = 0;
speedRight = 0;
}
}
- 主C文件代码
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "time0.h"
#include "time1.h"
#include "reg52.h"
extern char speedRight;
extern char speedLeft;
sbit leftTracking = P2^7; //左边轮子的循迹模块接单片机P2.7口
sbit rightTracking = P2^6; //右边轮子的循迹模块接单片机P2.6口
void main()
{
initTime0();
initTime1();
UartInit();
while(1)
{
//下方小车两个模块都能反射回来红外,输出低电平,灯亮,直走
if(leftTracking==0 && rightTracking==0)
{
speedRight = 25;
speedLeft = 25;
}
//上方小车左模块遇到黑线,红外被吸收,左模块输出高电平,右模块输出低电平,左转,反之右转
if(leftTracking==1 && rightTracking==0)
{
speedLeft = 10;
speedRight = 40;
}
if(leftTracking==0 && rightTracking==1)
{
speedLeft = 40;
speedRight = 10;
}
if(leftTracking==1 && rightTracking==1)
{
speedLeft = 0;
speedRight = 0;
}
}
}
3.4调试优化,使小车循迹
- 调试思路
冲出地图可以通过增大左右轮转向差数来解决,直线走不直可以通过调剂走直线左右轮速度变量控制左右轮速。
- 调试结果
一、地图长这样,和2.4的赛道有所区别,这版赛道我采用纸板,上面用毛笔蘸墨水画出赛道即可。
二、调试结果为设置小车直线行驶时左右轮速为35,拐弯轮速差为30。即可完美实现循迹。
三、调试后的代码
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "time0.h"
#include "time1.h"
#include "reg52.h"
extern char speedRight;
extern char speedLeft;
sbit leftTracking = P2^7; //左边轮子的循迹模块接单片机P2.7口
sbit rightTracking = P2^6; //右边轮子的循迹模块接单片机P2.6口
void main()
{
initTime0();
initTime1();
UartInit();
while(1)
{
//下方小车两个模块都能反射回来红外,输出低电平,灯亮,直走
if(leftTracking==0 && rightTracking==0)
{
speedRight = 35;
speedLeft = 35;
}
//上方小车左模块遇到黑线,红外被吸收,左模块输出高电平,右模块输出低电平,左转,反之右转
if(leftTracking==1 && rightTracking==0)
{
speedLeft = 10;
speedRight = 40;
}
if(leftTracking==0 && rightTracking==1)
{
speedLeft = 40;
speedRight = 10;
}
if(leftTracking==1 && rightTracking==1)
{
speedLeft = 0;
speedRight = 0;
}
}
}
4.跟随小车
4.1跟随小车实现原理
- 跟随小车实现原理和1.1一样,只不过传感器探头朝外。
-
跟随小车跟随原理:
左边跟随模块能返回红外,输出低电平,右边不能返回,输出高电平,说明物体在左边,需要左转
;右边跟随模块能返回红外,输出低电平,左边不能返回,输出高电平,说明物体在右边,需要右转
。
4.2跟随小车代码实现
- 基于
根据循迹原理实现循迹功能代码编写工程
开发 - 跟随算法实现代码
while(1)
{
//小车两个模块都能反射回来红外,输出低电平,灯亮,直走
if(leftFollow==0 && rightFollow==0)
{
goFront();
}
//右边跟随模块能返回红外,输出低电平,左边不能返回,输出高电平,说明物体在右边,需要右转
if(leftFollow==1 && rightFollow==0)
{
goRight();`
}
//左边跟随模块能返回红外,输出低电平,右边不能返回,输出高电平,说明物体在左边,需要左转
if(leftFollow==0 && rightFollow==1)
{
goLeft();
}
if(leftFollow==1 && rightFollow==1)
{
stop();
}
}
- 主C文件代码
#include "motor.h"
#include "delay.h"
#include "reg52.h"
//sbit leftTracking = P2^7; //左边轮子的循迹模块接单片机P2.7口
//sbit rightTracking = P2^6; //右边轮子的循迹模块接单片机P2.6口
sbit leftFollow = P2^5; //左边轮子的跟随模块接单片机P2.5口
sbit rightFollow = P2^4; //右边轮子的跟随模块接单片机P2.4口
void main()
{
while(1)
{
//小车两个模块都能反射回来红外,输出低电平,灯亮,直走
if(leftFollow==0 && rightFollow==0)
{
goFront();
}
//右边跟随模块能返回红外,输出低电平,左边不能返回,输出高电平,说明物体在右边,需要右转
if(leftFollow==1 && rightFollow==0)
{
goRight();`
}
//左边跟随模块能返回红外,输出低电平,右边不能返回,输出高电平,说明物体在左边,需要左转
if(leftFollow==0 && rightFollow==1)
{
goLeft();
}
if(leftFollow==1 && rightFollow==1)
{
stop();
}
}
}
4.3跟随红外模块使用注意事项
在关闭窗帘的室内使用,因为阳光中有部分红外线,会导致模块异常,实验结果出错。
5.摇头测距小车01_舵机和超声波封装
5.1摇头测距小车01_舵机和超声波封装核心思路
- 分别建立
hc04.c
,hc04.h
,delay.c
,delay.h
,sg90.c
,sg90.h
文件 - 拷贝以上相应代码去相应文件
- 在主C文件和其他文件中调用相应的.h文件。
注:此程序基于感应开关盖垃圾桶项目_实现距离感应开盖工程
开发
5.2分别建立hc04.c,hc04.h,delay.c,delay.h,sg90.c,sg90.h文件
- 建立的文件如下:
5.3拷贝以上相应代码去相应文件
- hc04.c对应代码为
#include "reg52.h"
#include "delay.h"
sbit Trig = P2^3; //超声波测距的控制Trig接单片机2.3口
sbit Echo = P2^2; //超声波测距的控制Echo接单片机2.2口
void startHC() //给超声波模块控制为Trig一个10us的触发信息
{
Trig = 0;
Trig = 1;
Delay10us();
Trig = 0;
}
void initTime1() //设置定时器T0为16位模式,从0开始数数,不忙启动定时器
{
TMOD &= 0x0F;
TMOD |= 0x10;
TL1 = 0;
TH1 = 0;
}
double getDistance()
{
double time;
//重新给初值,重新计时
TL1 = 0;
TH1 = 0;
//1.Trig ,给Trig端口至少10us的高电平
startHC();
//2.Echo信号,由低电平跳转到高电平,表示开始发送波
while(Echo == 0);
// 波发出去的那一下,开始启动定时器
TR1 = 1; //启动定时器T0
//3.Echo,由高电平跳转回低电平,表示波回来了
while(Echo == 1);
// 波回来的那一下,我们开始停止定时器,计算出中间经过多少时间
TR1 = 0; //关闭定时器
time = ((TH1 * 256) + TL1)*1.085; //单位为us。
/*
256的含义:
对于十进制数2向左移一位位20,相当于乘以10
那么对于二进制数2向左移移一位,相当于乘以2。移动8位,乘以2的8次方256
*/
//4.距离 = 速度 (340m/s)* 时间/2
//dis = 340m/s = 34000cm/s = 34cm/ms = 0.034cm/us
//dis = 0.034 * time/2; <==> 0.017*time;
return (0.017 * time);
}
- hc04.h对应代码为
void initTime1();
double getDistance();
- delay.c对应代码为
void Delay10us() //@11.0592MHz 软件延时10us
{
unsigned char i;
i = 2;
while (--i);
}
void Delay2000ms() //@11.0592MHz 软件延时2秒
{
unsigned char i, j, k;
i = 15;
j = 2;
k = 235;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay150ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 2;
j = 13;
k = 237;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay300ms() //@11.0592MHz 软件延时300毫秒
{
unsigned char i, j, k;
i = 3;
j = 26;
k = 223;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
- delay.h对应代码为
void Delay10us();
void Delay2000ms();
void Delay150ms();
void Delay300ms();
- sg90.c对应代码为
#include "reg52.h"
#include "delay.h"
sbit D5 = P3^7; //根据原理图D5 led灯接单片机3.7口
sbit D6 = P3^6; //根据原理图D6 led灯接单片机3.6口
sbit sg90_con = P1^1; //sg90舵机PWM控制线接单片机P1.1口,用于模拟PWM波形。
char cnt = 0; //记录爆表次数
char angle; //记录舵机角度变量
void initTime0() //初始化定时器T0
{
//1.设置定时器为T0,16位模式
TMOD &= 0xF0;
TMOD |= 0x01;
//2.设置初值,定时0.5毫秒
TL0 = 0x33;
TH0 = 0xFE;
//3.启动定时器,开始计时
TR0 = 1; //TR0为启动定时器标志,值为1启动定时器
TF0 = 0;
//4.打开定时器0的中断 赋1打开
ET0 = 1;
//5.打开总中断EA 赋1打开
EA = 1;
}
void initSG90_0() //SG90舵机初始化函数。
{
sg90_con = 1; //单片机上电,给sg90舵机控制线一个高电平。
angle = 1; //给角度控制变量初始化为0度,因为0.5毫秒的高电平PWM波为0度,angle = 1;时定时器刚好定时0.5毫秒
cnt = 0; //初始化爆表变量
}
void openCoverLight() //开盖灯的状态
{
D5 = 0;
D6 = 1;
}
void closeCoverLight() //关盖垃圾桶的状态
{
D5 = 1;
D6 = 0;
}
void openCoverDustbin() //垃圾桶开盖函数
{
angle = 3; //舵机开盖
cnt = 0;
Delay2000ms();
}
void closeCoverDustbin()//垃圾桶关盖函数
{
angle = 1; //舵机关盖
cnt = 0;
Delay150ms();
}
void Time0Handler() interrupt 1 //定时器T0中断函数,中断号为interrupt 1
{
cnt++;
//重新给定时器赋初值,定时0.5毫秒
TL0 = 0x33;
TH0 = 0xFE;
//判读记录爆表变量cnt的值是否小于角度变量的值,小于继续给sg90舵机控制线高电平,大于等于则给低电平
if(cnt < angle)
{
sg90_con = 1;
}
else
{
sg90_con = 0;
}
//判断cnt是否加到40,定时是否20毫秒PWM波形周期
if(cnt == 40)
{
cnt = 0; //定时到20豪秒,cnt从0开始加
sg90_con = 1;//定时到20豪秒,重新给舵机控制线一个高电平
}
}
- sg90.h对应代码为
void initTime0(); //初始化定时器T0
void initSG90_0(); //SG90舵机初始化函数。
void openCoverLight(); //开盖灯的状态
void closeCoverLight(); //关盖垃圾桶的状态
void openCoverDustbin(); //垃圾桶开盖函数
void closeCoverDustbin();//垃圾桶关盖函数
5.4在主C文件和其他文件中调用相应的.h文件。
- 主C文件中调用的.h文件
#include "reg52.h"
#include "hc04.h"
#include "delay.h"
#include "sg90.h"
- hc04.c文件中调用的.h文件
#include "reg52.h"
#include "delay.h"
- sg90.c文件中调用的.h文件
#include "reg52.h"
#include "delay.h"
- 主C文件代码
#include "reg52.h"
#include "hc04.h"
#include "delay.h"
#include "sg90.h"
/*
时间: 2023年10月4日14:00:24
程序功能:垃圾桶03实现距离感应开盖
注意:一定要在魔术手中Output内勾选输出Hex文件,不然代码无效,执行其他代码。
*/
void main()
{
double dis;
Delay300ms();
initTime0();
initTime1();
initSG90_0();
while(1)
{
dis = getDistance(); //超声波测距
if(dis < 10)
{
openCoverLight(); //开盖,D5亮
openCoverDustbin();
}
else
{
closeCoverLight(); //关盖,D5灭
closeCoverDustbin();
}
}
}
6.摇头测距小车02_实现疯狂摇头
6.1摇头测距小车02_实现疯狂摇头核心思路
- 修改关盖函数
closeCoverDustbin();
为sg90Right()
。修改开盖函数openCoverDustbin();
为sg90Middle();
- 增加
sg90Left();
函数 - 主C文件修改初识位置为
中间
;while(1)死循环中给予适度延时
调用右
、中
、左
、中
、sg90函数。
6.2修改关盖函数closeCoverDustbin();为sg90Right()。修改开盖函数openCoverDustbin();为sg90Middle();
- 修改关盖函数
closeCoverDustbin();
为sg90Right();
修改前
void closeCoverDustbin()//垃圾桶关盖函数
{
angle = 1; //舵机关盖
cnt = 0;
Delay150ms();
}
修改后(去掉延时,不然在最终呈现时会影响结果)
void sg90Right()//垃圾桶关盖函数
{
angle = 1; //舵机关盖
cnt = 0;
}
- 修改开盖函数
openCoverDustbin();
为sg90Middle();
修改前
void openCoverDustbin() //垃圾桶开盖函数
{
angle = 3; //舵机开盖
cnt = 0;
Delay2000ms();
}
修改后(去掉延时,不然在最终呈现时会影响结果)
void sg90Middle() //垃圾桶开盖函数
{
angle = 3; //舵机开盖
cnt = 0;
}
6.3增加sg90Left();函数
- 角度变量设置成5,sg90舵机转动180度。
- 代码体现
void sg90Left()//
{
angle = 5; //旋转180度
cnt = 0;
}
- 在sg90.h文件中添加sg90左、右、中函数声明
void sg90Middle(); //垃圾桶开盖函数
void sg90Right();//垃圾桶关盖函数
void sg90Left();
- sg90.h文件代码。
void initTime0(); //初始化定时器T0
void initSG90_0(); //SG90舵机初始化函数。
void openCoverLight(); //开盖灯的状态
void closeCoverLight(); //关盖垃圾桶的状态
void sg90Middle(); //垃圾桶开盖函数
void sg90Right();//垃圾桶关盖函数
void sg90Left();
6.4主C文件修改初识位置为中间;while(1)死循环中给予适度延时调用右、中、左、中、sg90函数。
- 初始化位置为中间后延时1秒。
sg90Middle();
Delay1000ms();
- while(1)死循环中给予
600ms
延时调用右、中、左、中、sg90函数最为适合。
while(1)
{
sg90Right();
Delay600ms();
sg90Middle();
Delay600ms();
sg90Left();
Delay600ms();
sg90Middle();
Delay600ms();
}
- 主C文件代码
#include "reg52.h"
#include "hc04.h"
#include "delay.h"
#include "sg90.h"
/*
时间: 2023年10月4日14:00:24
程序功能:垃圾桶03实现距离感应开盖
注意:一定要在魔术手中Output内勾选输出Hex文件,不然代码无效,执行其他代码。
*/
void main()
{
Delay300ms();
initTime0();
initTime1();
sg90Middle();
Delay1000ms();
while(1)
{
sg90Right();
Delay600ms();
sg90Middle();
Delay600ms();
sg90Left();
Delay600ms();
sg90Middle();
Delay600ms();
}
}
- delay.c和delay.h文件代码
#include "intrins.h"
void Delay10us() //@11.0592MHz 软件延时10us
{
unsigned char i;
i = 2;
while (--i);
}
void Delay2000ms() //@11.0592MHz 软件延时2秒
{
unsigned char i, j, k;
i = 15;
j = 2;
k = 235;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay150ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 2;
j = 13;
k = 237;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay300ms() //@11.0592MHz 软件延时300毫秒
{
unsigned char i, j, k;
i = 3;
j = 26;
k = 223;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay600ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 5;
j = 52;
k = 195;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay10us();
void Delay2000ms();
void Delay150ms();
void Delay300ms();
void Delay600ms();
void Delay1000ms();
7.摇头测距小车03_测距摇头
7.1摇头测距小车03_测距摇头核心思路
在主C文件中设计算法,前方距离小于35厘米就左右两边都测一下距离。距离大于35厘米就前进。
7.2算法设计思路
- 在主C文件的主函数中一上电就让测距模块在中间位置,在
while(1)
死循环中先测正前面的距离,距离大于35厘米
就直走,距离小于35厘米
就左边测,然后右边测,最后回中。具体请看程序代码。
sg90Middle();
Delay1000ms();
while(1)
{
disMiddle = getDistance(); //先测中间
if(disMiddle > 35) //如果中间距离大于35厘米就前进
{
//前进
}
else //否则就摇头
{
sg90Left(); //转左边
disLeft = getDistance(); //测左边
Delay600ms(); //等待600毫秒
sg90Middle(); //转中间
Delay600ms(); //等待600毫秒
sg90Right(); //转右边
disRight = getDistance(); //测右边
Delay600ms(); //等待600毫秒
}
sg90Middle(); //回到中间
Delay600ms(); //等待600毫秒
}
这样的代码验证后,sg90Middle();//回到中间 Delay600ms();//等待600毫秒;
这两句会出现舵机在中间抽搐。原因是当距离大于35厘米时会一直调用转向中间函数,但此时处于中间位置,所以会出现抽搐。
解决办法为定义标志位来控制
- 建立转向位置位置变量算法思路:定义转向位置变量宏,当初始化为中间的时候记录中间位置,在
while(1)
死循环内进行位置判断,如果位置不是中间位置,就回到中间,在中间位置就不做任何操作。
#define MIDDLE 0 //定义中间位置宏
#define RIGHT 1 //定义右边位置宏
#define LEFT 2 //定义左边位置宏
sg90Middle();
dir = MIDDLE; //记录中间位置
Delay1000ms();
disMiddle = getDistance(); //先测中间
if(dir != MIDDLE) //方向不在中间会中
{
sg90Middle(); //回中
Delay600ms();
dir = MIDDLE; //软件初始化
}
if(disMiddle > 35) //如果中间距离大于35厘米就前进
{
//前进
}
else //否则就摇头
{
sg90Left(); //转左边
disLeft = getDistance(); //测左边
Delay600ms(); //等待600毫秒
sg90Middle(); //转中间
Delay600ms(); //等待600毫秒
sg90Right(); //转右边
disRight = getDistance(); //测右边
dir = RIGHT; //记录末位置
Delay600ms(); //等待600毫秒
}
7.3完整主C文件代码
#include "reg52.h"
#include "hc04.h"
#include "delay.h"
#include "sg90.h"
#define MIDDLE 0 //定义中间位置宏
#define RIGHT 1 //定义右边位置宏
#define LEFT 2 //定义左边位置宏
/*
时间: 2023年10月4日14:00:24
程序功能:垃圾桶03实现距离感应开盖
注意:一定要在魔术手中Output内勾选输出Hex文件,不然代码无效,执行其他代码。
*/
void main()
{
double disMiddle; //中间距离变量
double disLeft; //左边距离变量
double disRight; //右边距离变量
char dir; //转头标志位变量
Delay300ms();
initTime0();
initTime1();
sg90Middle();
dir = MIDDLE; //记录中间位置
Delay1000ms();
//Delay600ms();
while(1)
{
disMiddle = getDistance(); //先测中间
if(dir != MIDDLE) //方向不在中间会中
{
sg90Middle(); //回中
Delay600ms();
dir = MIDDLE; //软件初始化
}
if(disMiddle > 35) //如果中间距离大于35厘米就前进
{
//前进
}
else //否则就摇头
{
sg90Left(); //转左边
disLeft = getDistance(); //测左边
Delay600ms(); //等待600毫秒
sg90Middle(); //转中间
Delay600ms(); //等待600毫秒
sg90Right(); //转右边
disRight = getDistance(); //测右边
dir = RIGHT; //记录末位置
Delay600ms(); //等待600毫秒
}
/*
验证后,这两句会出现舵机在中间抽搐。
原因是当距离大于35厘米时会一直调用转向中间函数,但此时处于中间位置,所以会出现抽搐。
解决办法为定义标志位来控制
sg90Middle(); //回到中间
Delay600ms(); //等待600毫秒
*/
}
}
8.摇头测距小车04_摇头测距和行驶bug调试
8.1摇头测距小车04_摇头测距和行驶核心思路
- 在7.3代码中添加判断转向代码
- 在转向代码中操控LED灯已验证是否进入判断执行方便调试
8.2在7.3代码中添加判断转向代码
转向思路:当左边的距离小于右边的距离时,右转一段距离,使用延时函数,延时300毫秒适合我的地图。当右边的距离小于左边的距离时,左转一段距离,也延时300毫秒。
- 转向代码
if(disLeft < disRight) //左边小于右边,向右转
{
goRight();
Delay300ms();
stop();
}
if(disRight < disLeft ) //左边大于右边,向左转
{
goLeft();
Delay300ms();
stop();
}
- 地图照片
8.3在转向代码中操控LED灯已验证是否进入判断执行方便调试
- 声明LED灯地址
右转指示灯
sbit D1 = P3^7;
左转指示灯
sbit D2 = P3^6;
- 主C文件主函数一开始就初识化指示灯
初始化熄灭
D1 = 1;
D2 = 1;
- 左右转功能中使用LED灯
if(disLeft < disRight) //左边小于右边,向右转
{
D1 = 0;
goRight();
Delay300ms();
//Delay200ms();
stop();
D1 = 1;
}
if(disRight < disLeft ) //左边大于右边,向左转
{
D2 = 0;
goLeft();
Delay300ms();
//Delay200ms();
stop();
D2 = 1;
}
8.4 Bug调试
Bug1:
小车直冲撞墙,不左右转也不后退。
解决办法:添加后退代码,距离小于10厘米就后退一定距离,指导距离大于10厘米。
if(disMiddle > 30) //如果中间距离大于30厘米就前进
{
//前进
goFront();
}
else if(disMiddle < 10)
{
goBack();
}
else
Bug2
:小车待在原地转头
解决办法:我开始以为是超声波测距没有测到数据,于是对美测到距离的情况做判断,可是实验后并没有解决待在原地不动转头。也没有进入代码程序,我想是不是左右距离一样导致无法判断,可是相应代码后还未解决。于是我试了很久很久,浪费了很多时间。终于我看到电机转动开始不怎么转了,想到可能是电池没电了。于是我用充电宝供电,一下子就又可以使用了。而且算法没一点矛盾。
所以在调试小车时一定要记得检测电源是否电量充足。
文章来源:https://www.toymoban.com/news/detail-834466.html
结束语
很高兴您能看到这里,点个赞再走呗。谢谢您啦!!!文章来源地址https://www.toymoban.com/news/detail-834466.html
到了这里,关于“无限交互,全新驾驶体验!智能语音小车,与您共同开创未来出行。”#51单片机最终项目《智能语音小车》【中】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!