然而,这种解决方法只能解决这个特定的问题,而且解决之后我们也无法学到一些实际的东西。要想彻底明白,需要从源头入手,也就是说, 不要问“MoveIt! 怎么把机械手从空间一个点移到另一个点?“ , 而是要问”MoveIt! 为什么能把机械手从空间一个点移到另一个点?“ 。 这一点明白之后,遇到类似的问题,才能从容应对。同理,这不仅适用于MoveIt,也同样适用于其他任何ROS功能。
所以,下文中我们会见到一些具体的例子,但整体上,更倾向于宏观的概念和一些基础的方法,希望对大家能有所帮助。这里的帮助指的是增强对运动规划和Moveit, OMPL的整体理解,而非局限于完成某一个功能,编译运行某一个文件。
我尝试用最简单,最通俗的表达方式来解释这些问题,其中不免会有一些学术上的错误用词和解释,请专业的朋友们见谅,也欢迎指出错误。
一. 基础概念
首先,我们要了解一些基础的概念,了解各个名词的意义和区别。
1.1. 运动规划 (Motion Planning)
我们这里讲的 运动规划 ,有别于 轨迹规划 (Path Planning)。一般来说,path planning用于无人车/无人机领域,而motion planning主要用于机械臂,类人机器人领域。当然了,这两者没有本质的区别,理论上说MoveIt!和OMPL同样可以用于无人车无人机的规划,但不免有些杀鸡用牛刀的感觉。两者规划的空间维度不同,导致他们的难易程度不同。举例说明,如果不考虑速度加速度,只考虑位置的话,无人车轨迹规划维度是3 (x,y,和角度), 无人机是6 (x,y,z,和另外3个量确定空间的旋转角度)。确定3D空间的一个姿势(pose)需要6个变量,而对于关节数大于6的机械臂结构,它的规划空间维度就大于6,成为冗余系统(redundant system),从而使规划问题变得更为复杂。所谓冗余系统,就是说,存在多种关节角度配置能够使得终端达到相同的位姿,存在无数的解。这是达到的最终姿势有无数个解,那么如何到达这个最终姿势,整个运动的轨迹,更是存在无数个解。
既然存在无数的解,那么问题来了。很明显,存在两种不同的方向,一种是找到最好的那个解,另一种是快速的找到一个有效的解。前者,大部分算法使用最优规划 (Optimization-based Planning),后者使用采样规划 (Sampling-based Planning)。具体的区别和算法,不在这里赘述。
1.2. 开源运动规划库 (OMPL).
接上文,而OMPL (Open Motion Planning Library), 开源运动规划库,就是一个运动规划的C++库,其包含了很多运动规划领域的前沿算法。虽然OMPL里面提到了最优规划,但 总体来说OMPL还是一个采样规划算法库 。而采样规划算法中,最出名的莫过于 Rapidly-exploring Random Trees ( RRT ) 和 Probabilistic Roadmap ( PRM )了, 当然,这两个是比较老的,还有很多 其他 新算法。
- OMPL能做什么? 简单说,就是提供一个运动轨迹。给定一个机器人结构(假设有N个关节),给定一个目标(比如终端移到xyz),给定一个环境,那么OMPL会提供给你一个轨迹,包含M个数组,每一个数组长度是N,也就是一个完整的关节位置。沿着这个轨迹依次移动关节,就可以最终把终端移到xyz,当然,这个轨迹应当不与环境中的任何障碍发生碰撞。
- 为什么用OMPL? 运动规划的软件库和算法有很多,而OMPL由于其模块化的设计和稳定的更新,成为最流行的规划软件库之一。很多新算法都在OMPL开发。很多其他软件(包括ROS/MoveIt)都使用OMPL做运动规划。
1.3. 逆运动学 (Inverse Kinematics)
- 什么是逆运动学(IK)?简单说,就是把终端位姿变成关节角度,q=IK(p)。p是终端位姿(xyz),q是关节角度。
- 为什么要用IK?OMPL是采样算法,也就是要在关节空间采样。 这与无人车的规划有一个最明显的区别,无人车的目标就是在采样空间, e.g. 目标是(x,y), 采样空间也是(x,y). 但是对于机械臂,目标是终端空间位置(xyz), 但采样空间却是关节空间(q0,q1,…qN)。有了IK之后,我们就可以把三维空间的目标p转化为关节空间的目标q。那么这样就会让采样算法能算的更快,具体方法不赘述,这样的算法有RRT-Connect,BKPIECE等等双向采样算法。
1.4. MoveIt!
问:我不想看也看不懂OMPL和各种算法,但是我想让机械臂动起来,怎么办?
答:那这正是MoveIt!的设计初衷。Move It!让它动起来!
OMPL是运动规划的“规划”部分,而MoveIt!是OMPL的ROS接口。当然这不完全准确,OMPL有单独的ROS接口,但依旧很繁杂,而MoveIt是OMPL ROS接口的接口。。。而且MoveIt!还结合了其他一些功能,总之MoveIt!就是个大接口。。
- MoveIt!能做什么?一句话,MoveIt!就是一个模块化的接口,让你在最短时间内,不用自己写太多代码,就能配置出一个ROS Package来为你的机械臂做运动规划。
2.1 准备URDF package
首先我们要准备一个机械臂的urdf,如果你已有URDF,可以使用自己的urdf模型。若手头没有现成的URDF,可以从 此处 下载一个库卡LWR简化模型URDF,这是一个固定底座7自由度的机械臂。
从该连接处依次进入examples/sovlers/ik_solver_demo/resources,下载里面的lwr_simplified.urdf。
复制代码
1
2
3
4
|
cd path_to_catkin_ws/src
catkin_create_pkg lwr_description
cd lwr_description
mkdir urdf
|
将下载好的lwr_simplified.urdf放入urdf文件夹中,这样一个urdf package便创建好了。
2.2 MoveIt!配置助手 (MoveIt! Setup Assistant)
2.2.1 打开MoveIt! Setup Assistant
复制代码
1
|
roslaunch moveit_setup_assistant setup_assistant.launch
|
MoveIt! Setup Assistant 是一个图形界面,可以让我们不用写代码看代码,直接用鼠标点击就可以配置机器人的运动规划所需要的信息。点击Create New MoveIt Configuration Package来创建新的配置包,选择刚刚下载的urdf,然后点击Load Files 载入文件。
2.2.2 创建碰撞免检矩阵(ACM)
点击Setup Assisant的左边第二项’Self-Collisions’,在这里我们将创建碰撞免检矩阵(Avoid Collision Matrix, ACM)。再次强调,怎么创建很简单,点击一下’Regenerate Default Collision Matrix’就可以了,问题是,为什么?ACM是做什么的?
我们知道,碰撞检测是非常复杂的运算过程。对于多关节机械臂或者类人机器人来说,机械结构复杂,肢体多,碰撞检测需要涉及很多的空间几何计算。但是对于刚体机器人来说,有些肢体之间是不可能发生碰撞的,比如原本就相邻的肢体,比如类人机器人的脚和头。这里生成的ACM就是告诉我们,这个URDF所描述的机器人,哪些肢体之间是不会发生碰撞的。那么在之后的碰撞检测算法中,我们就可以略过对这些肢体之间的检测,以提高检测效率。
2.2.3 创建虚拟关节 (Virtual Joints)
在Setup Assistant 第三项Virtual Joints里面,我们要创建所谓的虚拟关节。这个虚拟关节,可以理解为一个连接机器人和世界的关节。
- Group Name: 不用多说,名字。。。我们就叫Arm。
- Kinematic Solver: 运动学求解工具,这个就是负责求解正向运动学(Forward Kinematics)和逆运动学(IK, 见1.3节)的。 一般我们选用KDL, The Kinematics and Dynamics Library。这是一个运动学与动力学的库,可以很好的解决6自由度以上的单链机械结构的正逆运动学问题。当然你也可以用其他IK Solver, 比如SRV或者<span \”=”” style=”box-sizing: border-box;”>IK_FAST,甚至你可以自己开发新的Solver然后插入进来,如果有空,我以后会发帖讲解如何创建新的运动学求解库并插入到MoveIt。
- Kin. Search Resolution: 关节空间的采样密度
- Kin. Search TImeout: 求解时间
- Kin. Solver Attempts: 求解失败尝试次数,一般来说这三项使用默认值就可以。你也可以根据具体需要做出适当调整。
2.2.5 创建机器人预设位姿 (Robot Poses)
2.2.6 配置终端控制器(End Effectors)
2.2.7 配置被动关节(Passive Joints)
所谓被动关节,就是指现实中不配置电机的关节,也就是不会出现在机器人的Joint State Msg里,以避免MoveIt与JointState出现匹配错误。这里我们的LWR机械臂并没有此类被动关节,所以可以直接跳过。
2.2.8 生成配置文件(Configuration Files)
最后一步,在Configuration Package Save Path里面选择一个保存地址,一般我们把他放在path_to_catkin_ws/src/lwr_moveit_config 然后点击Generate Package,这样一个完整的MoveIt Configuration Package就创建好了! 先不要急着运行,我们先来看看都生成了哪些东西,还有一些重要的配置参数都是在哪定义的。
三. MoveIt 配置包详解
打开刚刚创建好的lwr_moveit_config文件夹,我们发现有config和launch两个文件夹。 3.1 MoveIt! 配置文件 先看config,里面有
- fake_controllers.yaml:这是虚拟控制器配置文件,方便我们在没有实体机器人,甚至没有任何模拟器(如gazebo)开启的情况下也能运行MoveIt。
- joint_limits.yaml:这里记录了机器人各个关节的位置速度加速度的极限,这些都会被用于以后的规划中。
- kinematics.yaml:这里就是上一章2.2.4里面设置的东西,用于初始化运动学求解库
- lwr.srdf:这个是一个重要的MoveIt配置文件,我们将在下一节详解。
- ompl_planning.yaml:这里是配置OMPL各种算法的各种参数。
SRDF是moveit的配置文件,配合URDF使用。打开lwr.srdf,
下面,我们看看launch文件夹,一打开发现有很多文件,瞬间不想看了。。不要急,我们来看看几个重要的文件。
3.3.1 demo.launch
demo是运行的总结点,打开我们可以看到他include了其他的launch文件。其中第14行说,如果有需要,发布静态的tf。比如说,你的机器人基座不在世界坐标的原点,你可以发布一个静态tf来描述机器人在世界坐标中的位置。第17-21行,就是我们发布虚拟机器人状态的地方了,当然,如果你有实体机器人或者有gazebo之类的模拟器,你需要去掉这一部分,有其他相应的节点来发布机器人状态。26-32行运行了另一个moveit重要的节点,move
group。
3.3.2 move_group.launch
顾名思义,move group的功能是让一个规划组群动起来。怎么动,那就要做运动规划了,在move_group.launch第24-26行定义了运动规划库的使用,我们可以看到,默认的是使用ompl运动规划库。同样的,如果以后有时间,我会发帖详解如何创建新的运动规划库插件并让moveit使用其他的运动规划算法。其他的都是设置一些基本参数,暂时可以略过。
3.3.3 planning_context.launch
这里我们可以看到,定义了所使用的urdf和srdf文件,以及运动学求解库。不建议手动更改这些,但是如果你需要使用不同的urdf,srdf,可以在这里更改。
3.3.4 setup_assistant.launch
如果你需要更改一些配置,那么可以直接运行
复制代码
1
|
roslaunch lwr_moveit_config setup_assistant.launch
|
这样就可以基于当前设置做更改,而不是重新设置。
四. 运行MoveIt!
4.1 Launch Demo
现在我们可以来尝试运行moveit了!
复制代码
1
|
roslaunch lwr_moveit_config demo.launch
|
等待几秒,当看到 All is well! Everyone is happy! You can start planning now! 的时候,就代表启动成功了。我们可以看到一个Rivz窗口,左下角有一个运动规划MotionPlanning模块。
4.3 运动规划终于,到了运动规划的时候了。。在Planning子模块中单击Plan,一个运动轨迹就会出现与Rviz窗口中并循环播放。你可以在Display->MotionPlanning->Planned
Path里面设置各种显示参数。
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
[ INFO] [1453481861.884163555]: LBKPIECE1: Attempting to use default projection.
[ INFO] [1453481861.884336258]: LBKPIECE1: Attempting to use default projection.
[ INFO] [1453481861.884489778]: LBKPIECE1: Starting planning with 1 states already in datastructure
[ INFO] [1453481861.884523826]: LBKPIECE1: Attempting to use default projection.
[ INFO] [1453481861.884547702]: LBKPIECE1: Starting planning with 1 states already in datastructure
[ INFO] [1453481861.884564358]: LBKPIECE1: Attempting to use default projection.
[ INFO] [1453481861.884587404]: LBKPIECE1: Starting planning with 1 states already in datastructure
[ INFO] [1453481861.884604829]: LBKPIECE1: Attempting to use default projection.
[ INFO] [1453481861.884626253]: LBKPIECE1: Starting planning with 1 states already in datastructure
[ INFO] [1453481861.905034917]: LBKPIECE1: Created 99 (46 start + 53 goal) states in 88 cells (45 start (45 on boundary) + 43 goal (43 on boundary))
[ INFO] [1453481861.905633020]: LBKPIECE1: Created 87 (33 start + 54 goal) states in 76 cells (31 start (31 on boundary) + 45 goal (45 on boundary))
[ INFO] [1453481861.913846457]: LBKPIECE1: Created 126 (76 start + 50 goal) states in 115 cells (75 start (75 on boundary) + 40 goal (40 on boundary))
[ INFO] [1453481861.914639489]: LBKPIECE1: Created 220 (72 start + 148 goal) states in 201 cells (70 start (70 on boundary) + 131 goal (131 on boundary))
[ INFO] [1453481861.948016518]: ParallelPlan::solve(): Solution found by one or more threads in 0.063719 seconds
|
我们可以看到,本次运动规划使用了LBKPIECE算法,并且使用了4线程并行规划,规划时间为 0.063719秒。你也可以在OMPL算法里选择其他规划算法比如RRT,RRT-Connect,PRM,EST等等等等。。看看他们之间的轨迹区别和速度区别。
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
|
(noname)+++++
* obs1.dae
1
mesh
24 12
-0.0315993 0.126397 -0.0315993
-0.0315993 -0.126397 -0.0315993
-0.0315993 0.126397 0.0315993
-0.0315993 -0.126397 0.0315993
0.0315993 0.126397 0.0315993
0.0315993 -0.126397 -0.0315993
0.0315993 0.126397 -0.0315993
0.0315993 -0.126397 0.0315993
-0.0315993 -0.126397 0.0315993
0.0315993 -0.126397 0.0315993
0.0315993 0.126397 0.0315993
-0.0315993 0.126397 0.0315993
0.0315993 0.126397 -0.0315993
0.0315993 -0.126397 -0.0315993
-0.0315993 -0.126397 -0.0315993
-0.0315993 0.126397 -0.0315993
-0.0315993 -0.126397 0.0315993
-0.0315993 -0.126397 -0.0315993
0.0315993 -0.126397 -0.0315993
0.0315993 -0.126397 0.0315993
0.0315993 0.126397 -0.0315993
-0.0315993 0.126397 -0.0315993
-0.0315993 0.126397 0.0315993
0.0315993 0.126397 0.0315993
0 1 2
1 3 2
4 5 6
4 7 5
8 9 10
11 8 10
12 13 14
12 14 15
16 17 18
19 16 18
20 21 22
20 22 23
0.7 0 0.8
0.706825 0 0 0.707388
0.5 0 0 1
* obs2.dae
1
mesh
24 12
-0.0315993 0.126397 -0.0315993
-0.0315993 -0.126397 -0.0315993
-0.0315993 0.126397 0.0315993
-0.0315993 -0.126397 0.0315993
0.0315993 0.126397 0.0315993
0.0315993 -0.126397 -0.0315993
0.0315993 0.126397 -0.0315993
0.0315993 -0.126397 0.0315993
-0.0315993 -0.126397 0.0315993
0.0315993 -0.126397 0.0315993
0.0315993 0.126397 0.0315993
-0.0315993 0.126397 0.0315993
0.0315993 0.126397 -0.0315993
0.0315993 -0.126397 -0.0315993
-0.0315993 -0.126397 -0.0315993
-0.0315993 0.126397 -0.0315993
-0.0315993 -0.126397 0.0315993
-0.0315993 -0.126397 -0.0315993
0.0315993 -0.126397 -0.0315993
0.0315993 -0.126397 0.0315993
0.0315993 0.126397 -0.0315993
-0.0315993 0.126397 -0.0315993
-0.0315993 0.126397 0.0315993
0.0315993 0.126397 0.0315993
0 1 2
1 3 2
4 5 6
4 7 5
8 9 10
11 8 10
12 13 14
12 14 15
16 17 18
19 16 18
20 21 22
20 22 23
0.6 0.5 0.8
0.706825 0 0 0.707388
0.5 0 0 1
* obs3.dae
1
mesh
24 12
-0.0315993 0.126397 -0.0315993
-0.0315993 -0.126397 -0.0315993
-0.0315993 0.126397 0.0315993
-0.0315993 -0.126397 0.0315993
0.0315993 0.126397 0.0315993
0.0315993 -0.126397 -0.0315993
0.0315993 0.126397 -0.0315993
0.0315993 -0.126397 0.0315993
-0.0315993 -0.126397 0.0315993
0.0315993 -0.126397 0.0315993
0.0315993 0.126397 0.0315993
-0.0315993 0.126397 0.0315993
0.0315993 0.126397 -0.0315993
0.0315993 -0.126397 -0.0315993
-0.0315993 -0.126397 -0.0315993
-0.0315993 0.126397 -0.0315993
-0.0315993 -0.126397 0.0315993
-0.0315993 -0.126397 -0.0315993
0.0315993 -0.126397 -0.0315993
0.0315993 -0.126397 0.0315993
0.0315993 0.126397 -0.0315993
-0.0315993 0.126397 -0.0315993
-0.0315993 0.126397 0.0315993
0.0315993 0.126397 0.0315993
0 1 2
1 3 2
4 5 6
4 7 5
8 9 10
11 8 10
12 13 14
12 14 15
16 17 18
19 16 18
20 21 22
20 22 23
0.3 0 0.8
0.999784 0 0 0.0207948
0.5 0 0 1
* table.dae
1
mesh
24 12
-0.420619 0.0420619 -0.841237
-0.420619 -0.0420619 -0.841237
-0.420619 0.0420619 0.841237
-0.420619 -0.0420619 0.841237
0.420619 0.0420619 0.841237
0.420619 -0.0420619 -0.841237
0.420619 0.0420619 -0.841237
0.420619 -0.0420619 0.841237
-0.420619 0.0420619 0.841237
-0.420619 -0.0420619 0.841237
0.420619 -0.0420619 0.841237
0.420619 0.0420619 0.841237
0.420619 -0.0420619 -0.841237
-0.420619 -0.0420619 -0.841237
-0.420619 0.0420619 -0.841237
0.420619 0.0420619 -0.841237
-0.420619 -0.0420619 0.841237
-0.420619 -0.0420619 -0.841237
0.420619 -0.0420619 -0.841237
0.420619 -0.0420619 0.841237
0.420619 0.0420619 -0.841237
-0.420619 0.0420619 -0.841237
-0.420619 0.0420619 0.841237
0.420619 0.0420619 0.841237
0 1 2
1 3 2
4 5 6
4 7 5
8 9 10
8 10 11
12 13 14
15 12 14
16 17 18
19 16 18
20 21 22
20 22 23
0.7 0 0.63
0.706825 0 0 0.707388
0 0.5 0.5 1
* target
1
mesh
24 12
-0.0315993 0.126397 -0.0315993
-0.0315993 -0.126397 -0.0315993
-0.0315993 0.126397 0.0315993
-0.0315993 -0.126397 0.0315993
0.0315993 0.126397 0.0315993
0.0315993 -0.126397 -0.0315993
0.0315993 0.126397 -0.0315993
0.0315993 -0.126397 0.0315993
-0.0315993 -0.126397 0.0315993
0.0315993 -0.126397 0.0315993
0.0315993 0.126397 0.0315993
-0.0315993 0.126397 0.0315993
0.0315993 0.126397 -0.0315993
0.0315993 -0.126397 -0.0315993
-0.0315993 -0.126397 -0.0315993
-0.0315993 0.126397 -0.0315993
-0.0315993 -0.126397 0.0315993
-0.0315993 -0.126397 -0.0315993
0.0315993 -0.126397 -0.0315993
0.0315993 -0.126397 0.0315993
0.0315993 0.126397 -0.0315993
-0.0315993 0.126397 -0.0315993
-0.0315993 0.126397 0.0315993
0.0315993 0.126397 0.0315993
0 1 2
1 3 2
4 5 6
4 7 5
8 9 10
11 8 10
12 13 14
12 14 15
16 17 18
19 16 18
20 21 22
20 22 23
0.6 0.2 0.8
0.706825 0 0 0.707388
0 1 0 1
.
|
接下来可以尝试不同的OMPL算法,不同的目标位姿和不同的环境。来看看MoveIt的鲁棒性如何。
那么,MoveIt!和OMPL的运动规划就差不多讲完了,当然这是很浅显的,与实用性的东西都还有距离。以上都是纯手打,现做的例子,希望有所帮助。接下来该讲什么,你们可以在下面留言,是更深入的讲MoveIt!,还是讲OMPL运动规划算法,还是讲如何模拟具体实例。