2008-2009-1科技创新3——计算机控制小车走迷宫M02设计报告

1. 概述
2. 项目整体情况
2.1 项目介绍
2.2 完成情况
2.3 自我评价
3. 项目具体说明
3.1 整体结构
3.2 硬件部分
3.2.1 小车
3.2.2 芯片
3.2.3 USB转RS232线
3.2.4 电脑及摄像头
3.3 软件部分
3.3.1 图像处理
3.3.2 迷宫形成的算法
3.3.3 迷宫路径的算法及小车转向程序
3.3.4 单片机程序及串口通信
3.4 系统测试情况
3.5 系统的不足及改进
4. 感受及收获
5. 致谢
6. 参考文献
7. 附录

姓名

班级

学号

分工情况

备注

杨非

F0703034

5070309864

总体组织和设计,图像处理,迷宫算法,单片机程序,串口调试。

组长,下图右二

刘骋昺

F0703034

5070309856

图像处理,迷宫算法,路径算法,串口调试,撰写报告。

下图左二

汪铖杰

F0703034

5070309860

迷宫制作,硬件调试。

下图右一

曾镇城

F0703034

5070309866

迷宫制作,硬件调试,撰写报告。

下图左一

photophoto2

1. 概述

本文档为M02小组于2008-2009学年第一学期参加电院科技创新[3]课程(计算机控制小车走迷宫)的实验报告。电院科技创新[3]是一门很有趣也很具有挑战性的课程,认真地完成它需要花费很多时间去寻找资料进行学习,并且在实验室进行多次调试。本组在学习研究以及实地调试的过程中积累了很多经验,在此基础上撰写了本报告,其中对实验方法和实验过程有较为详细的描述,并且对实验进行了总结。本文档可供参加本课程的同学参考。

2. 项目整体情况

2.1 项目介绍

system
图1 计算机控制小车走迷宫系统示意图[1]
如图1所示,本次科创要求由摄像头(USB接口)实时捕捉迷宫内小车的位置情况,通过USB线传送至电脑里编写的上位机软件,软件通过图像识别找出当前小车的位置信息及迷宫的信息,经过计算,作出控制决策,生成控制信号,并经通讯模块发送至小车。小车上的控制电路对控制信号作出相应反应,驱动电机。具体的系统组成见图2。
xmsm
图2 系统组成与信息流程[1]

2.2 完成情况

我们完成了以下内容:
1、完成了迷宫的制作及摄像头的采购。
2、用LabVIEW软件实现了图像的捕捉及二值化处理。
3、用C++编写了将二值化后的图像转化成迷宫的程序。
4、用C++编写了寻找迷宫最短路径的程序。
5、用C++编写了根据最短路径对小车发出指令的程序。
6、用LabVIEW软件实现了小车位置的动态识别。
7、用LabVIEW软件将上述程序整合到一个图形化的程序中,并制作了虚拟仪器的前面板。
8、进行小车硬件调试,使小车能正确运行自检程序。
9、编写了单片机程序,完成串口通讯调试,使小车能够正确接收指令行走。
10、完成了整个系统的调试,使小车能够基本正确地走出迷宫。

2.3 自我评价

我们组的组员通过自学掌握了LabVIEW和单片机编程的有关知识,独立地编写了各个部分的程序。尽管在单片机变成方面有一些困难,但最终还是想到了一些程序的方法去解决。考虑到我们90%以上的工作都是独立完成,我们认为我们已经完全达到了本次科创课程的要求,我们的小车能够顺利地走出迷宫,另外我们各方面的能力也得到了锻炼。
我们的作品还具有以下特色:
1、对光照的要求不是很苛刻,可以实时改变阈值来解决因光线变化引起的小车识别问题。
2、利用LabVIEW软件编写虚拟仪器程序,拥有图形化的界面。
3、各种算法的独创性:包括图像转化为迷宫的程序、寻找最短路径的程序等。
4、寻找最短路径的算法很稳定,采用宽搜的思想,能找到所有迷宫的最优路径。

3. 项目具体说明

3.1 整体结构

