宜昌老张

基于LabVIEW测控的MakeBlock智能车避障控制

0
阅读(3015) 评论(0)

     Makeblock是一个让人们动手去实现自己想法的积木式搭建平台。提供完整的机械,电子和软件的解决方案,用户可以用简单易用的模块快速的搭建出各种机器人,3D打印机,艺术装置,产品原型,小型机械等等。这是金属版“乐高积木”,使用过程像拼积木一样简单,搭建出来的作品不仅仅是玩具,也可以是坚固实用的机器人或者机械装置。

图1 MakeBlock介绍


一、序言

    这次的文章展示了MakeBlock金属积木用于本科毕业设计进行机电一体化产品设计的一个实例,这个实例是由LabVIEW测控下的避障智能车。

图2 MakeBlock智能车实物

   该设计是上位机软件LabVIEW对以Arduino为下位机的智能小车进行控制并且实时监控。采用夏普GP2D12红外线测距传感器实时测量小车运行过程中与前方障碍物的实际距离以提供避障依据。

图3 GP2D12红外线测距传感器

  通常的避障车设计,控制算法由智能车自带的微控制器完成,而这个设计的不同在于控制算法完全由上位PC机这个大脑来完成,下位机通过无线通信的方法上传传感器测距信息和接受小车驱动电机控制命令,智能车上的下位机只是感应器和执行器而已。通过实验,这样的上下位机无线监控的方法,在避障响应上完全能达到预期要求。

对于上位机的LabVIEW软件,要求在控制界面上实现对小车前进、后退、左转和右转的控制,又要在界面上监视小车的运行状态。对于下位机Arduino,必须不断地采集GP2D12红外线测距传感器测量的距离信息并向上位机发送。同时,读取上位机发送的指令,控制小车的运行姿态,实现各种动作。labVIEW前面板如下图。

图4 LabVIEW前面板

图5 实验全景图

实验视频:


二、硬件设计

   该实验在上次实验的基础上,将超声波测距传感器替换为夏普GP2D12红外线测距传感器,采用夏普GP2D12红外线测距传感器实现距离测量。下面对其进行简明介绍。

   夏普GP2D12红外线测距传感器是一种模拟量传感器,其性价比较高。它是由一个红外发射管和一个PSD(Position Sensing Device 位置敏感检测装置)以及相应的计算电路构成。它是一种智能型的传感器,把距离转换为电压值。下图6是GP2D12红外线测距传感器的电压值与它所测定的距离值的关系曲线。

 

图6 夏普GP2D12红外线测距传感器测距曲线

   从图6中,可以看出GP2D12红外线测距传感器的电压值与它所测定的距离值呈现一种非线性的关系。通过对图中曲线的拟合,并考虑到Arduino的模拟量采样命令analogRead()的数据范围是0至1023,可以得到实际距离(distance)与Arduino控制器的采样数据(Val)的近似关系是:

    distance=2547.8/((float)Val*0.49-10.41)-0.42;                                               

   在避障过程中,Arduino周期性读取GP2D12红外线测距传感器的电压值,并将其发送给上位机处理,从而判断是否进行避障策略。

   本实验中,GP2D12红外线测距传感器占用Arduino的0号模拟量接口,只需要把传感器的引线直接插装在XBee传感器V5扩展板的0号模拟量接口上就可以了。

   智能车自带的下位机Arduino控制器有三层电路板层叠而成,底下的板子是Arduino UNO,中间的板子是L298 Shield2A大电流双路直流电机驱动板,最上面的板子是Xbee V5传感器扩展板。这三个板子形成的电控套件,又称为智能车三件套。如图7所示。

图7 智能车三件套


三、下位机软件的整体构架

   Arduino 作为下位机对智能小车整个系统的性能起到了重要的作用。而软件作为硬件的思想,对系统的影响是显而易见的。下位机软件程序要完成对Arduino控制板上各种传感器及功能模块的数据采集,并实时将其反馈给上位机的LabVIEW程序进行处理。同时,必须及时接受上位机发送的控制信号,对电机进行控制。

   系统运行时,Arduino控制器读取上位机发送的字符串命令。该字符串命令的格式为:

“C”+“a/s/d/w/c/x/z”+“M/N”(注:”/“的含义是”或“)

   Arduino控制器检测接受到的字符串,当接收到命令标识符时读取后面的两个字符。第一个字符val[1]实现对小车的手动控制(a/s/d/w/c),或是选择小车自主避障(“x”和“z”),其中,“x”是在自主避障情况下的前进指令,“z”是在该情况下的左转指令,上位机通过一定的算法选择发生“x”或者“z”;第二个字符val[2]控制小车的档位(M是低档,N是高档)。

   自主避障时,上位PC机读取Arduino传感器的值,并按上述的转换算法公式得到实际的距离值。如果距离值大于40cm,说明前方是安全的,命令小车可以放心前进;反之,命令小车左转,躲避障碍物,从而实现自动避障。基于这样的想法,设计并实现了Arduino下位机程序的编写。下图是Arduino程序的流程图,展示了程序的基本思想。

图8 Arduino程序设计框图

Arduino程序:

int E_right =5;               //连接电机1的使能端口到数字接口5
int M_right =4;               //连接电机1的转向端口到数字接口4
int E_left = 6;     //连接电机2的使能端口到数字接口6
int M_left=7;       //连接电机2的转向端口到数字接口7
char val[3];        // 暂存上位机发送的三个字节的指令
int velocity1;      //右电机速度
int velocity2;      //左电机速度
char flag='c';      //定义小车避障状态标志
int GP2D12 = 0;     //把夏普GP2D12红外测距传感器连接在模拟量端口0
int readGP=1027;    //初始化夏普GP2D12红外测距传感器为最大读数
int beishu;         //定义倍数量,调节高低档小车避障转向时间
 
void setup()
{
    Serial.begin(9600);                  //设置串口波特率为9600 pbs
    pinMode(M_right, OUTPUT);            //设置引脚为输出模式
    pinMode(E_right, OUTPUT);
    pinMode(M_left, OUTPUT);
    pinMode(E_left, OUTPUT);
   delay(200);
}
void advance()                          //声明前进子函数
{
    digitalWrite(M_right,HIGH);         //右电机反转
    analogWrite(E_right, velocity1);    //右电机速度调节
    digitalWrite(M_left,LOW);           //左电机正转
    analogWrite(E_left, velocity2);     //左电机速度调节
}
void back()//声明后退子函数
 
{
    digitalWrite(M_right,LOW);              //右电机正转
    analogWrite(E_right,velocity1);         //右电机速度调节
    digitalWrite(M_left,HIGH);              //左电机反转
    analogWrite(E_left,velocity2);          //左电机速度调节
}
void right()                               //声明右转子函数
 
{
    digitalWrite(M_right,LOW);              //右电机正转
    analogWrite(E_right,velocity1);         //右电机速度调节
    digitalWrite(M_left,LOW);               //左电机正转
    analogWrite(E_left,velocity2);          //左电机速度调节
}
void left()//声明左转子函数
 
{
    digitalWrite(M_right,HIGH);             //右电机反转
    analogWrite(E_right,velocity1);         //右电机速度调节
    digitalWrite(M_left,HIGH);              //左电机反转
    analogWrite(E_left,velocity2);          //左电机速度调节
}
void Stop()                                //声明停止子函数
 
{
    digitalWrite(E_right, LOW);            //右电机停
    digitalWrite(E_left, LOW);             //左电机停
}
 
void loop()
{
delay(20);
    //Serial.available()记录了读缓冲区当前还没有被读的字节数目
        if (Serial.available())     //如果读缓冲区的字节没被读取完
       {
       if('C'== Serial.read())     //如果读到的缓冲区字节为'C'
           {
              while(!Serial.available());    //提取数据中的命令字
              val[1]= Serial.read();
 
              while(!Serial.available());    //提取数据中的速度参数
              val[2]= Serial.read();
                             
              if(val[2]=='M')         //如果速度参数为'M',设置为低档
             {
                   velocity1=76;      //写入低档左右电机速度参数
                   velocity2=100;
                   beishu=1;          //设置低档转向时间调节参数
             }
             if(val[2]=='N')          //如果速度参数为'M',设置为高档
            {
                   velocity1=190;     //写入高档左右电机速度参数
                   velocity2=250;
                   beishu=2.5;        //设置高档转向时间调节参数
                           }  
              switch(val[1])          //命令字分析
            {
              case 'a':           //命令字为'a'
                     left();      //左转
                     flag='a';   //设置转向状态标识符为'a'
                     break;
              case 's':           //命令字为's'
                     back();      //后退
                     flag='s';    //设置转向状态标识符为's'
                     break;     
              case 'd':          //命令字为'd'
                     right();    //右转
                     flag='d';  //设置转向状态标识符为'd'
                     break;   
              case 'w':         //命令字为'w'
                     advance(); //前进
                     flag='w';  //设置转向状态标识符为'w'
                     break;
              case 'c':          //命令字为'c'
                     Stop();    //停止
                     flag='c';  //设置转向状态标识符为'c'
                     break;
              case 'x':          //命令字为'x',进入自动避障
                     advance();  //前进
                     flag='w';  //设置转向状态标识符为'w'
                     break;
              case 'z':          //命令字为'z',进入自动避障
                     left();     //左转
                     delay(1000/beishu); //延时一定时间
                     flag='a';     //设置转向状态标识符为'a'
                      break;  
              default:             //其它情况,退出
                      break;
              }
          }
         //读取GP2D12红外测距传感器模拟量数据
         readGP = analogRead(GP2D12);
         Serial.print(readGP,BYTE); //发送GP2D12红外测距传感器模拟量数据
         Serial.print(flag);       //发送小车运行方向状态标识符
         delay(20);                //延时20ms,等待下一循环
        }
}

