- 代码地址:https://github.com/Jiahonzheng/Robotics/tree/master/HW3
- 演示视频:https://www.bilibili.com/video/av70787064
任务概要
- 在 Homework2 的基础上,优化原有设计,并设计具有挑战性的路径。设计路径可以多样,可以是起始点在同一位置(环线),也可以不是,可含有以下各种难点:
- 道路断续
- 道路交叉
- 急转弯
- 巡线小车控制算法包括但不限于 PID ,但必须采用视觉巡线(不能贴地模拟光感巡线)
完成情况
- 已实现机器人视觉巡线功能,废弃原有的贴地传感方案。
- 已完成比赛路径的设计
视觉巡线
由于我们在 HW2 中使用的是视觉传感器贴地模拟光感的实现方案,因此在本次作业中,我们需要使用“真正”的视觉巡线方案。在这个过程中,我们走了不少弯路,也因此耽误了比赛的参与。
简单版本:基于单行图片的黑色线中心判断
一开始,我们使用的策略是这样的:针对图片中的某一行,提取黑色线的中心位置,并计算它与图片竖向中心线的距离,在此距离(偏差量)应用 PID 控制理论,得到调节量—— Akermann 运动学模型的前轮转向角。
由于便携控制机器人的运动学状态,我们编写了 motor
函数,用于控制机器人的前进动力。
除此之外,我们还编写了 steer
工具函数,其使用了 Ackermann 转向运动学模型的相关推导,用于实现两个转向轮能按照给定角度进行不同角度的转向。
检测黑色线中心的核心算法是针对特定行,例如在下图中,我们使用第 0 行,从左至右扫描出黑色线的起始点和终止点,从而得到它的中心点位置。随后,由于我们的图像是 64*64 像素的,因此图像的竖向中心线的位置是 32 。通过简单的反三角函数的使用,我们即刻求取黑色线中心点与第 0 行第 32 个元素(即我们选取的参考点的)的夹角。
在获取到黑色线中心的偏离角度后,我们即可对此偏差量应用 PID 控制原理。经过多次调整,我们已得到了 PID 的三个较优的参数。
最终版本
尽管在演示中,简单版本的机器车车速度很快,且巡线效果很好,但在复杂的场景中,表现却很糟糕(例如在急转弯路径中),因此我们想提高机器小车面对复杂场景的鲁棒性。
简单版本的局限性在于,它只观测了某一方的黑色像素分布情况,没有考虑全局,因此我们打算通过分析全局的黑色像素分布,尽可能提高机器人对复杂场景的鲁棒性。
我们通过调用 getVisionSensorImage
方法,并为其传入灰度处理的 Handle Flag ,获取视觉传感器的结果,这是一个 64*64 的 Table
。随后,我们对其左右两部分的像素点进行统计,从而获取左右两部分的像素差,作为偏差值。最终,我们对此偏差值进行 PID 控制。获取偏差值的具体代码如下所示。
在获取到偏差值后,我们应用 PID 控制原理,输出转向角度,具体流程如以下代码所示。
由于在速度较快情况下,我们并未找到此模型的较优 PID 参数,因此我们的机器人只能在较为低速的情况下,进行巡线,且对比于简单版本的机器人模型,巡线质量并没有足够的优势,估计还是因为 PID 参数没有调整到位。
路径设计
为了测试小车的巡线功能,让小车在敏捷性和稳定性上到达最佳平衡,我们针对性地设计不同类型的路径。
交叉的路径
点击 [Menu bar –> Add –> Path –> Circle type] 添加路径,Ctrl 点击路径上的点来修改路径形状达到理想状态。
接着修改路径属性,首先取消勾选 Show orientation、Show path line 和 Show current position :
然后点击 [Common –> Scaling] ,勾选 In-place scaling,再修改 Scaling factor 直到路径规模合适:
打开 Path Shaping Parameters 修改属性,将 Scaling Factor 修改为 4.00 :
点击 Adjust Color 修改路径颜色为黑色:
最后,我们将路径的 Z 坐标增加 0.5mm ,如下:
至此,交叉路径设计完成,其最终效果如下图所示。
我们在此路径上,测试机器小车的巡线功能,小车性能表现良好。
断续的路径
断续路径的设计过程与之前类似,只是此处应用 Segment type 的 path ,将几段路径以如下的层次结合在一起:
断续路径的最终效果如下图所示。
连续急转弯
连续急转弯的路径设计过程,与类似前面两种路径类似,其最终效果如下图所示。
计时器
我们需要在 Scene 添加 Simulation Time Display 来记录仿真时间。
我们添加如下初始化语句来获取小车从起点出发的位置和仿真时间:
其中 sim.getSimulationTime()
原型如下:
sim.getObjectPosition()
原型如下,我们传入 -1 作为 relativeToObjectHandle
参数来获取小车的绝对位置:
在小车的驱动调整模块添加以下语句。当小车再次到达起点时,会输出总用时。
至此,计时器设计完毕。
效果展示
简单版本
以下是简单版本的视觉巡线小车的静态演示图,注意到它的视觉传感器是斜向前下方的。
下面是机器人巡线的示意图,详细细节可参考视频:https://www.bilibili.com/video/av70787064
最终版本
在最终版本中,为了提高对场景的鲁棒性,我们将视觉传感器垂直向地,其静态演示图如下。
在巡线测试中,由于最终版本的巡线小车未寻找到较优的 PID 参数,其运行轨迹与目标轨迹并不如简单版本那么贴合,循迹效果如下图所示,演示视频请参见 report 文件夹下的 “演示视频.mp4” 文件。
存在问题
本次实验,还是颇有挑战性的,例如在如何提升巡线小车对复杂场景的鲁棒性上,我们遇到了挺多问题,问题根源其实是我们把问题复杂化了,然后由于能力不足,导致时间和精力的浪费,最后耽误了今日下午的巡线比赛,我们队员也对自己表示很遗憾。
在实验的一开始,我们就打算使用 Hough 直线检测技术,这也正是计算机视觉课程正在上课的内容。我们的设想是,针对视觉传感器传回的数据,我们对其进行 Hough 变换,找到其中的直线,随后判断特征直线的夹角,进而求取 PID 控制的偏差量。
由于 V-REP 并不支持 Hough 变换的调用,因此我们使用了 V-REP 的 Remote API 的功能,在环境配置上踩了不少的坑。我们通过调用 OpenCV 的相关接口,先后对图像进行二值化、腐蚀、边缘检测(Canny算子)处理,最后引入 Hough 变换操作,从而成功在图像上绘制直线。在求取特征直线的夹角时,我们尝试了多种方法,都未得到较好的效果,并且由于算法设计并不完善,导致该版本下的巡线机器人并无鲁棒性。由于耽误了太长的时间,且并未获得出色的实验结果,我们放弃了此方案。
在查阅相关机器人技术论坛后,我们了解到了一种夹角检测方法,即在经过预处理的图像上(如二值化、腐蚀),我们通过调用 OpenCV 的 minAreaReact 方法,在图像上绘制包覆输入信息的最小斜矩形,并获取斜矩形的角度。有了这个想法,我们也快速完成了基本 demo 的验证,发现该算法对于 U 型弯和 V 型弯极其容易造成误判,导致巡线失败。再一次,经过多次尝试后,我们放弃了此方案。
期间,我们还实现了另一种“解决方案”,使用 OpenCV 探测图像的 5 个区域是否有黑色线,即用 OpenCV 视觉模拟 5 路光感巡线小车。但由于我们并未找到较好的 5 路区域的探测结果的表示方式,这导致了我们并未实现 PID 控制。后来,在完成最终版本代码后,我尝试在晚上搜索,找到了一篇文章,里面对 5 路巡线小车的 PID 控制有详细的阐述,打算日后好好研究一下,文章链接:https://www.arduino.cn/thread-49918-1-1.html 。
最终,我们还是回到了通过检测图片左右两部分的黑色像素的差异,实现巡线的实现方案上。奇怪的是,在前面摸索弯路时,我们并没有想到此方案,这是我们对本次实验的自我表现深感遗憾的主要原因。
不过,尽管走了很多弯路,我们还是了解到了很多机器人巡线的前沿信息,例如使用强化学习来实现复杂场景下的巡线,也因此了解到机器人巡线的更多优秀的算法,只是现在的我们还不足以将其实现,希望我们能够在日后的学习中,能够继续学习各种各样的控制理论与算法思想。