在介绍各部分具体功能和实现之前,让我们首先对整个过程有一个清楚的了解。首先,我们要得到一张迷宫的图像(包含起点和终点标志),通过设置阈值将图像二值化,以此来确定迷宫的起点和终点。再取另一个阈值进行二值化处理,形成一个简化后的迷宫数组。接着根据这个数组和之前获得的起点和终点,求得最短路径。然后才用实时监视小车的位置的方法,对小车发出指令让其前进、左转或是右转。在整个过程中,必须使迷宫和摄像头的相对位置保持不变。图3为系统结构图。


1
图3 计算机控制小车走迷宫系统结构图[1]

3.2 硬件部分

3.2.1 小车

小车分为三个部分,底板部份、车体部份和89S52部份。底板部分负责向小车车体部份的电机提供8V电压,向89S52提供5V工作电压以及转换各种信号。车体部份主要为上层芯片和整体提供支撑,它的电机为小车提供动力前进。89S52部份负责识别收到的信号,并将其转换成电机的控制信号。

3.2.2 芯片

AT89S52是一种低功耗、高性能CMOS8位微控制器,具有8K 在系统可编程Flash 存储器。AT89S52使用Atmel公司高密度非易失性存储器技术制造,与工业80C51产品指令和引脚完全兼容。片上Flash允许程序存储器在系统可编程,亦适于常规编程器。在单芯片上,AT89S52拥有灵巧的8 位CPU和在系统可编程Flash,使得AT89S52为众多嵌入式控制应用系统提供高灵活、超有效的解决方案。AT89S52具有以下标准功能:
8k字节Flash,256字节RAM,32 位I/O 口线,看门狗定时器,2 个数据指针,三个16位定时器/计数器,一个6向量2级中断结构,全双工串行口,片内晶振及时钟电路。另外,AT89S52 可降至0Hz 静态逻辑操作,支持2种软件可选择节电模式。空闲模式下,CPU停止工作,允许RAM、定时器/计数器、串口中断继续工作。掉电保护方式下,RAM内容被保存,振荡器被冻结,单片机一切工作停止,直到下一个中断或硬件复位为止。
其PDIP图如下:
2
图4 AT89S52芯片的PDIP图[1]
另一块芯片是L298N,用它来控制两个电机。
3
图5 L298N芯片[1]
管脚图如下:
4
图6 L298N的管脚图[1]

3.2.3 USB转RS232线

该线将增加电脑一个COM口,对于电脑与小车进行串行通信来说该线是透明的。其USB端接电脑,TxD接无线发送模块的数据端,Vcc与发送模块Vcc相连,GND与发送模块GND连接。

3.2.4 电脑及摄像头

电脑须有两个以上USB口,摄像头为普通USB摄像头。

3.3 软件部分

这一部分包括图像处理、迷宫的形成、迷宫路径的生成、小车转向程序、单片机程序和串口通信。
我们使用的软件是LabVIEW 8.5和C++。其中前者需要安装几个部分:依次安装LabVIEW 8.5,NI.Vision.v8.5.Development.Module和NI.Vision.v8.5.Acquisition.Software(后两个在ftp://ie:ie@ie.sjtu.edu.cn:8021/LabVIEW/NI Soft/上可以下载)。然后安装NI-IMAQ for USB Cameras(http://zone.ni.com/devzone/cda/epd/p/id/5030)。依次安装后,可进入NI Vision Assistant / Acquire Image(USB)测试,如无法采集图像,用文件夹中包含的ImaqDirectShowDll.dll覆盖C:\WINDOWS\system32中同名文件即可。
我们用LabVIEW软件将上述程序整合到一个图形化的程序中,并制作了虚拟仪器的前面板(图7)。
5
图7 软件运行时的界面(前面板,部分)

3.3.1 图像处理

我们首先介绍使用NI Vision拍摄单张图片的方法。打开National Instruments Vision Assistant 8.5,选择Acquire Image,然后在左下方双击选择Acquire Image (USB)即可进行单次或者连续采集了。
取得了一张迷宫的图片之后,就可以将它显示在前面板中,具体实现见图8。其中标有“IMAQ”的图标的作用是创建一个图片变量。为了使程序框图更加清晰,我们把每个小的功能模块用一个“if结构”框起来,并且始终设置为真。

6

图9所示为图像的二值化的程序框图。在我们的程序里,二值化后又加了一次滤波降噪。但是效果不是特别明显,我们建议可以尝试一下先滤波降噪然后再二值化,或者直接省略这个环节。

7

我们需要对入口和出口进行检测。我们依据前人的经验,在小车上贴了个圆,出口出放了一个方块。通过LabVIEW对圆进行检测,即可得到入口的坐标。实际操作中,我们采用了手动选择出口位置(图11)。这一步里面,我们并没有标识小车的头和尾,这也为我们后面编写小车转向程序造成了麻烦,因此我建议必须把小车的头和尾标识出来,分别检测,以确定小车当前的朝向。图10是使用LabVIEW进行圆的检测,然后将小车匹配数(也就是检测到的圆的数目)显示在前面板上。其中10和14是圆的半径的范围。
8
图10 用LabVIEW检测圆(小车)
对于拍的照片来说,迷宫不是它的全部,因此我们需要确定迷宫的范围。在LabVIEW里,我们可以手动地选定一个矩形框,这个函数的返回值是左上角和右下角的横纵坐标共4个值,足以确定迷宫的位置。我们把这一步叫做迷宫的定标,见图11。
9
图11 迷宫的定标(图中还包括出口位置的确定与调用dll)

3.3.2 迷宫形成的算法

这一部分的算法决定了程序对光线的要求是否苛刻,因此一个好的算法很重要。我们的方法是把一个4×4的迷宫转换成9×9的矩阵,每条通路和每条墙都作为一行或一列。我们将图片的长宽9等分,对每一份统计二值化后图像的白点总个数,通过设置的阈值,来判断它是不是达到了墙壁的要求。这是一个基本的思路,有了它以后写出程序应该比较容易,具体请参见附录里construct1文件夹。
有了基本的算法以后,就可以用LabVIEW调用C++编译出的dll了。在函数选板中找到“互连接口”->“库和可执行程序”—>“调用库函数节点”就可以建立一个dll调用节点。设置各种参数并导入dll文件就可以了。cpp文件编译出dll文件的方法就是在VC++6.0中建立工程时选择“Win32 Dynamic-Link Library”,然后把extcode.h,fundtypes.h,platdefines.h加入工程。特别注意的是,数值传递要用指针,二维数组在dll里要初始化(提供维数分配空间)。另外,函数必须事先声明,并且要加入extern "C" _declspec(dllexport),使其能够输出,且名字不变,方便调用。

3.3.3 迷宫路径的算法及小车转向程序

根据迷宫寻找路径涉及到算法的问题,我们采用的是宽度优先搜索(BFS)的方法寻找到一条最短路径。附录里path文件夹里的就是实现这个功能的程序。BFS实现时应当使用队列,程序里的sqT是一个队列的类型,其中x,y为当前点的坐标,pre为前一个位置在队列中的地址。
小车的转向程序在附件里的turning文件夹中。由于时间有限,我们使用了一个简略版本的程序,使小车能够顺利地走出我们自己的迷宫。在我们的程序里,“F”表示向前行进,“L”表示左转弯90度,“R”表示右转弯90度。

3.3.4 单片机程序及串口通信

我们没有单片机程序设计的基础,虽然也借了一些书来看,但还是收获不多,最后我们使用已有的程序进行修改,以符合我们之前程序的要求。
3.3.1节中曾提到,我们并没有确定小车的朝向,因此转弯时不知道它已经转了多少度。为此,我们做了多次的测试,最后决定将转过90度的时间测出来,然后烧进单片机里。也就是说,只要小车收到转弯信号,就立刻转90度。关于小车部分的内容远没有这里提到的那么简单和理想,实际情况曾一度让我们很无奈,详见3.4节。
LabVIEW的串口通信很容易,见图12,只要使用这么几个模块就行了,图中的“0”是小车转向程序发过来的指令,在我们的指令系统中,这个应该是L/R/F的ASCII码,然后将其转为字符,并成字符串发送到单片机里去。10

3.4 系统测试情况

测试中我们遇到了很多的问题,也逐一进行了解决。大致有以下几方面的问题。
1、3.3.2节中介绍的调用dll的方法是我们在网上找到的,函数的声明以
extern "C" _declspec(dllexport) void main(参数)开头,必不可少,否则就会调用失败。另外二维数组也是困扰了我们很长时间的东西。在写参数列表是,二维数组必须写成:p[][320],后面的值不能搞错,也不能不写。
2、小车方面,两个不稳定的三极管应该要剪掉。
3、首先要让小车通过selftest。在这一步里,我们需要学会把程序烧进单片机。
3、小车的车轮与地面摩擦力不足的问题,我们是通过在轮子上绑橡皮筋解决的。根据马达马力的大小和摩擦力的不同,分别绑上不同数量的橡皮筋,使其能够走直线和顺利转弯。
4、接下来就要实现小车与电脑的通信。将USB线的黑线接到小车芯片的GND,白线(TXD)接到芯片的RXD即可。另外两根线不需要接。
5、在使用串口调试助手时,必须将波特率设置正确,使其与单片机程序相符。我们组的单片机程序对应的波特率为1200。
6、测试中我们发现,对小车连续地发送信号会造成不可预知的错误,因此我们选择隔零点几秒发送一个信号,保证运行中的稳定。
7、转弯的时候可以试一下改变两个轮子的转速,外侧的轮子转的快些,这样就可以边转边向前走。

3.5 系统的不足及改进

我们的不足指出主要在于小车的标识和转向程序,由于时间有限,我们仅按照预定的方案在小车上做了一个标记,因此不能检测其车头和车尾。这样我们在对小车发出转向指令时就不知道小车当前的朝向。简便起见,我们设所有的迷宫的拐弯都是90度,然后设置一个时间,让小车一收到转向指令就转90度,并且调节轮子转速使小车走入下一个区域,不再受到同一个转向信号。
改进的方案在3.3.1节也曾提到过,就是使用两个标记分别表示小车的头和尾,实时检测小车的朝向。
另外一点就是迷宫的出口可以改为自动检测。

4. 感受及收获

杨非:
作为这次科技创新的组长,可以说为了这次科创付出了很多,也收获了很多。
这个学期上半学期的时候,我们组只是去听了几次讲座,工作也只是停留在纸上谈兵的阶段。到了期中考核的时候,我们组除了做的一个迷宫,几乎什么实质性的工作都还没有完成。这时很多组已经初步建立了自己的软件部分的框架,我开始感到了事情的紧迫性,召集组员开了会,完成了任务的分配,并初步定了一个时间表。
我和lcb 同学一开始是负责软件的部分,看了上届很多组的报告,我们权衡再三,决定使用labview 作为开发的平台。可这个时候,我们对labview可以说还是一窍不通,手头仅有的资料也就是讲座的课件标程,以及上一届留下的程序。万事开头难,一开始看了他们的程序根本一点也不懂,我们去图书馆借了几本书,对着书和课件学了好几个晚上,可以说终于对这个虚拟仪器的开发平台有了一个初步的了解。凭着这么一点概念和一点勇气,我们一步一步地开始完成我们的程序。开始的图像二值化还算简单,可以凭着imaq 库里的几个函数就搞定了。做到迷宫压缩的时候由于需要把一个320*240的像素矩阵压缩到一个9*9的迷宫矩阵,当时我脑子里迸发出一个比较不错的算法,但这样的所牵涉到的计算工作可就不是图形化编程软件所擅长的了,我们只好用c++来实现了这个算法,并编译成dll 的动态链接库来实现与labview 的调用关系。这个想法是不错,可是实现起来难度可不一般,刚想出来的算法总归会有bug ,由于经验的缺乏,我们一开始根本不知道如何去调试这个库,每次都是在labview 里运行到库里面卡住,这是我们十分焦虑。我们那几天疯狂的在网上论坛和相关的图书里查阅资料,想找到问题的所在,可是这是我们才发现,书上的东西都是互相抄来抄去的,没什么参考价值,这个过程持续了一个星期左右,我们才想到一种调试方法(我们管它叫注释法。。。)终于库函数的调用调试好了。之后的由于有了经验软件部分的工作做的异常顺利,我也感觉自己能熟练的应用labview 进行编程了,还在一些论坛回答了一些网友的问题:)
软件部分结束之后我们去拿了小车,开始硬件的调试与单片机的编程,我也都参与其中。那段时间一有空就往实验室跑,这段过程中也遇到了不少困难,其中最困扰我们的当属串口通讯了。我们要求要把字符串传到单片机里,单片机再根据接受到的字符来控制马达。我们在调试过程中遇到过各种清况,一开始时收不到,之后是收到的不对,再之后是收到的东西对了但小车的反应不对,还有的时候反应对了但听不下来。我们也不知道到底是单片机程序的问题,还是电路板焊接的问题,还是之间通讯设置的问题。抱着无数的疑问我去找了张士文老师,他耐心的听了我的问题,并和我一起一条一条的分析可能存在的错误以及解决的建议,我感觉对我帮助很大,一方面理清了我的思路,让我有了一个解决问题的方向,另一方面对我也是一个很大的鼓舞。结果回去之后我们多少时间就发现了问题的所在,把最后一个问题波特率设置改对以后(这个问题很隐蔽,因为单片机里程序设置波特率是用的16进制表示),我们的小车终于能对串口发出的信息做出正确地反应了!
最后的总体调试,由于我们前面拖得太久,所以时间也很紧张,不过幸亏前期工作做的比较完善,在调试中并没有出现太大的问题,设置了一些颜色阈值与马达转速时间后,我们的小车就可以顺利的走出迷宫了。
现在课程已经结束,在检测中看到小车走出迷宫之后,觉得自己和小组同伴们这半个学期的努力都没有白费。完成了这次科创任务之后,我深感坚持的重要性,困难实在太多了,如果没有我和其他组员们的坚持,也许我们就抄个别人的程序交掉了。我们的团队精神也得到了很大的磨练,组员们都投入了整晚整下午的时间来工作,尤其是lcb 同学,有两次是我们突然有了一些好的想法,然后为了实现算法和我一起去编程到第二天凌晨。通过课程我也学到了很多知识,积累了很多经验,这些知识和经验往往都是书本上学不到而在实践中很有用的。
在这里我要感谢张士文老师和助教们的辛勤工作与悉心指导以及我的组员们的坚持不懈与互相鼓励,这次经历将鼓舞着我在以后的道路上克服一个又一个的困难。

刘骋昺:
当接到这个题目的时候,相信几乎所有人跟我们一样,完全没有头绪。当时我懂得的所有东西,就是用C或者C++进行编程,对于给定的迷宫,通过深搜或者宽搜,找出最短路径来。不懂LabVIEW,不懂单片机,也不懂图像处理,我们急切地盼望讲座的开始。第一次讲座是关于图像处理的,但是我们基本没有听懂——内容太过理论化了,如此复杂的东西绝不可能在1个小时的时间里搞懂,况且这也不是科创的目的。错过了OpenCV讲座,我们选择了使用LabVIEW做可视化的程序。然而,我们也是第一次接触LabVIEW,可能是它功能太强大的缘故,也不太容易上手。于是几周过去了,中期检查的消息出来之前我们还没开工,唯一完成的仅是迷宫的制作。我与组长连续花了几个晚上研究LabVIEW,终于可以算熟悉一些了,能够进行基本的数据操作和图像二值化处理了。接下来,对LabVIEW的不熟悉使我们的进度在好长一段时间里面停滞不前——我们在调用dll上卡住了。又是几个下午和晚上的努力,最后我们通过逐步排除找到了问题的所在,并迅速解决了问题。了解了LabVIEW调用dll的特性以后,我们迅速地完成了迷宫的形成,最短路径的生成。
在做软件部分的时候,我们查阅了大量的资料,学习到了很多知识和他人的经验,这些都是课本无法给予我们的。万事开头难,一旦攻克了一个“技术难关”,我们便会获得一定的成就感,这份成就感就带领我们继续下去。如果说我们比其他组的同学多了什么东西,那一定是勇气和信念——勇气是勇于挑战自我,在自己不懂的领域进行探索的勇气,信念是相信我们小组一定会获得成功的信念。在整个过程中,虽然有几次因为一个始终没能解决的问题而感到无助,但是我始终没有怀疑过我们能够完成此次科创的任务,没有想过我们要不要像有些组一样应付了事。其实回过头看,所有人的起点都是相同的,但是结果却相差很多,这使我体会到了“一份耕耘一分收获”,没有付出,就一定不会有回报。科创做了一个学期,回头看,总结如下:我们的过程是崎岖的,但结果是令人鼓舞的。

汪铖杰:
在本次科创课程完成过程中,我主要负责迷宫制作和小车调试部分的工作。在工作过程中也感到了一点乐趣,当然也有一定的收获。
迷宫制作的过程相对而言略有些枯燥,毕竟没有什么太大的技术含量。为了增加在迷宫制作方面的乐趣,我在迷宫的设计方面花了一定时间,设计了一个既能符合小车走出路径规格(迷宫大小相对合理,路径复杂度适中)又赋有一定意义(迷宫墙组成了我的名字拼音首字母的缩写CJ·W)的迷宫。
相对迷宫制作的过程,在小车硬件调试方面则显的复杂,甚至有时会让人觉得有些迷茫。由于本组领小车较晚,领来的小车是新类型的小车。而课程网上所带的小车相关文档及向芯片输入程序方法都是关于旧小车,这无疑给小车的调试带来了不小的额外难度。在第一次将selftest程序烧入芯片时遇到了较大的困难,由于新小车芯片程序输入过程需要使用ALL-11程序编制器,而我们对该仪器的使用方法完全不懂。在助教的帮助下我们基本掌握了输入程序的方法,并经过后来的一些调整,解决了小车的接触问题,完成了小车的第一步调试。
后来遇到的又一个问题是USB线的连接问题,由于新旧小车的差别,在USB的连线方面有较大差别(我曾比较过旧车和新车,在旧车的电路板上有GOUND和RXD的接线端,而新小车则没有)。为了解决这个问题,我们从网上搜来了芯片的管脚图,“完成”了USB线的连接。不过在后面的调试过程中发现,最初的USB线连接是有问题的,在经过调整后完成了正确连接。
最后的一个问题就是小车左轮和右轮的转速不对称和摩擦力问题,经过多次调试(利用橡皮筋捆绑轮子的方法来调节),使小车基本完成了向前向后左转右转的基本行进。
很幸运的是我们组的小车最后顺利的走出了迷宫,这是我们团队合作的成功,当然也离不开老师和助教的帮助。