四、上位机软件的整体构架

    作为一个功能强大的上位机软件,LabVIEW要实现的主要功能一方面是对小车进行前进、后退、左转、右转和停止的控制,这里要求采用手动控制;一方面要实现小车的自主避障,需要LabVIEW实现避障算法,而不是像上次实验只在下位机实现控制算法;另一方面当然是需要对小车的运行状态进行实时监控。基于这样的考虑,编写了下面的LabVIEW的程序代码。

    图9是展示了LabVIEW是如何实现这些功能的。代码分为了几个模块:发送控制指令,读取信息实现避障算法,小车运行方向状态的显示,系统故障自动退出模块以及系统时间显示模块。在如下的文章中,将会详细的说明这个程序。

    图10所示的是LabVIEW发送命令字符串。通过一个事件结构来触发不同的事件,发送运动方向的字符“a/s/d/w/c”;通过一个布尔开关触发条件结构,来发送档位选择字符“M/N”。为了实现对自动避障方案的选择,为障碍物指示灯设置了一个局部变量,遇到障碍物时,通过一个选择函数节点,将字符“z”写入到字符串命令中。

    采用格式化写入字符串函数节点将所有的命令拼接成事先规定的格式的字符串命令写入串口,通过串口发送给下位机的Arduino控制器执行。

   这篇文章LabVIEW向下位机写入命令采用了格式化字符串的处理方式,与我原来文章的方式风格不同,值得借鉴。

 图9 基于红外线测距传感器避障的LabVIEW程序框图

 

图10 LabVIEW发送指令模块

   采用两个VISA读取函数节点来读取串口中缓存的信息。每次读取两个字节的信息。将读取的到的字符串通过截取字符串函数节点得到两个字符。第一个字符判断小车是下位机发送测距信息,通过一定的算法,将字符串信息转换为十进制的实测距离值,把它作为是否向下位机发送避障控制指令的依据。

   第二个字符是下位机Arduino做出方向动作后对其动作进行的实时反馈,LabVIEW通过判断该字符就可以判断小车的运动方向,或者是在手动控制时判断小车是否实施了上位机的控制。具体的程序块如图11所示。

 

 

图11  LabVIEW读取指令及监视程序模块

    在这里为系统添加一个小程序,它是用于记录系统运行时间的,如图12所示。

  

图12  LabVIEW系统运行时间模块

     为了便于操作控制,方便监视小车的运行状态,对LabVIEW前面板控件进行一些调整和修饰,用到了一些GUI的酷件,期望能够得到较好的人机界面。最终的前面板如图4所示。

到这里,一个采用LabVIEW编程的智能小车上位机监控程序就完成了。 

     LabVIEW和Arduino程序可以在这里下载:http://yunpan.cn/QXzIWfnbcDtfq

     从个人公司到行业龙头企业,从单一汽车行业到社会各行各业,机器人正以其不可替代的优越性改变着传统的生产方式。为进一步探究机器人产业高速腾飞的原因,中央电视台《经济半小时》栏目组再次向您展示一场以机器人为主角的新型产业革命……

     这次毕业设计的学生李炜,已经就业到富士康郑州公司,从事非标准自动化设备开发工作,看来机器人正在快速进入工业制造领域,在校公开学生应该看到这样的一个机遇,并为此多准备,多实践!