本次智能车项目设计的硬件平台为树莓派4B model、驱动电桥和带有四个马达电机的小车。使用的传感器有红外测距传感器灰度传感器摄像头。项目软件开发主要使用C++语言,以及OpenCV相关库。

​ 项目主要完成的功能有:传感器循迹与避障视觉循迹路牌识别手势识别视觉SLAM建图。其中,我主要负责的包括视觉循迹单目SLAM建图

1、视觉循迹

​ 本项目内搭建的循迹环境主要为白色底板和黑色道路线如下图所示:

image-20210116195929572.png
image-20210116195929572.png

​循迹算法基于图像连通域,分为无连通域、单连通域和多连通域三种状态,分别对应不同的操作策略。图像预处理包括灰度转化、二值化处理、形态学开闭操作、连通域计算及阈值处理等;

// 图像预处理
Mat gray, image;
cvtColor(frame, gray, COLOR_BGR2GRAY);
threshold(gray, image, 40, 255, THRESH_BINARY);

// 形态学滤波
Mat element = getStructuringElement(MORPH_ELLIPSE, Size(5, 5)); 
morphologyEx(image, image, MORPH_OPEN, element); 
element = getStructuringElement(MORPH_ELLIPSE, Size(4, 4));
morphologyEx(image, image, MORPH_CLOSE, element);

// 连通域预处理
Mat labels, stats, centroids;
image = image(Rect(0, 0, 640, 380));
bitwise_not(image, image);
int num_con = connectedComponentsWithStats(image, labels, stats, centroids);
vector<int> list;

// 过滤较小面积的连通域, 去噪
for(int i=1;i<num_con;i++){
    if(stats.at<int>(i, CC_STAT_AREA) > 1200){
        list.push_back(i);
    }
}

​ 连通域的统计思路主要为:设定一个阈值,并将面积大于该阈值的认为是轨道线;

  • 无连通域

    ​ 无连通域时,认为小车超界,执行后退动作;

  • 单连通域

    ​ 单连通域的情况分为三种;在这种状态下,主要通过连通域外界矩形对角线斜率和连通域重心这两个特征进行逻辑判断;

    • 当连通域重心在“高区域”时,认为小车距离弯道还有一段距离,执行直行操作;
    • 当连通域重心在“中区域”时,认为小车进入弯道,需要进行转向操作,转向均通过改变左右轮的PWM值和旋转方向实现。转向分为两个等级:TurnRotate;其中Turn为根据连通域外界矩形对角线斜率的大小进行自适应调节,Rotate为原地转向调节,左右两轮方向相反;

      if(angle > 1.2){
          visual_forward(diff_pwm_cyc, -diff_pwm_cyc);
      }
      else if(x1 < x2){
          // 左转
          if(angle >= 1){
              turnLeft(0);
          }
          else{
              turnLeft(1.5*log(angle)<-100?100:-1.5*log(angle));
          }
      }
      else{
          // 右转
          if(angle >= 1){
              turnRight(0);
          }
          else{
              turnRight(1.5*log(angle)<-100?100:-1.5*log(angle));
          }
      }
  • 当连通域重心在“低区域”时,认为小车快要超出边界,进行Rotate操作;

这里重心的区域判断主要依据重心行坐标相对于图像整体的位置;

  • 多连通域

    ​ 多连通域情况下认为小车在相对较直的道路上,将执行自适应前进的操作;具体为,在屏幕中取5行,从中心向外计算白块数量,比较左右的白块数量,白块数量多的一边为转向方向。

    void visual_forward(int diff_right, int diff_left)
    {
        if(diff_left > 0){
            diff_right = diff_right * 2;
            diff_left = diff_left * 2;
        }else{
            diff_left = diff_left * 2;
            diff_right = diff_right * 2;
        }
        softPwmWrite(4,0); //左轮前进
        softPwmWrite(1,130-diff_left>0?130-diff_left:0); 
        softPwmWrite(6,0); //右轮前进
        softPwmWrite(5,130-diff_right>0?130-diff_right:0); 
        printf("forword\n");
        printf("%d, %d\n", 130-diff_left, 130-diff_right);
    }
2、单目SLAM建图

​ 单目SLAM建图使用ORB角点作为建模的特征;SLAM核心算法实现由OpenCV提供,同时针对SLAM计算需要,小车的移动速度要尽可能的慢,并且具有较高的手动扩展能力,对此我们进行了细致的调整,以使小车能够满足在光滑大理石地面的稳定移动。

​ 下图为小车车载单目摄像头对两个盒子的建模。

image-20210116204445927.png
image-20210116204445927.png


​ 由于个人对于树莓派系统有较深入的了解,使用和配置较为数量,在项目中遇到的问题主要集中在视觉循迹的策列制定和参数调节上;

  • 树莓派小车的运动结构;由于小车使用的是橡胶胎面,在光滑地面表现尚可,但是在摩擦较大的纸面表现并不佳,需要较高的启动电压,同时由于运动学的限制,做出一些动作所需要空间成本较高;具体到本项目中体现为,原地旋转Rotate的抖动严重,最小转弯半径较大。
  • 树莓派算力不足;由于循迹控制主要基于视觉进行,我们采取的控制策略需要对整个帧进行图像处理运算;最初使用3B+型号出现了严重的性能不足,延迟过大的情况,于是我们更换使用4B型号,计算延迟缩短到可以接受的范围内;
  • 小车循迹对环境依赖严重;本项目所使用的视觉算法不涉及视觉认知,所有特征均为人工挑选并调整;整个系统的鲁棒性不强;具体表现为对不同曲率的弯道会出现转弯不足和转弯过度的情况,且所有参数均为根据现有道路进行调整,需要调节的参数数量较多,缺少理解层面的映射关系;

​ 本次项目设计为我们提供了一次珍贵的动手实践机会,让我们学习到知识不只是停留在纸面和脑海中;项目设计的上限很高,只有当我真正动手从零开始搭建一个系统,并完成一些功能、实现一些目的时,我发现实践中重要的不仅仅是核心算法和逻辑处理,更重要的是来自方方面面容易忽视的细节;如果我们不认真考虑这些细节之处,最终的结果往往会失之毫厘差之千里。

bling.jpg
bling.jpg