视频地址:video
基本规格
智行mini2:底盘长度为378.0mm,宽度为280.0mm,高度(不加机械臂)为210.0mm,重量为11.4kg


场地搭建



基本介绍
- 语音控制
建图完毕后,将机器人放置回出发区。依靠基于百度SDK与远场语音阵列的语音识别技术实现语音识别。终端控制开启语音识别节点,对机器人发出含有颜色信息的指令,麦克风阵列开始录音识别。
- 颜色识别
机器人向左移动并打开深度相机采集货物图像。在颜色识别脚本中,实现了一个拓展类,其继承了ros和opencv集合的基类,通过二值化函数、滤波函数、开闭运算函数、霍夫变换函数等函数,提取出摄像头视野中的红、绿、蓝矩形。
- 自主导航
机器人成功抓取货物后,需要自主导航移动到终点并避开途中的三个障碍物。针对影响路径规划质量cost factor, neutral cost和lethal cost,利用gazebo仿真,在反复调试后成功使全局规划器规划出曲率适中的优质路径。
- 机械臂控制
机器人移动到终点,接收get_pose节点消息,为避免物块落地后弹起致使物体垂直投影偏离中心圆靶区域,调整机械臂前端小臂向下运动、中端大臂向下运动,底座保持不动。最后关闭压泵和吸盘,释放物块。

主要功能
自主导航

机器人使用gmapping算法实现小范围建图。在ROS中编译好相关软件包后,配置对应launch文件,添加好laser到base_link的静态TF变换即可运行,最终建图效果如下:

根据之前建好的栅格地图,在程序中给定目标点,之后的自主导航全部依赖于该目标点来实现智行mini2在语音控制下的自主导航。
动态避障
在导航过程中,机器人不断根据里程计、激光雷达等传感器数据来确定自己的位置。并规划当前位置附件的局部路线以达到避障的效果。最终将局部规划的路线以速度指令的方式输出。在navigation的launch文件中启动了三个节点,其中Amcl订阅激光雷达的输出话题以确定机器人周围环境的状态。因此,当未预置障碍物突然出现时,机器人也能实现动态避障的功能。下图即为激光雷达得到的障碍信息:

颜色识别
在脚本中实现了一个拓展类,其继承了ros和opencv集合的基类,通过二值化函数、滤波函数、开闭运算函数、霍夫变换函数等函数,能够提取出摄像头视野中的红、绿、蓝矩形。通过阈值分割提取图像中的目标物体,通过边缘检测提取目标 物体的轮廓,使用这两种方法基本能够确定物体的边缘或者前景。接下来拟合这些边缘和前景,如确定边缘是否满足某种几何形状,如直线、圆、椭圆等,或者拟合出包含前景或者边缘像素点的最小外包矩形、圆、凸包等几何形状,为计算它们的面积或者模板匹配等操作打下坚实的基础。识别效果如下:

机械臂控制
机械臂驱动的库文件核心函数有:
Init:初始化机械臂armGetState:机械臂急停armSetStop:机械臂急停armSetPump:开关气泵,true为开,false为关armSetValue:开关吸盘,true为吸盘松,false为吸盘吸住armSetSingleSteps:单通道运动动,index 为 0,代表 X 轴,index 为 1,代表 Y 轴,index 为 2,代表 Z 轴,steps 参数为运动步数,armSetSingleAbsSteps:单通道绝对值运动,只 X,Y,Z 某个通道运行到某一绝对值armSetAbsStep:三通道绝对值运动动,X,Y,Z 按照指定参数运动armSetRunPara:设置运动参数
这些驱动已经被写成了ros库,可以通过ros直接操纵机械臂。

语音控制
通过运行respeaker_audio.py,实现了基于百度SDK与远场语音阵列的语音识别技术,出现recording 的时候说话,可以实现语音识别。运行语音控制文件,生成了一个节点,节点中包含了语音驱动和语音识别类。启动底盘和语音控制,然后等出现 record 后说出指令词可使智行 mini2 抓取相应颜色的方块并自主导航到目的地。

