最短路寻线智能小车(智能出租车)

前几周和舍友一起参加学院的电子设计大赛。这是为我们的小车写的报告。这是我第一次比较规范的写代码,做点有项目性质的东西,也是第一次做单片机上的编程,整体比较粗糙。高手看了别见笑。如果有什么错误欢迎大家指出来,交流使人进步!

一.系统方案选择与论证

1.1.设计要求

1.1.1.任务

设计制作一个智能小车模型,在一份已知地图上从指定起点出发通过计算最短路径选择最佳路线到达终点。每个节点为一个“十”字交叉路口。

1.1.2.要求

根据任务要求我们为自己的设计确定了以下目标:

基本部分:

  1. 计算从起点到终点的最短路径。
  2. 在每个节点通过转向选择一条路线到达下一个节点。
  3. 从一个节点到下一个节点进行寻线操作。
  4. 到达一个节点能够自动停车。

1.2.系统方案对比论证

1.2.1.系统总体方案设计论证

本系统大体上分为四个基本模块,它们的关系可由如下方框图示意

1.2.2.各模块方案选择与论证

1.2.2.1.车体设计

方案 1:自己制作电动小车。但自己制作的车体比较粗糙,对于寻线小车,车身重量以及平衡都要有精确的测量,而且也要控制好小车行驶的路线和转弯的力矩及角度,这些都比较难良好地实现。

方案 2:购买具有组装完整的车架车轮。使用两轮驱动并配置万向轮,选用转轴力矩大的减速电机来提供动力及精确调节转弯。 成品的车架车轮由于装配紧凑,使得各种所需电路的安装十分方便,看起来也比较美观。而且这种电动车一般都价格适中。

基于以上分析,我们选择方案2。

1.2.2.2.控制器模块(单片机的选择)

单片机控制模块在本系统中处于核心地位。其工作包括处理键盘输入、显示模块控制、响应传感器、控制电机运行等。对单片机控制模块的基本要求是具有较高的速度、资源配置满足要求。

方案 1:采用ATMEL 公司生产的 AT89C52 单片机。由于存储地图信息所需的空间较大,该单片机仅有256字节RAM,难以满足要求。

方案 2:采用宏晶科技公司生产的 STC89C52RC 作为主控制芯片,该芯片有足够的存储空间,可以方便的在线SP 下载程序,能够满足该系统软件的需要,该芯片内衣内置了256字节外部存储器,对于本作品系统已经足够,采用该芯片可以比较灵活的选择各个模块控制芯片,能够准确的计算出时间 ,有很好的实时性。

基于以上分析,我们选择方案2。

1.2.2.3.电机驱动模块

电机驱动模块是本系统的执行机构,用于控制小车的运动。因为本系统中未设置位置传感器,系统对位置的定位完全靠软件来实现,这就要求电机的控制精度必须要高。

方案 1:使用多个功率放大器驱动电机。通过使用不同的放大电路和不同参数的器件,可以达到不同的放大要求,放大后能够得到较大的功率。但由于放大电路很难做到完全一致,当电机的功率较大时运行起来会不稳定,而且电路的制作会比较复杂。因此也没有选择这种方案。

方案 2:采用电机驱动芯片 L298N。L298N 为单块集成电路,高电压,高电流,四通道驱动,可直接的对电机进行控制,无须隔离电路。通过单片机的 I/O 输入改变芯片控制端的电平 , 即可以对电机进行正反转 , 停止的操作 , 非常方便 , 亦能满足直流减速电机的大电流要求。调试时在依照下表,用程序输入对应的码值,能够实现对应的动作。

电机转动状态编码:

基于以上分析,我们选择了方案2,用 L298N 来做为电机的驱动芯片。

1.2.2.4.寻线传感器模块

寻线我们采用采 TCRT5000 光电传感器模块。TCRT5000 传感器采用高发射功率红外光电二极管和高灵敏度光电晶体管组成,输出信号经施密特电路整形,稳定可靠。

电路原理图:

1.2.2.5.显示模块

显示部分我们采用 LCD1602A 液晶模块。高模块可以显示2×16个字符,工作电压为4.5~5.5V。一下为借口信号说明:

1.2.2.6.电源模块

在本系统中,需要用到的电源有单片机的 5V,L298 芯片的电源 5V 和电机电源。因为 L298N 芯片和电机的功耗较大,为了系统稳定,这里对单片机进行单独供电。整个系统采用2个4.5V的电池组供电。

二.项目的具体设计及实现

2.1.驱动部分

2.1.1.驱动模块硬件设计

L298引脚图

驱动模块与单片机接线图

2.1.2.小车状态控制代码

//电机运行状态函数
#define stopLeft() {in1=in2=1;}//左电机停止
#define transferLeft() {in1=0;in2=1;}//左电机正转
#define reverseLeft() {in1=1;in2=0;}//左电机反转;下同
#define stopRight() {in3=in4=1;}
#define transferRight() {in3=0;in4=1;}
#define reverseRight() {in3=1;in4=0;}
 
//小车运行状态函数
//小车停止
void stop() {
	stopLeft();
	stopRight();
}
 
//小车前进
void advance() {
	transferLeft();
	transferRight();
}
 
//小车后退
void reverse() {
	reverseLeft();
	reverseRight();
}
//小车左转
void turnLeft() {
	reverseLeft();
	transferRight();
}
 
//小车右转
void turnRight() {
	reverseRight();
	transferLeft();
}

2.2.检测部分

2.2.1.检测部分原理

这一部分是车身控制和功能实现部分的基础,单片机通过对传感器信号的处理来控制电机正转、反转或不转,从而控制小车前进、左转、右转等动作,基本原理可用下图来表示

2.2.2.寻线操作主要代码

uchar inLine() {
	while(1) {
		if(blackLineLeft==onBlackLine && blackLineRight==onWhite) {
			turnLeft();
			while(blackLineLeft==onBlackLine) {
				if(blackLineRight==onBlackLine) {
					stop();
					delay0_1(2);
					break;
				}
			}
		}else if(blackLineLeft==onWhite && blackLineRight==onBlackLine) {
			turnRight();
			while(blackLineRight==onBlackLine) {
				if(blackLineLeft==onBlackLine) {
					stop();
					delay0_1(2);
					break;
				}
			}	  
		}else if(blackLineLeft==onBlackLine && blackLineRight==onBlackLine){
			stop();
			delay0_1(2);
			break;
			//运气非常好的时候才会进这个分支	
		}else{
			advance();
		}
	}
	return ok;
}

2.2.3.节点处旋转代码

//小车在节点左转90度
void left90Deg() {
	stopLeft();
	transferRight();
	while(blackLineRight==onBlackLine);
	while(blackLineRight==onWhite);
	while(blackLineRight==onBlackLine);
	while(blackLineLeft==onBlackLine||blackLineRight==onWhite) turnLeft();
	stop();
}
 
//小车在节点右转90度
void right90Deg() {
	stopRight();
	transferLeft();
	while(blackLineLeft==onBlackLine);
	while(blackLineLeft==onWhite);
	while(blackLineLeft==onBlackLine);
	while(blackLineRight==onBlackLine||blackLineLeft==onWhite) turnRight();
	stop();
}

2.3.最短路计算部分

2.3.1.地图信息在单片机中的存储

在单片机中如下一份地图使用两个矩阵邻接表表示:

两节点间的数字代表权值,及两点距离(图片并没有精确绘制,仅代表节点间的关系)。

//两点距离矩阵
uint idata dist[MAX][MAX]={
	  {  0,inf, 50, 11,inf, 17,inf,inf, 31},
	  {inf,  0, 15, 14,  8,inf,inf,inf,inf},
	  { 50, 15,  0,inf, 12,inf,inf, 30,inf},
	  { 11, 14,inf,  0,inf,  7, 20,inf,inf},
	  {inf,  8, 12,inf,  0,inf,  7, 13,inf},
	  { 17,inf,inf,  7,inf,  0, 32,inf,  7},
	  {inf,inf,inf, 20,  7, 32,  0, 11,inf},
	  {inf,inf, 30,inf, 13,inf, 11,  0, 60},
	  { 31,inf,inf,inf,inf,  7,inf, 60,  0}
};
 
//每段起点方向矩阵,方向取反就是终点方向矩阵
uchar code idx[MAX][MAX]={
	  {0,0,2,1,0,4,0,0,3},
	  {0,0,2,3,1,0,0,0,0},
	  {2,3,0,0,4,0,0,1,0},
	  {3,2,0,0,0,4,1,0,0},
	  {0,3,2,0,0,0,4,1,0},
	  {3,0,0,2,0,0,1,0,4},
	  {0,0,0,3,2,4,0,1,0},
	  {0,0,2,0,3,0,4,0,1},
	  {3,0,0,0,0,2,0,1,0},
};

2.4.Dijkstra算法

2.4.1.Dijkstra算法简介

Dijkstra是由荷兰计算机科学家艾兹赫尔·戴克斯特拉(Edsger Wybe Dijkstra)发明的。算法解决的是有向图中单个源点到其他顶点的最短路径问题。举例来说,如果图中的顶点表示城市,而边上的权重表示著城市间开车行经的距离,该算法可以用来找到两个城市之间的最短路径。

该算法的输入包含了一个有权重的有向图 G,以及G中的一个来源顶点 S。我们以 V 表示 G 中所有顶点的集合。每一个图中的边,都是两个顶点所形成的有序元素对。(u, v) 表示从顶点 u 到 v 有路径相连。我们以 E 所有边的集合,而边的权重则由权重函数 w: E → [0, ∞] 定义。因此,w(u, v) 就是从顶点 u 到顶点 v 的非负花费值(cost)。边的花费可以想像成两个顶点之间的距离。任两点间路径的花费值,就是该路径上所有边的花费值总和。已知有 V 中有顶点 s 及 t,Dijkstra 算法可以找到 s 到 t 的最低花费路径(例如,最短路径)。这个算法也可以在一个图中,找到从一个顶点 s 到任何其他顶点的最短路径。
这个算法是通过为每个顶点 v 保留目前为止所找到的从s到v的最短路径来工作的。初始时,原点 s 的路径长度值被赋为 0 (d[s] = 0),同时把所有其他顶点的路径长度设为无穷大,即表示我们不知道任何通向这些顶点的路径(对于 V 中所有顶点 v 除 s 外d[v] = ∞)。当算法结束时,d[v] 中储存的便是从 s 到 v 的最短路径,或者如果路径不存在的话是无穷大。 Dijkstra 算法的基础操作是边的拓展:如果存在一条从 u 到 v 的边,那么从 s 到 v 的最短路径可以通过将边(u, v)添加到尾部来拓展一条从 s 到 u 的路径。这条路径的长度是 d[u] + w(u, v)。如果这个值比目前已知的 d[v] 的值要小,我们可以用新值来替代当前 d[v] 中的值。拓展边的操作一直执行到所有的 d[v] 都代表从 s 到 v 最短路径的花费。这个算法经过组织因而当 d[u] 达到它最终的值的时候每条边(u, v)都只被拓展一次。算法维护两个顶点集 S 和 Q。集合 S 保留了我们已知的所有 d[v] 的值已经是最短路径的值顶点,而集合 Q 则保留其他所有顶点。集合S初始状态为空,而后每一步都有一个顶点从 Q 移动到 S。这个被选择的顶点是 Q 中拥有最小的 d[u] 值的顶点。当一个顶点 u 从 Q 中转移到了 S 中,算法对每条外接边 (u, v) 进行拓展。

(以上关于Dijkstra算法简介来自《维基百科》)

2.4.2.Dijkstra算法在单片机上的实现

uchar idata nodeNext[MAX],pointIfUsed[MAX];
uchar findMinEdgePoint()
{
  uchar t=MAX+1;
  for(i=0;i<MAX;++i) {
	  if(pointIfUsed[i]==notUsed) {
		  if(t==MAX+1||shortPath[t]>shortPath[i]) {
			  t=i;	 
		  }
	  }
  }
  return t;
}
 
void dijkstra()
{
  for(i=0;i<MAX;++i) {
	  shortPath[i]=dist[end][i];
	  nodeNext[i]=end;
  }
  for(i=0;i<MAX;++i) {
	  pointIfUsed[i]=notUsed;
  }
  pointIfUsed[end]=used;
  while(1) {
	  uchar t=findMinEdgePoint();
	  if(t==MAX+1) break;
	  pointIfUsed[t]=used;
	  for(i=0;i<MAX;++i) {
		  if(pointIfUsed[i]==notUsed) {
			  if(shortPath[t]+dist[t][i]<shortPath[i]) {
				  nodeNext[i]=t;
				  shortPath[i]=shortPath[t]+dist[t][i]; 
			  }
		  }
	  }
  }
}

三.设计文件

3.1.电路图

面积比较大,保存后再看吧

3.2.源代码

本系统源代码由以下6个文件组成

完整的源代码比较长,这里提供下载就不贴出来了

下载

四.测试

4.1.测试数据

通过在牛皮纸上用黑胶布绘制图一所示地图,使小车在该地图上测试,得到如下数据。

4.2.数据分析

有时没有相关传感器测量实际转过角度多少,仅通过循迹传感器统计扫过黑线数量,在需要旋转180度是容易出现问题,导致部分测试失败。

结束语

经过为期十四天的设计,感触颇深的是解决问题的方法、技巧。在这十四天中,我们遇到许多问题,对待问题要多方法处理,多角度处理。通过这几天的实践,我们不但增强了实践能力和协作精神,而且懂得了联系实际的重要性,这对我们以后的学习和工作不无裨益。当然,我们的设计还存在着一些缺陷,有待于在将来的设计中进一步提高,在此恳请各位老师批评指正。

» 本博客采用署名 2.5 中国大陆许可协议进行许可,本文版权归作者所有,欢迎转载,但必须在明显位置给出原文连接。
anyShare分享到:

Leave a Comment

NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>