目录
一:硬件设备
1:主控
所选取主控芯片为STC89C52RC,这里建议用stm32f103系列的单片机。两者价格差不多,用51后期调试特别麻烦。
2:扫码模块
MJ-8000,关于这个模块有发表的一篇文章已做过详细论述:基于STC89C52RC单片机的二维码或一维码识别小车_差一年半入门的小白的博客-CSDN博客
该模块集成度很高,二次开发较为简单,只需要根据资料串口控制自己所需要的模式即可。
3:货物检测
红外避障模块,一端VCC,一端GND,检测数字信号高低即可,灵敏度可调(即检测距离可调)。
4:循迹模块
TCRT5000(如果是自己做一些作品的话,场地一定要用白色背景板!!!要不然会出很多奇奇怪怪的标志位误判问题,折磨了我半个月之久),使用起来和红外避障模块类似,一端VCC,一端GND,检测数字信号。
5:电机驱动模块
L298N,这个模块如有想具体了解的可以私信我(大多数人应该都用到过,所以就不耗费时间做讲解了)
6:电源
串联3节18650电池(一定要加电源保护模块,要不然有可能会损坏电池)
最后展示一下机器人从0到1的过程吧:
所有元器件 :
机器人底盘:
机器人成品:
二:软件代码
#include "reg52.h"
#include "car.h"
#include "angle.h"
#include "ultrasound.h"
sbit L0 = P0^0;//绿灯
sbit L1 = P0^1;//红灯
sbit Out = P2^0;//货物检测
sbit Right_D0 = P0^4;//右循迹模块
sbit Left_D0 = P0^5;//左循迹模块
#define PWM_Limit 100//PWM峰值
unsigned int PWM;//小车控速
unsigned char address;//小车位置识别变量
unsigned int msec;//时间
unsigned char sec;//时间
unsigned char seclect = 1;//房间号码
float distance;//超声波避障
bit L1_flag;//L1灯亮灭标志位
bit Run_flag;//小车是否行走标志位
bit Recave_flag;//扫码成功标志位
/*定时器初始化函数*/
void delay(unsigned int z)
{
unsigned int x,y;
for(x = z; x > 0; x--)
for(y = 57; y > 0; y--);
}
void Time_Init(void)//定时器0初始化
{
TMOD |= 0x01;
TH0 = (65535 - 500 + 1)/256;
TL0 = (65535 - 500 + 1)%256;
EA = 1;
TR0 = 1;
ET0 = 1;
}
/*串口初始化函数*/
void usart_Init()//串行中断初始化程序
{
TMOD |= 0X20; //定时器1工作模式2 8位自动重装
TH1 = 0XFD;
TL1 = 0XFD; //比特率9600
TR1 = 1; //启动T1定时器
SM0 = 0;
SM1 = 1; //串口工作方式1 10位异步
REN = 1; //串口允许接收
ES = 1; //串口中断打开
}
/*串口复用定时器函数*/
void Usart_Time()//把串口中断4改为定时器中断3
{
static Change_flag;
if(Change_flag == 0)
{
if(Recave_flag == 1)//扫码成功后无需串口功能,改为定时器功能
Change_flag = 1;
}
else if(Change_flag == 1)
{
REN = 0; //串口不允许接收
ES = 0; //串口中断关闭
Change_flag = 2;
}
else if(Change_flag == 2)
{
// distance = distance_count();//读取超声波数据
}
}
/*货物检查函数*/
void Goods_Checek()
{
if(address < seclect)//起点处做货物检测
{
if(Out == 0)//货物在车身
{
Run_flag = 1;//小车货物检测解锁
}
else//货物不在车身
{
Run_flag = 0;//小车货物检测锁定
}
}
}
/*LED指示灯函数*/
void LED_Show()
{
if(address < seclect)
{
L0 = L1 = 0;//起点处两个灯都熄灭
}
}
/*小车PWM模式及PWM占空比设置模式*/
void Car_PWM_Channo(unsigned char Mode,unsigned int PWM_Max)
{
if(PWM <= PWM_Max)
Car_Mode_Select(Mode);
else if(PWM <= PWM_Limit)
Car_Mode_Select(0);
else
PWM = 0;
}
/*小车行驶逻辑函数*/
void Car_Run()
{
unsigned char Left,Right,Middle,Judge;
if(Recave_flag == 1)//接收到二维码内信息
{
if(Run_flag == 1)//货物已放车上
{
if(address < 4)
{
if(Right_D0 == 0 && Left_D0 == 1)//车身向右偏移需要向左转
{
delay(1);//消抖
if(Right_D0 == 0 && Left_D0 == 1)
{
Left = 2;
if(Left == 2 && Right == 0 && Middle == 0 && Judge == 0)
{
PWM = 0;
Right = Middle = Judge = 1;
}
else if(Left == 2 && Right == 1 && Middle == 2 && Judge == 1)
{
PWM = 0;
Right = Middle = Judge = 0;
}
else if(Left == 2 && Right == 2 && Middle == 1 && Judge == 1)
{
PWM = 0;
Right = Middle = Judge = 0;
}
else if(Left == 2 && Right == 1 && Middle == 1 && Judge == 2)
{
PWM = 0;
Right = Middle = Judge = 0;
}
Car_PWM_Channo(3,80);
}
}
else if(Left_D0 == 0 && Right_D0 == 1)//车身向左偏移需要向右转
{
delay(1);//消抖
if(Left_D0 == 0 && Right_D0 == 1)
{
Right = 2;
if(Right == 2 && Left == 0 && Middle == 0 && Judge == 0)
{
PWM = 0;
Left = Middle = Judge = 1;
}
else if(Right == 2 && Left == 1 && Middle == 2 && Judge == 1)
{
PWM = 0;
Left = Middle = Judge = 0;
}
else if(Right == 2 && Left == 2 && Middle == 1 && Judge == 1)
{
PWM = 0;
Left = Middle = Judge = 0;
}
else if(Right == 2 && Left == 1 && Middle == 1 && Judge == 2)
{
PWM = 0;
Left = Middle = Judge = 0;
}
Car_PWM_Channo(4,80);
}
}
else if(Right_D0 == 0 && Left_D0 == 0)//车身在直线范围内,直行
{
delay(1);//消抖
if(Right_D0 == 0 && Left_D0 == 0)
{
Middle = 2;
if(Middle == 2 && Left == 0 && Right == 0 && Judge == 0)
{
PWM = 0;
Left = Right = Judge = 1;
}
else if(Middle == 2 && Left == 1 && Right == 2 && Judge == 1)
{
PWM = 0;
Left = Right = Judge = 0;
}
else if(Middle == 2 && Left == 2 && Right == 1 && Judge == 1)
{
PWM = 0;
Left = Right = Judge = 0;
}
else if(Middle == 2 && Left == 1 && Right == 1 && Judge == 2)
{
PWM = 0;
Left = Right = Judge = 0;
}
Car_PWM_Channo(1,30);
}
}
else if(Right_D0 == 1 && Left_D0 == 1)//到达某个站点
{
delay(1);//消抖
if(Right_D0 == 1 && Left_D0 == 1)
{
Judge = 2;
if(Judge == 2 && Left == 0 && Right == 0 && Middle == 0)
{
PWM = 0;
address++;
Left = Right = Middle = 1;
}
else if(Judge == 2 && Left == 1 && Right == 2 && Middle == 1)
{
PWM = 0;
Left = Right = Middle = 0;
}
else if(Judge == 2 && Left == 2 && Right == 1 && Middle == 1)
{
PWM = 0;
Left = Right = Middle = 0;
}
else if(Judge == 2 && Left == 1 && Right == 1 && Middle == 2)
{
PWM = 0;
Left = Right = Middle = 0;
}
if(address == seclect)//到达指定房间
{
if(Out == 0)//货物在车身
{
while(Out == 0)
{
L1 = 1;//红灯亮
Car_PWM_Channo(0,100);
}
}
else//货物不在车身
{
L1 = 0;//红灯灭
Car_PWM_Channo(1,30);
}
}
else
{
Car_PWM_Channo(1,30);
}
}
}
}
else if(address >= 4)
{
L0 = 1;//绿灯亮
Car_PWM_Channo(0,100);
address = 4;
}
}
}
}
/*主函数*/
void main()
{
Time_Init();//定时器初始化
usart_Init();//串口初始化
while(1)
{
LED_Show();//LED检测函数
Goods_Checek();//货物检测函数
Usart_Time();//串口复用及超声波探测
Car_Run();//小车行驶函数
}
}
/*定时器0中断函数*/
void Time1_Service() interrupt 1//定时器中断
{
TH0 = (65535 - 500 + 1)/256;//0.1ms定时
TL0 = (65535 - 500 + 1)%256;//0.1ms定时
PWM++;
}
/*串口中断函数*/
void usart_Int( ) interrupt 4//串行中断服务程序
{
if(RI)//检测二维码是否读取完成
{
if(SBUF == '1')//二维码识别到的内容
{
Recave_flag = 1;
seclect = 1;
}
else if(SBUF == '2')//二维码识别到的内容
{
Recave_flag = 1;
seclect = 2;
}
else if(SBUF == '3')//二维码识别到的内容
{
Recave_flag = 1;
seclect = 3;
}
RI = 0;
// while(!TI);
// TI = 0;//未用到发送模式
}
}
三:循迹环PID
关于循迹方面是可以做一个PID算法来进行优化的。具体思路就是依靠两路循迹的0和1组成的(00,10,01,11)四个值来模拟误差。先设置一个期望值,PID算法中KP、KI、KD只需设置KP这一项即可。然后让PID = KP*(期望 - 当前值)即可,再把PID在进行最高输出限幅和最低输出限幅之后最后让输出的PID= PWM即可实现一个简单的闭环控制。加上了PID算法之后能让循迹过程更加丝滑稳定(不加PID就是固定速度输出加了PID之后机器会根据误差情况来调节速度快慢)。
四:成品视频
在文章的最后来分享一段最终成品的视频,喜欢本文的朋友们记得留个赞哦:
酒店服务机器人
遇到问题或者想具体了解的朋友们可以评论区留言,觉得有帮助的话请点个赞多多支持吧!
原创不易,转载请标明出处。