具体实现过程如下:
在主函数中创建了两个话题:一个是接收到语音指令,机器人自动抓取相应颜色的方块;以及机器人自动导航到目的地。订阅了一个话题,接收get_pos发来的消息,接收到消息后关闭压泵和吸盘,释放方块。
参数调整
全局路径规划参数
在导航过程中发现:在机器人规划的整体路径中,连接两个拐弯点的路径几乎为直线,且拐弯处路径靠近挡板,容易造成撞板的问题。(图中绿线即为全局规划路径)。
要解决该问题,即让机器人规划出合理的全局路径。事实上,成本因素(cost_factor)、中性成本(neutral_cost)和致命成本(lethal_cost)决定了计算全局路径的质量。
Navfn代价值的计算方法如下:neutralcost = COST NEUTRAL + COST FACTOR * costmap cost value,costmap cost values的输入值在0到252之间。
在成本中性值为50的情况下,成本系数需要大约为0.8,以确保输入值在输出范围(50到253)上均匀分布。实验证明,将成本系数设置为过低或过高会降低路径的质量。这些路径的曲率相对来说过小或过大。曲率过大,机器人容易撞板;曲率过小,可能出现无法通过障碍物或者来回震荡的问题。极端中性的成本值也有同样的效果。对于致死成本,即使可行路径明显,将其设置为低值也可能导致无法生成任何路径。通过多次实验,我们发现cost factor=0.55,neutral cost=70,lethal cost=253时,全局路径是比较理想的。

ld_navfn_behavior:若在某些情况下,想让global_planner完全复制navfn的功能,那就设置为trueuse_dijkstra:设置为true,将使用dijkstra算法,否则使用 A*算法use_quadratic:设置为true,将使用二次函数近似函数,否则使用更加简单的计算方式,这样节省硬件计算资源use_grid_path:如果设置为true,则会规划一条沿着网格边界的路径,偏向于直线穿越网格,否则将使用梯度下降算法,路径更为光滑点allow_unknown:是否允许规划器规划穿过未知区域的路径,只设计该参数为true还不行,还要在costmap_commons_params.yaml中设置track_unknown_space参数也为true才行default_tolerance:当设置的目的地被障碍物占据时,需要以该参数为半径寻找到最近的点作为新目的地点visualize_potential:是否显示从PointCloud2计算得到的势区域lethal_cost:致命代价值,默认是设置为 253,可以动态来配置该参数neutral_cost:中等代价值,默认设置是 50,可以动态配置该参数cost_factor:代价地图与每个代价值相乘的因子publish_potential:是否发布costmap的势函数
同时,由于转弯通常比直行更加复杂,将vy_sample设置成高于vx_sample的值,(vy_sample=40, vx_sample=20),解决机器人在拐弯处卡顿问题。


膨胀层
膨胀层由成本在0-255之间的单元组成。每个单元被占用(无障碍)或是未知的。Inflation radius 和cost scaling factor决定了膨胀。Inflation radius控制着零成本点离障碍物的距离。cost scaling factor与单元成本成反比,设置得更高会使衰减曲线更陡。而最佳的costmap衰减曲线是一条坡度相对较低的曲线。在该曲线坡度较低时,机器人倾向于远离障碍物,行进路线在道路中间;当costmap曲线陡峭时,机器人倾向于接近障碍物。经过不断尝试,我们将inflation radius 设置为0.35,将cost scaling factor 设置为9.5。可以使机器人达到优良的避障效果
cost_scaling_factor:膨胀过程中应用到代价值的比例因子,代价地图中到实际障碍物距离在内切圆半径到膨胀半径之间的所有 cell 可以使用如下公式来计算膨胀代价:
exp(-1.0*cost_scaling_factor*(distance_from_obstacle-inscribed_radius))*(costmap_2d::INSCRIBED_INFLATED_OBSTACLE-1)
公式中costmap_2d::INSCRIBED_INFLATED_OBSTACLE目前指定为 254,由于在公式中 cost_scaling_factor 被乘了一个负数,所以增大比例因子反而会降低代价。
inflation_radius:膨胀半径,膨胀层会把障碍物代价膨胀直到该半径为止,一般将该值设置为机器人底盘的直径大小。如果机器人经常撞到障碍物就需要增大该值,若经常无法通过狭窄地方就减小该值。

在机器人进行路径规划时,规划算法依靠的是gmapping扫描构建的一张环境全局地图,但是仅仅依靠一张原始的全局地图是不行的。因为这张地图是静态的,无法随时来更新地图上的障碍物信息。在现实环境中,总会有各种无法预料到的新障碍物出现在当前地图中,或者旧的障碍物现在已经从环境地图中被移除掉了,那么就需要来随时更新这张地图。同时由于默认的地图是一张黑白灰三色地图,即只会标出障碍物区域、自由移动区域和未被探索区域。机器人在这样的地图中进行路径规划,会导致规划的路径不够安全,因为我们的机器人在移动时需要与障碍物之间保持一定的安全缓冲距离,这样机器人在当前地图中移动时就更安全了

