以下,我从技术介绍和学习流程两个方面来进行讲解。其中技术介绍方面又分为机械、电子和软件三个章节。由于我在项目组里做的是软件开发工作,对机械和电子了解得相对较少,因此机械和电子这两个章节的内容我会简略地介绍,而讲解的重点自然便是软件章节。最后关于学习流程方面,我从基础、进阶、高级以及资源这四个章节来分别进行叙述,力求将自己的学习方法完整地呈现出来。
我们机器人的机械臂在机械设计上有一些不一样的地方。相比于其他使用大扭矩舵机或电机作为关节的机械臂,我们使用了由同步轮和同步带所组成的机械结构,这种独特的结构使得我们的机械臂在一定程度上拥有了抓取更重物体的负载能力。除此之外,机械臂末端的手爪也由上一代的对称张开闭合的结构变成了平行夹紧的形式,即两个金属滑块可以通过在滑轨上对称平行的移动。这使得手爪可以适应不同粗细、形状的物体,为抓紧物体提供最基本的保障。当然,这个机械臂也曾经给我造成过一些小困扰,我会在下面的软件部分讲到,通过这个困扰的解决,你就可以理解为什么机械的设计在某种程度上会影响软件代码的编写。
电子部分作为连接机械臂软硬件的重要组成部分,主要的任务是负责将软件组通过RS-485传过来的数据进行解析,并且以最快的速度传递给接有驱动盒的电机来实时地控制机械臂。同样的,通过电机编码器返回的数据可以用来记录每个机械臂关节的实时状态,经过一定的数学解算,就可以将其作为运动规划层的输入,为复杂运动的规划提供基本的保证。机械臂控制使用的是位置闭环算法,具体这方面我了解的并不是很多,不过之前用起来还是很稳定的。
从软件架构图中可以很清楚的看到,机械臂的软件层主要由三个部分组成,从下到上依次为:硬件接口层、运动规划层和任务决策层。
硬件接口层
ROSControl提供的硬件抽象层主要负责管理机器人的硬件资源,而控制器从抽象层请求资源即可,并不直接接触硬件。这提高了代码的复用率和可移植性。
首先,让我们先看一下ROSControl官方提供的数据流图是什么样子的:
细心的读者可能会发现这两个架构图在硬件接口层部分有一些不一样的地方。接下来我就讲解一下硬件接口层部分各子模块的功能,并解释彼此不同的原因。
首先看一张来自Gazebo官网的ROSControl架构图:
从图中可以看到,Simulation和Hardware之上的HardwareResource和ControllerManager是一样的,这很清晰地体现了ROSControl的底层无关性,即无论使用的是抽象的仿真还是具体的硬件,只要程序能继承RobotHW硬件抽象层的基类来做到数据接口的统一,ControllerManager就可以对相应的资源进行管理。
对于Simulation和Hardware来说,它们内部架构相似,但配置以及使用方式是不一样的。Simulation的RobotHW部分,Gazebo官方已经将其实现,并且提供了相应的ROSControl插件来从机械臂的URDF文件中载入所需的数据。用户只需写好URDF和YAML文件,并使用ROSLaunch将其整合到一起就万事大吉了。
而对于Hardware这部分来说,除了上面说到的配置之外,我们还需要自己编写C++代码来继承RobotHW基类,并在里面分别使用命令和状态硬件接口句柄对相应的关节数据进行注册,然后再将不同的硬件接口注册到RobotHW上。最后,我们还要自己编写函数完成对关节和电机数据的相互转换,并且根据指定的通信协议,实现read和write函数。
在终端中输入以下命令启动MotionControl测试。
$>roslaunchxm_arm_bringupxm_arm_bringup_gazebo_joint_control.launch$>rosrunxm_arm_teleopxm_arm_teleop_position_keyboard你可以使用键盘上的按键来控制机械臂每个关节的移动位置。
(2)初始化关节和电机数据,并使用HardwareInterface对相应的数据进行注册,最后初始化与串口通信有关的Topic。
(3)根据机械臂公式,实现关节和电机数据之间的互相转换。
(4)实现定制的read和write函数。
(5)加载关节名字到ROS的参数服务器中。
以上就是硬件接口层的全部内容了。作为整个机械臂软件架构最底层的部分,它的重要性不言而喻。根据我之前开发机械臂的经验,只有编写出稳定且鲁棒的的硬件接口层,才能为之上的运动规划提供强有力的保证。否则,等到机械臂出现暴走失控的情况的时候就麻烦了(我之前就曾入过这样的坑!)。
运动规划层
首先,简要地介绍一下什么是MoveIt!。以下是MoveIt官网给出的定义:
下图是MoveIt的总体框架:
这张图我在学习MoveIt!的时候看过很多遍,理解这个架构图对于学习MoveIt!非常重要。从图中可以看到,move_group是MoveIt!最核心的部分。它将其他独立的组件集成到一起,为使用者提供了一系列可以使用的命令和服务。
接下来,我们介绍一下MotionPlanning。
这里我引用古月居前辈对运动规划的解释:
PlanningScene
PlanningScene用来表示机械臂周围的外部世界并且保存机械臂自己本身的状态。它通过监听对应的Topic来获取关节状态信息、传感器信息。并可以根据传感器信息和用户的输入,生成机器人周围3D世界空间的表示。
3DPerception
Kinematics
运动学算法是机械臂各种算法中的核心,尤其是反向运动学算法IK(InverseKinematics)。MoveIt!使用插件的形式可以让用户灵活的选择需要使用的反向运动学算法,也可以选择自己的算法。
CollisionChecking
好的,讲了这么多抽象的概念,就让我们像上一节讲ROSControl一样,用具体的例子来实践一下。
之后,运行下面命令来启动MoveIt!SetupAssistant。
接下来,让我们运行两个例子来测试一下MoveIt!。
首先,我们测试一下MoveIt!的MotionPlanning。请在终端中输入下列命令:
第二个例子,我们来测试一下带有AvoidCollision的MotionPlanning。同样的,请在终端中输入下列命令:
$>roslaunchxm_arm_bringupxm_arm_bringup_moveit_and_gazebo.launch因为我在Gazebo中给机器人的头部添加了深度传感器的插件,所以当你把桌子放到机器人前方的时候,MoveIt!可以立马从点云Topic中获取物体的信息,并在Rviz中生成可视化的OctoMap。在下一次做运动规划的时候,MoveIt!会将由正方体组成的OctoMap看成障碍物并考虑在内。图中,机械臂的初始位置为伸直形态,我将其从桌子的下方移动到了桌子的正上方,规划的效果如下图所示。
当然,在使用MoveIt!对机械臂进行运动规划的时候并不是每一次都能成功,有些时候会出现超时报错的情况。遇到这种问题的时候,你可以尝试尝试其他OMPL算法,因为不同的OMPL算法可能对不同的情况有各自的优化。
任务决策层处于整个架构图最顶端,是控制整个机械臂的大脑所在。首先,我要阐明的一点是:这一部分在我那一届机械臂软件代码中并没有实现,这是我后来总结机械臂开发经验的时候重新设计的。
任务决策层的核心简单来说是在其内部定义了一个小型的状态机,它可以根据不同的任务类型、物体位置以及物体的类型来选择不同的数据发给下面的运动规划层。这里我举一个具体的例子来说明其工作的整个流程:首先,机器人决策模块给机械臂的任务决策层发送了一个抓取的状态,任务决策层接收到之后就会在自己事先存储好的状态表中进行查找,如果匹配抓取状态成功,就把表中的状态链取出并放到状态队列中去。每次状态控制器会根据当前状态队列中的子动作来分析其所需要的数据。比如说,抓取状态可以拆分成很多子动作:初始、准备、抓取、手爪张开、手爪夹紧、手握物体等。此时,如果队列中第一个动作是初始,那控制器便会从预先设定好的机械臂位置池中取出相应的位置,并从MoveIt!参数表中取出其所需要的数据,最后通过MoveIt!接口把初始动作发送给运动规划层进行规范和执行。当机械臂完成这个动作后,任务决策层会比较机械臂实际运动的位置和预想位置之间的差值,如果误差小于某个值,其便会返回执行成果给控制器,控制器则会继续地执行下一个状态,直到整个状态队列中的动作都被执行完。如果误差过大,则报错退出,以防止机械臂出现任何不可控的意外情况。
最后,鉴于这一部分只停留在我的设想阶段,目前只供参考。至于最终能不能实现出来,还有待日后的验证。
开发规范
版本管理
文档写作
这方面也有很多的开发者不是很重视。但个人认为如果你想成为真正的强者,只会编代码是远远不够的,你还需要优秀的文档写作能力。比如说你在开发的过程中遇到了一些问题,并成功地解决了。这个时候,你应该及时地将遇到的问题和解决办法以项目日志的形式记录下来,这样伴随着项目开发进度的不断向前,日志的内容也会越来越多。我敢保证,若干年之后,你一定会拥有别人绝对没有的宝贵财富!当然,除了开发日志外,你也要学会如何使用Markdown来编写项目文档。Markdown是你与开源世界交流的最重要的工具,一定要学会,况且它也并不是很难学。
编程能力
这个我就不用讲太多了,我相信如何学习编程,大家可能知道的比我还多。我这里主要想强调一下,一定要重点理解、学习C++。毕竟编译型语言要比解释性语言Python在执行效率上要高,而且对于机械臂开发来说,MoveIt!中的C++API也要比Python的要多。
ROS基础
Gazebo
MoveIt!
多看看别人的MoveIt!配置是什么样的,然后你自己再重新地配置几遍,主要是熟悉其中的一些概念。最后,在Rviz里试着拖动机械臂到新的位置,点击Plan按钮看看MoveIt!是怎么通过IK来输出一条平滑的轨迹的。当然,如果你配置好了深度传感器接口的话,可以试一试MoveIt!是如何在有障碍物的情况下进行运动规划的。
总之,想要开发好机械臂,MoveIt!+Gazebo是必不可少的。
至此,如果你能按照我说的完成前两步的话,你应该已经会用MoveIt!,并能用其做简单的运动规划了。当然,如果你想成为机械臂开发大神的话,你还需要重点学习MoveIt!的代码API。MoveIt!的API不少,你需要多尝试,找到最适合你们机械臂使用的API(推荐C++的API)。
FCL
在MoveIt!中,碰撞检测使用的是FCL库。你需要了解和学习FCL的API,并将其融入到机械臂的运动规划中去。
OMPL
IK解算
如果你认为你已经对MoveIt!的使用了如指掌,你可以尝试挑战一下难度——根据你们自己机械臂的实际情况,手写IK解算插件并将其集成到OMPL中去。
理论
以下是我认为学习机械臂比较好的资源,推荐给大家。
网站
书籍
《Effective_Robotics_Programming_with_ROS_Third_Edition》《Learning_ROS_for_Robotics_Programming_Second_Edition》《Mastering_ROS_for_Robotics_Programming》《Programming_Robots_with_ROS》《Robot_Operating_System(ROS)_The_Complete_Reference》《ROS_By_Example_2_Indigo》《ROS_Robotics_By_Example》