曾镇城:
这次科创[3]和科创[1]有很大的区别,不仅难度加大,而且是以小组的形式共同完成,让我们体会到团队合作精神的重要性。虽然自己在本次活动中不是承担核心任务,但是也在和同伴的交流学习中得到很多的东西。如在讨论用哪种编程方法时,我也参阅了一些Matlab的书籍,感觉受益匪浅。另外,小车的焊接工作将科创[1]的知识学以致用,让我倍感兴奋。通过这次活动,我的动手能力得到很大的提高,并且进一步加强了自学能力。

5. 致谢

在本课程实验中,我们特别致谢以下单位和个人:
上海交通大学电子信息与电气工程学院电工电子实验中心
科技创新[3]张士文老师
各位给予帮助的助教和所有帮助我们的同学

6. 参考文献

[1] 科技创新3网站:http://eelab.sjtu.edu.cn/Course/course/view.php?id=8
[2] LabVIEW程序设计教程,江建军、刘继光等,电子工业出版社,2008
[3] 科技创新3设计报告.doc,2008年的M08组,杨晓光等。我们从中学习了如何使用LabVIEW实现串口通信。
[4] 2008年的M20组和M22组——我们从中学习了单片机程序的写法。

7. 附录

小车走迷宫视频

详细系统使用说明

电脑软件程序源代码及可执行文件

单片机程序源代码及hex文件

系统电路图

完整版的设计报告