costmap 简单来说就是为了在这张地图上进行各种加工,方便后面进行路径规划而存在的,通过使用 costmap_2d 这个软件包来实现的,该软件包在原始地图上实现了两张新的地图。一个是 local_costmap, 另外一个就是 global_costmap,两张costmap 一个是为局部路径规划准备的,一个是为全局路径规划准备的。
global_frame:全局代价地图需要在哪个坐标系下运行robot_base_frame:在全局代价地图中机器人本体的基坐标系,就是机器人上的根坐标系。通过global_frame和robot_base_frame就可以计算两个坐标系之间的变换,得知机器人在全局坐标系中的坐标了update_frequency:全局代价地图更新频率,一般全局代价地图更新频率设置的比较小static_map:配置是否使用map_server提供的地图来初始化,一般全局地图都是静态的,需要设置为 truerolling_window:是否在机器人移动过程中需要滚动窗口,始终保持机器人在当前窗口中心位置transform_tolerance:坐标系间的转换可以忍受的最大延时plugins:在global_costmap中使用下面三个插件来融合三个不同图层,分别是static_layer、obstacle_layer和inflation_layer,合成一个master_layer来进行全局路径规划

恢复行为
机器人执行任务到后期,里程计误差累加,有可能无法规划出到达终点的路径。这时机器人采用rotate_recovery恢复行为,即原地旋转360°恢复或者将本地代价地图恢复成与全局代价地图相同的状态。即便如此,有时机器人会在尝试过所有恢复行为后放弃。为了避免机器人放弃而中断任务,尝试了多种恢复行为。由于希望机器人能有足够的时间计算出局部最优路径,sim_time的值被调整为一个较高的值(4.0),这样可以避免机器人在随机障碍之间穿行的过程中因计算时间不足而无法计算出最优路径,造成障碍物移位。但高sim_time值也意味着轨迹较长。所以考虑增加reset_distance,以便删除本地代价地图上较大的区域,让本地规划器有更好的机会找到路径。
move_base
机器人的线速度和角速度的调整也很重要。如果过大,激光雷达采样效果差;反之则行进速度太慢,造成不必要的时间损耗。由下图的比赛场地可以得知:每个通道的宽度为750cm。为了保证建图过程顺利进行和用时最少,希望机器人在拐弯时与挡板的最小距离为10-15cm。结合机器人的机身参数,预计机器人中心与挡板的距离为29cm左右。结合简单物理公式V=Vn×R,将机器人线速度设置为0.4593m/s,角速度设置为1.55rad/s,保证在用时最少的情况下达到较优的建图效果。同时,为了减少机器人碰撞障碍物可能,提高了控制器和全局规划的更新频率。
- shutdown_costmaps:当
move_base在不活动状态时,是否关掉 costmap - controller_frequency:向底盘控制移动话题
cmd_vel发送命令的频率 - controller_patience:在空间清理操作执行前,控制器花多长时间等有效控制下发布
- planner_frequency:全局规划操作的执行频率.如果设置为 0.0,则全局规划器仅在接收到新的目标点或者局部规划器报告路径堵塞时才会重新执行规划操作
- planner_patience:在空间清理操作执行前,留给规划器多长时间来找出一条有效规划
- oscillation_timeout:执行修复机制前,允许振荡的时长
- oscillation_distance:来回运动在多大距离以上不会被认为是振荡
- base_local_planner:指定用于
move_base的局部规划器名称 - base_global_planner:指定用于
move_base的全局规划器插件名称

机械臂释放位置调整
机器人订阅话题,接收get_pos发来的消息,到达目的地后接收到消息,关闭压泵和吸盘,释放方块。发现机器人在高处释放货物,货物掉落后会重新弹起,以至货物最终的垂直投影偏离圆心、圆靶区域。因此将机械臂最终释放货物的位置修改为(0,-325,-4159)(分别代表机器人释放货物时机械臂的大臂、小臂、底座位置)使机器人在较低位置释放货物,获得了良好的效果,具体代码如下:

导航初始位置调整
在机器人抓取货物后,会向后退一小段距离再开始自动导航以防止运动时将货架上其他物块击落,但由于后退后机器人距离场地边缘过近,此时开始自动导航容易出现撞壁问题。于是团队修改了代码,通过发布cmd_vel话题,令机器人抓取物块后移动至远离货架和墙壁的位置,再启动自动导航程序,以减少撞壁可能。具体代码如下:
