循迹小车组装全攻略附代码
ANME俱乐部
2023年10月26日 18:05

一.设计思路分析

        通过车身前部的传感器采集路线信息并发送给单片机进行处理和分析,根据车头上传感器和黑线的不同位置关系做出不同的控制命令,将做出的控制命令发送给电机驱动,通过电机驱动最终控制电机转动实现车的行进和转向。整体控制思路如下图所示

二.硬件设计

        1.通过循迹模块检测黑线,检测原理是红外发射管发射光线到路面,红外光遇到白色路面则被反射,接收管接收到反射光,经整形后输出低电平;当红外光遇到黑线时则被吸收,接收管没有接收到反射光,经整形后输出高电平。一套四路循迹模块有6个引脚,其中VCC(+5V),GND为循迹模块供电,另外4个引脚输出对应四个光电对管识别的信号(高电平或低电平,即1或0)

        循迹模块相当于车的眼睛,通过光电对管来检测黑线和车的相对位置,从而通过单片机做出调整。通常情况下光电对管越多,对路面状况的采集越好,可以更精细的控制车,所以我建议使用两个光电循迹模块,即8个光电对管

 

        2.使用和流水灯上完全相同的STC89C52RC单片机来控制小车,将两个循迹模块引出的8条信号线连接到单片机的一组I/O口上,使单片机获知黑线的位置,通过选择语句,使光电对管在回馈不同的信息时,控制两个(组)电机达到不同转速,实现根据黑线的位置来转向。

 

        3.将电池通过稳压模块连接到电机驱动的+12V,GND接口上,电机驱动上的+5V接口可以为单片机和循迹模块等元件提供5V的电源,单片机和循迹模块的GND引脚也连接到电机驱动的GND接口上。

单片机上的4个引脚分别连接电机驱动上的IN1,IN2,IN3,IN4引脚,其中每个引脚像控制流水灯一样控制电机驱动输出端口的高低电平。每个(组)电机的两极连接到电机驱动对应一侧的两个输出端口上,当两个端口都为高电平或都为低电平时,没有电流通过电机,电机不会转动,当其中一个端口为高电平而另一个为低电平时,电机转动,两个引脚的高低电平互换,电机随之反向转动。因此我们通过单片机的四个I/O口控制两个(组)电机。

电机驱动的高电平电压近似等于电池或稳压模块输入电机驱动的电压,通常情况下电机两端的电压越高电机转速越快,但电机的损耗更大,寿命更短,出现故障的概率也更大。

 

 

 

 

 

 

三.程序设计

        1.对赛道上黑线的检测和数据反馈

四路循迹模块的四根信号线接入单片机的一组I/O口后,有较强反射(白色赛道)的情况下传回高电平,即为1,反射较弱(在黑线上或抬离赛道)的情况下传回低电平,即为0。可以以一个十六进制整数的形式读出那一组I/O口的数据。例如0x7f,即为

0111 1111,八个光电对管中最左侧的检测到黑线。

 

        2.通过赛道上的信息控制电机使车循迹转向

        通过选择结构语句,if(){}或switch(){},对循迹模块的信息进行判断,执行与路面情况相对应的电机控制语句,例如

 

代码块
clike
自动换行
复制代码
if(P0==0x7f)            //当P0的状态与0111 1111相同,即最左侧对管检测到黑线时

{

//控制电机使车左转

}

if(P0==0xfe)            //当P0的状态与1111 1110相同,即最右侧对管检测到黑线时

{

//控制电机使车右转

}
复制成功

 

        3.通过PWM控制方法使电机达到不同的转速

        PWM,脉冲宽度调制技术,使我们可以仅通过数字输出(高低电平,0或1)去控制电路中的模拟量(电机的转速,灯的亮度)。

我们使用的51单片机的引脚只能输出数字量,即高低电平,0或1,直接对应控制LED灯或电机也只能使LED灯亮或灭,电机转或者不转。如果控制LED灯以一定频率亮或灭(即闪烁),可以看作控制灯在一个闪烁周期内的平均亮度,当周期逐渐变短,我们能看到灯闪得越来越快,当闪烁周期小于10毫秒时我们无法观察出闪烁,现象即为灯常亮。通过调整一个闪烁周期内亮和灭的持续时间的比例就可以去控制灯的亮度。同理,我们控制电机在一个周期内高电平占整个周期的比例来控制电机的相对转速。

        其中,高电平持续时间占整个周期的时间称为占空比(正占空比)。占空比的直接表现形式即为通过PWM控制电机的转速和一直通电状态下电机的转速之比。我们画一个以电压为Y轴,时间为X轴的二维坐标系,PWM在图像上会以方形的波形出现,这种波形即为方波。通过方波我们可以直观的看出占空比和周期的关系。

        对车轮转速控制就是通过控制单片机输出给驱动的PWM信号的占空比来实现,我们可以用延迟函数控制高电平和低电平的持续时间来实现PWM,也可以用定时器中断来更精准的实现。例如

        整体程序设计思路如下图所示

四.制作与调试

        现在,我们基本理解了整台车的结构和原理,我们可以动手去做车了。元件的连接方法见视频【来自ANME!学习如何制作一辆寻迹小车第二期(组装篇)】,关于元件在车身上的安装,有如下几点注意事项

        1.电路连接牢固,不虚连,不短路,部分接触点可以用焊锡进行加固并增强导电性

        2.元件在车身固定牢固,不会因为碰撞或摩擦松动或掉落,推荐用铜柱,螺丝螺母作为主要安装方式,也可使用热熔胶进行粘接,为防止连接处松动或损坏也可用热熔胶在该处加固补强。

        3.考虑元件在车身上的位置配重,安放元件时要使驱动力具有足够的下压力,并且车的重心最好尽量降低,不要过于偏前或偏后,防止造成推头或甩尾。车身上最重的元件是电池,电池的摆放位置对车的速度和弯道表现会有一定影响。

        4.光电对管最好伸出车头一段距离(10-15cm为宜),为单片机处理路况并操控预留出时间,以免造成转向延迟,光电对管与路面垂直安放并尽量贴近路面可以达到最好的效果。赛道的黑线宽度为2.5cm,两个相邻的光电对管之间距离最好宽于此宽度。

 

        整车的调试

        循迹模块上有电位计(十字型旋钮),以此调节每个光电对管的灵敏度,使其在路面上或黑线上提供正确的信号。

        稳压模块上有一个电位计(一字型旋钮),以此调节其输出电压的大小,调整时断开负载,使用万用表测量输出电压。

        循迹转向的调试是整个调试过程的关键,通过改动每个状态下两个电机的占空比使车以不同的速度,不同的转向半径转向,调整占空比,摸索最快过弯的方法是一个很耗时的过程,也是提升小车成绩最关键的一步。

 

 

        智能车的整个思路和大部分细节基本都讲解完了,从上周末到今天,写这篇攻略陆陆续续花了一个星期,码字不易,喜欢给个赞。制作智能车过程会出现各种各样想象不到的问题,有时候可能会被一个问题卡住好几天,遇到解决不了的问题首先可以去百度一下,自行搜索解决办法,大部分的问题网上都有相关的解决方案,找出解决问题的方法。发现问题,解决问题,这是学习并提升能力的最重要的过程,也是参加科技比赛的意义所在。

                                                                                                                            -- 肇

代码块
clike
自动换行
复制代码
/**************************
* 简易七路循迹小车程序
***************************/
#include "reg52.h" //此文件中定义了单片机的一些特殊功能
#include<intrins.h>
typedef unsigned int u16;
typedef unsigned char u8;
sbit outa=P1^0; //到298n in1 2 3 4
sbit outb=P1^1;
sbit outc=P1^2;
sbit outd=P1^3;
sbit lin3=P2^1;
sbit lin2=P2^2; //寻迹模块1234567
sbit lin1=P2^3;
sbit mid=P2^4;
sbit rin1=P2^5;
sbit rin2=P2^6;
sbit rin3=P2^7;
int a,b,c=0,m,z;
void delay(u16 z)
{
  for(m=z;m>0;m--);
} 
void timeroinit()	   //计时器
{
  TMOD=0x01;
  TH0=(65536-100)/256;
  TL0=(65536-100)%256;
  EA=1;
  ET0=1;
  TR0=1;
}

void zz()		//直行
{   
  a=60;
  b=60; 
}
void lz1()		//左转1
{ 
  a=40;
  b=60;
}
void lz2()		//左转2
{ 
  a=20; 
  b=60;
}
void lz3()		//左转3
{ 
  a=10; 
  b=60;
}
void rz1()		//右转1
{
  
  a=60;
  b=40;
}
void rz2()		//右转2
{
  a=60;
  b=20;
}
void rz3()		//右转3
{
  a=60;
  b=10;
}

void tz()	   //停止
{ 
  
  a=0; b=0;
}
void fz()
{
  outa=0;
  outb=1;
  outc=0;
  outd=1;
}
void timeroinit1()interrupt 1
{
  TH0=(65536-100)/256;
  TL0=(65536-100)%256;
  if(c==100)
  c=0;
  if(c<=a)
  {
    outa=1;
  }
  else
  {
    outa=0;
   };
   if(c<=b)
   {
    outc=1;
   }
   else
   {
     outc=0;
	};
   c++;
}
void main()
{
  int i=0;
  outb=0; outd=0;
  timeroinit();
  while(1)		 //循迹
  { 
    
    if(lin3==0&&lin2==0&&lin1==0&&mid==1&&rin1==0&&rin2==0&&rin3==0)
    zz();	 
    if(lin3==0&&lin2==0&&lin1==1&&mid==0&&rin1==0&&rin2==0&&rin3==0)
	rz1();
	if(lin3==0&&lin2==1&&lin1==0&&mid==0&&rin1==0&&rin2==0&&rin3==0)
	rz2();
	if(lin3==1&&lin2==0&&lin1==0&&mid==0&&rin1==0&&rin2==0&&rin3==0)
	rz3();
	if(lin3==0&&lin2==0&&lin1==1&&mid==1&&rin1==0&&rin2==0&&rin3==0)
	rz2();
   	if(lin3==0&&lin2==0&&lin1==0&&mid==0&&rin1==1&&rin2==0&&rin3==0)
	lz1();
    if(lin3==0&&lin2==0&&lin1==0&&mid==0&&rin1==0&&rin2==1&&rin3==0)
	lz2();
    if(lin3==0&&lin2==0&&lin1==0&&mid==0&&rin1==0&&rin2==0&&rin3==1)
	lz3();
	if(lin3==0&&lin2==0&&lin1==0&&mid==1&&rin1==1&&rin2==0&&rin3==0)
	lz2();
   //终点检测
    if(lin2==1&&mid==1&&rin2==1)
	{
	   if(i<=6)
	   {
          zz();
		  while(lin2==1&&mid==1&&rin2==1);
		  i++;
		};
	   
	    if(i==7)
	    {
	        tz();
			 while(1);
		};
	}	   
   }
}
复制成功