本帖最后由 电赛小编 于 2019-4-11 17:41 编辑
从老师那里得知有这个活动,可以申请学习,没想到真的选上了,非常的高兴能够用这个机会,真的学到了很多东西,现在经过多天折腾终于出了一些成果,实现了预期的基本功能,每一个传感器的探索突破都让我甚是欢喜,感觉自己学的专业好神奇。MSP430的改进版MSP432与之相比在功能上更强大,是EE学生必须掌握的单片机之一。而与之对应的是CCS软件,在这次活动之前,我们团队3人还没有接触过CCS,但是这次活动让我从零开始,TI的课程非常良心,一步一步循序渐进的教我们如何使用CCS软件来对芯片进行编程。在实验操作中,我们也逐步熟悉了这套软件,更是学习到了很多课上学不到的东西。这套器件超赞,TI的这次活动超赞,负责的**姐也超赞。不再多扯,来点详细的干货吧!
成果视频链接:http://www.bilibili.com/video/av45580923?share_medium=android&share_source=copy_link&bbid=XYF6E2EFC491378016967212B3CEB8CBFBD9D&ts=1551942128850
1. 硬件组装零件的组装对于整个迷宫挑战小车来说是至关重要的一部分,因为稍有不慎,硬件连接错误,会导致整个机器在学习和调试的过程中,受到很大的影响。在我们组装过程中,按照说明指导书,一步一步实现硬件的组装,学习到了各个传感器和驱动装置的硬件连接方法,同样也发现并感慨TI公司在小车设计排版方面的巧妙。尽管已经很认真仔细的组装小车的每一个部分,但是最后调试的时候还是出现了一些小问题,在安装小车右车轮时,因为小车右轮电机的转子并没有调整好插入轮毂的距离,导致调试的时候轮子会晃动,无法走直线,但在我们重新调整完转子之后,完成了正常任务。
2. 碰撞传感器的学习小车的碰撞传感器就像是猫咪的胡须,它可以摸到前方有没有障碍物,在遇到障碍物时可以让小车做响应的路径调整。由于第一次使用TI-launchpad MSP432,所以最初的探索是从各个功能模块的初始化开始的,但经过不久的摸索发现掌握其基本使用方法还是比较容易的。 RSLK小车的车头装有6个碰撞传感器,连接到TI-launchpad MSP432的P4端口上的其中6位,在碰撞开关没有被按下时传回高电平信号,被按下之后传回低电平信号,进而通过程序控制电机转换工作模式。而要使用P4口作为输入,需要先进行定义。先将P4口的SEL0/SEL1寄存单元置0,定义其为GPIO口,而后使其DIR单元置0定义它为输入口,并且需要激活该端口的内部上拉电阻,保证在没有触发碰撞开关时端口电平为高电平,至此该端口初始化完成,作为碰撞传感器的初始化程序。在主程序中调用碰撞传感器的初始化函数之后,只要调用含有读取P4口状态语句的函数就可以得到传感器传回的实时信息,进而处理使用。 过程 在研究这个碰撞传感器时,我们对CCS这个环境的调试窗口还不够熟悉,不知道到底有没有返回信号,这是一个很实际的问题。后来在学习视频了解到这款Launchpad上设置了强大的调试接口,只要连接上电脑在CCS中下载程序就可以进行逐步的调试,非常的方便,可以看到每一步语句执行后每一个寄存器和I\O口的变化,从刚开始太懂P4口的输入寄存器为何变化到后来可以利用输入的高低电平,我们掌握了通过CCS在线调试的方法和碰撞传感器的使用方法。 3. 循迹传感器的学习循迹传感器,是迷宫小车的眼睛,也是迷宫循迹中小车最主要的传感器,可以说它的作用非常巨大。在刚开始学习循迹传感器的时候,有些迷茫,一直对于初始化端口的概念模糊不清,于是我们看着教学的课程PPT还有结合课后提供的材料一步一步学会了寻线传感器的初始化。这里重要的是对于P7和P5.3的初始化需要注意顺序和延时时间,因为会有充电的问题。当把传感器正常激活后,我们按照相应的逻辑编写程序,并通过Expressions实时在线看到传感器下的Data值,来调节相应的程序,最后实验Lob6的测试。在后期的迷宫整合中,我们对于传感器的偏移量进行了相应的PID控制方法,使得小车有更好的稳定性,并在线上稳定行驶。对于识别的算法下文会有相应的介绍。 4. 电机的PWM控制 电机可以说是小车的双腿,怎么走,走多快,都是我们要花心思考虑的问题。而在该闭环的控制系中,需要软件编程运用算法设计控制器实现对执行元件即电机的控制。所以控制算法直接影响到电机的性能。本次使用的小车上装有两个直流减速电机,对于直流电机,微控制系统中最常用的调速方法即为PWM控制——通过脉宽调制来改变直流电机两端的实际电压,进而对电机实现调速控制。 使用TI-Launchpad MSP432生成PWM波需要使用其中的定时器,由于定时器时钟频率非常高,所以要对定时器信号进行分频处理,得到需要的标准低频信号,使用时只需要改变其中的参数即可。MSP432中有四个定时器,分别为TA0、TA1、TA2、TA3,每个定时器都可以输出4个同频率但不同占空比的信号,因此我们使用TA0即可,电机驱动模块的使能端被接在了定时器TA0的3、4口上(即P2.6、P2.7),通过对TA0的第0口的相关寄存器进行设定,控制PWM波的周期,通过对3、4口相关寄存器的设定,控制输出PWM波的占空比。 电机驱动模块上每个电机分配了3个控制端:使能端、方向控制端、休眠控制端,对定时器进行初始化之后,该PWM波就开始在**不断地产生,给到驱动模块的使能端,只要休眠控制端给高电平,电机就会转圈,在程序中改变县级速度时,只需要调用改变3、4口相关寄存器参数的函数即可。 过程 虽然原理比较简单,但是在探索过程中还是遇到了不少困难,因为它不同于51中的定时器,频率高、功能强大、寄存器复杂,单纯对定时器的初始化定义就花了很久的功夫,好在TI在论坛中给这个套件准备的学习视频中有详细的讲解,并且在课件中有实例的程序,稍加改动之后,我们就可以方便的调用。 解决了电机的控制,接下来要解决的就是路线问题了。 5. 走循迹迷宫的算法实现RSLK套件提供的寻迹传感器共有八个,分别连接P7.0至P7.7。在走迷宫时八个寻迹传感器可以提供更多的信息来决策。走迷宫和循迹的难度差别较大,对算法要求较高。经过小组思考与讨论,我们总结了需要解决的几个关键问题,并提出了解决办法,如果有更好的解决办法,希望大家提出。 要想实现迷宫巡线,就必须了解迷宫的结构。迷宫巡线时要面临以下几种情况如图所示;其中a,b两种无需多说,是迷宫中最简单的路口,只需两边传感器有一边接收到信号并转弯即可,但此时程序写起来并不能按照循迹来写。如果读者学习过《自动控制原理》这门课,便可以了解到按照循迹来写相当于给了这个系统一个很大的干扰,系统回到稳定状态需要的时间比较长,如果不能在下一个路口到来之前快速达到稳态,很可能会影响下一个路口的判断与动作,另外如果连续几个弯道还会造成误差的积累。导致超过系统的调节幅度。
c、d两种形式的路口相比a,b更复杂一些,对路口判断也较为复杂。该分支在调试时也最容易出现问题,特别是小车驶入图中横向的线路时,如果小车在到达路口时左偏或者右偏很可能导致误判,从而走错道路。 e图为十字路口,难度高于前两种,遍历此路口的所有可能也较为困难。而且在考虑到这种路口的时候,有的读者可能会发现此时选择哪个分支前进也很重要。这就涉及到逻辑问题了。 迷宫实现过程 ① 一开始我们拿到小车实现了传感器的功能后,可以实现基本的循迹。此时用的比较基础的方法,即左偏右拐,右偏左拐。但随后我们发现小车与轨迹偏差值会发散震荡,也就是表现为小车在多次左右摇摆之后冲出了跑道。结合自控原理知识,我们降低了小车拐弯的速度,小车效果变好,可以完成循迹。 ② 接着我们开始测试直角弯a,b,发现小车偶尔在做出反应之前已经通过了直角弯。所以我们增加了一行程序,当小车左右两边传感器感应到跑道后,旋转90度再继续前进,但由于在走直线时出现的晃动,再加上牛眼轮在地板上出现惯性导致的滑动,偶尔出现脱离跑道的现象。此时我们认识到简单的程序不太容易实现循迹迷宫,因此我们加入了PID算法来计算小车两轮的差速并对1部分的循迹程序进行修改。使得小车可以完成直角弯。并尽量减少了拐弯后小车的晃动幅度。 ③ 在对c,d,e三种情况进行思考时我们发现必须对各个路口进行优先级的确定,以便在路口选择合适的一条路。所以我们首先定义右拐为最高优先级,直行次之,左拐最低。通过不断地右拐可以遍历所有可能的路径。但此时我们又遇到了新的问题:小车需要对a,c或b,d进行识别和判断,也需要对c,d,e,f进行识别,所以我们在这些路口进行了多次间隔10ms的检测并综合三次检测的返回数据来确定路口的形状,虽然这样检测给小车带来的晃动更大,但是也实现了我们的预期目标。 ④ 除此之外我们还需要考虑死路时的返回,我们采取的方式是转体180度。效果也不错。 ⑤ 综上,循迹迷宫需要建立在循迹效果较好的基础上,PID调速可以很好地解决这个问题,相对于单纯的二值调速系统,PID在调节速度与调节效果上都是更加优秀的。
总结 初出茅庐,还请大佬们指导,我们写的也非常初级,基本上是小打小闹,但我已经很开心了,下面附上我们的主程序,大家感兴趣的话可以下载试一试,应该是可以用的! - #include <stdint.h>
- #include "msp.h"
- #include "..\inc\Bump.h"
- #include "..\inc\Clock.h"
- //#include "..\inc\SysTick.h"
- #include "..\inc\CortexM.h"
- #include "..\inc\LaunchPad.h"
- #include "..\inc\Motor.h"
- #include"..\inc\PWM.h"
- #include"..\inc\Reflectance.h"
- void TimedPause(uint32_t time){
- Clock_Delay1ms(time); // run for a while and stop
- Motor_Stop();
- //while(LaunchPad_Input()==0); // wait for touch
- //while(LaunchPad_Input()); // wait for release
- }
- //reflact
- uint8_t Data;
- int32_t position;
- void reflactance(void){
- Data = Reflectance_Read(1000);
- position = Reflectance_Position(Data);
- if(position==0){
- Motor_Forward(4000,3900);
- }
- else if(position>=237){
- Motor_Stop();
- TimedPause(100);
- Motor_Forward(2000,7000);
- TimedPause(100);
- }
- else if(position<=-237){
- Motor_Stop();
- TimedPause(100);
- Motor_Forward(7000,2000);
- TimedPause(100);
- }
- else if((position>0)&&(position<237)){
- Motor_Forward((3000-8*position),3700);
- }
- //else if((Data == 0x30)||(Data == 0x60)||(Data == 0xC0)){
- else if((position <0)&&(position>-237)){
- Motor_Forward(3700,(3000+8*position));
- }
- if(Data == 0xFF){//T型路口
- Motor_Forward(4000,3900);
- TimedPause(200);
- Motor_Right(4000,3900);
- TimedPause(350);}
- if((Data == 0xF8)||(Data == 0xF0)||(Data == 0xE0)){//直角右转
- Data = Reflectance_Read(1000);
- TimedPause(10);
- if((Data == 0xF8)||(Data == 0xF0)||(Data == 0xE0)){
- Data = Reflectance_Read(1000);
- TimedPause(10);
- if((Data == 0xF8)||(Data == 0xF0)||(Data == 0xE0)){
- Motor_Forward(4000,3900);
- TimedPause(150);
- Motor_Right(4000,3900);
- TimedPause(350);}
- else;}
- else;}
- else;
- if((Data == 0x1F)||(Data == 0x0F)||(Data == 0x07)){//直角左转
- Data = Reflectance_Read(1000);
- TimedPause(5);
- if((Data == 0x1F)||(Data == 0x0F)||(Data == 0x07)){
- TimedPause(5);
- Data = Reflectance_Read(1000);
- if((Data == 0x1F)||(Data == 0x0F)||(Data == 0x07)){
- Motor_Forward(4000,3900);
- TimedPause(150);
- Data = Reflectance_Read(1000);
- if(Data == 0x00){
- Motor_Left(4000,3900);
- TimedPause(400); }
- else Motor_Forward(4000,3900);
- }
- else;}
- else;}
- //else;
- else if(Data == 0x00){
- Motor_Right(4000,3900);
- }
- }
- void endstop(void){
- Data = Reflectance_Read(1000);
- if(Data == 0xDB){
- Motor_Stop();
- while(LaunchPad_Input()==0); // wait for touch
- while(LaunchPad_Input());
- }
- }
- //bump and driver
- void bumprun(void){
- uint8_t numflag = 0;
- numflag = Bump_Read();
- if(numflag == 1){
- TimedPause(500);
- Motor_Left(4000,6000);
- TimedPause(500);//SysTick_Wait10ms(30);
- }
- else if(numflag == 3){
- TimedPause(500);
- Motor_Right(4000,6000);
- TimedPause(500);// SysTick_Wait10ms(30);
- }
- else if(numflag == 2){
- TimedPause(500);
- Motor_Backward(4000,4000);
- TimedPause(500);// SysTick_Wait10ms(30);
- Motor_Right(4000,6000);
- TimedPause(500);// SysTick_Wait10ms(30);
- }
- else if(numflag == 0)
- Motor_Forward(4000,4000);
- }
- void bumprun1(void){
- uint8_t numflag1 = 0;
- numflag1 = Bump_Read1();
- if ((numflag1 & 0xED)!=0xED){
- Motor_Backward(4000,4000);
- TimedPause(300);// SysTick_Wait10ms(30);
- Motor_Right(4000,3900);
- TimedPause(400);// SysTick_Wait10ms(30);
- }
- }
- int main(void){
- Clock_Init48MHz();
- LaunchPad_Init(); // built-in switches and LEDs
- Bump_Init(); // bump switches
- Motor_Init(); // your function
- PWM_Init34(10000, 5000, 7000);
- Reflectance_Init();
- while(LaunchPad_Input()==0); // wait for touch
- while(LaunchPad_Input()); // wait for release
- // write a main program that uses PWM to move the robot
- // like Program13_1, but uses TimerA1 to periodically
- // check the bump switches, stopping the robot on a collision
- while(1){
- reflactance();
- bumprun1();
- endstop();
- }
- }
复制代码
|