260330
接下来就来创建一个小车的雏形吧,虽然最终预期是履带作为移动,但是我们目前而言建模一个履带可能还是有点困难,因此就以一个底盘+四个轮子就可以了。
我们写一个这样的 .sdf文件吧。
我们可以先让 AI 写一下,最终得到的结果是这样的:

嗯...不过这个将近 300 行的 sdf 文件我看不太懂...

先让 AI 解释一下这个 sdf 文件的结构吧。
总的来说,我们的 SDF 文件本质上是在用 xml 来描述一个仿真世界里的物体:它有哪些部件、长什么样、碰撞怎么样计算、质量多大、怎么连接、怎么能动等。
简单的来说:
- visual:外观,即模型的皮肤。
- collision:碰撞箱,实际计算碰撞时候的体积。
- inertial:质量和惯性,模型的物理属性。
- joint:关节,模型之间的铰链。
我们先看最外层的结构:
<?xml version="1.0"?>
<sdf version="1.9">
这是声明 xml 版本和说明是 sdf 文件格式,版本为 1.9。
然后下面我们定义了一个模型的主体:
<model name="kibot_one_base">
这里代表我们定义了一个名为 kibot_one_base的模型。
一个 model下通常都会放下面的标签:
- link:刚体部件
- joint:部件之间的连接
- plugin:控制插件
- pose:整体的控制姿态
<sdf>
<model>
<link name="chassis">...</link>
<link name="front_left_wheel">...</link>
<link name="front_right_wheel">...</link>
<link name="rear_left_wheel">...</link>
<link name="rear_right_wheel">...</link>
<joint name="front_left_wheel_joint">...</joint>
<joint name="front_right_wheel_joint">...</joint>
<joint name="rear_left_wheel_joint">...</joint>
<joint name="rear_right_wheel_joint">...</joint>
</model>
</sdf>
比如我们模型的这里:
<model name="kibot_one_base">
<pose>0 0 0.12 0 0 0</pose>
<self_collide>false</self_collide>
...
</model>
这里我们定义的 pose的六个参数分别代表下面的值:
x y z roll pitch yaw
即:
- x:前后位置。
- y:左右位置。
- z:上下位置。
- roll:绕 x 轴旋转值。
- pitch:绕 y 轴旋转值。
- yaw:绕 z 轴旋转值。
我们这里就将这个模型放在了距离地面 0.12 米的位置。
然后是 link,link代表一个刚性部件,在我们的这个小车里则有五个 link。
每个 link 下也有 pose,不过和 model 下的 pose不同的点在于,model下的 pose 指代整个模型的中心相对于世界坐标的位置,而 link下的 pose 则指代指定部件相对该模型中心 pose 的位置。
分别是 chassis底盘和四个wheel轮子。
可以看到,我们的底盘和轮子都是各自的 link,这是因为在仿真中:能参与物理计算的部件,通常要单独建成 link。
如果我们把整台车都建为 link,它就作为一个整体存在了,但是轮子就不能单独转动了。
因此轮子必须是 link,然后用 joint来连上去。
我们后面想加上传感器的话,也是一样的,这些传感器也都要使用 link 然后和底盘的连接也都要使用 joint。
往下面的话,我们可以看到 visual 和 collision。
visual负责模型看起来是什么样,而 collision则负责模型实际的碰撞箱应该是怎么样的。
即,实际看到的和实际参与物理运算的通常不会完全一致。
比如我们的底盘:
<visual name="chassis_visual">
<geometry>
<box>
<size>0.55 0.40 0.12</size>
</box>
</geometry>
</visual>
这里我们定义了一个长方体,长 0.55m, 宽 0.40m, 高 0.12m。
然后定义它的碰撞箱:
<collision name="chassis_collision">
<geometry>
<box>
<size>0.55 0.40 0.12</size>
</box>
</geometry>
</collision>
由于我们目前的建模很简陋,因此碰撞箱是和视觉一致的。
然后是inertial,表示该物体的惯性属性。
如我们底盘的:
<inertial>
<mass>12.0</mass>
<inertia>
<ixx>0.18</ixx>
<iyy>0.38</iyy>
<izz>0.50</izz>
</inertia>
</inertial>
这部分表示质量 mass为 12kg,惯量矩阵中 ixx=0.18, iyy=0.38,izz=0.50。
对于我们的轮子,我们则没有使用长方体,而是使用圆柱体了(这不是废话么)。
<geometry>
<cylinder>
<radius>0.08</radius>
<length>0.05</length>
</cylinder>
</geometry>
通过表示半径和长度,我们即可表示该圆柱体。
然后是 joint,用来描述两个 link 之间的连接关系。
比如:
<joint name="front_left_wheel_joint" type="revolute">
<parent>chassis</parent>
<child>front_left_wheel</child>
<axis>
<xyz>0 1 0</xyz>
</axis>
</joint>
这里代表父节点为 chassis,子节点为 front_left_wheel。
然后它们两个间的连接方式为 revolute,即转动关节。
如果没有 joint的话,这五个 link 就会是五个独立的零件,而不会成为一台完整的车车,有了 joint后 Gazebo 才知道轮子是挂在底盘上的,且它们会组成一个完整的车。
然后这其中的 axis,表示该往哪个方向转,比如我们这里就是朝 y 方向进行转动。
不过这通常需要和轮子的朝向来配套看,我们的左前轮的 pose 是这样的:
<pose>0.18 0.24 0.08 1.5708 0 0</pose>
在 roll那里它旋转了 90 度后相当于底部是和 x 轴垂直了,因此才会设置为绕 y 轴旋转。
然后是 dynamics,friction, surface 参数,如:
<dynamics>
<damping>0.2</damping>
<friction>0.05</friction>
</dynamics>
以及轮子 link 里的:
<surface>
<friction>
<ode>
<mu>1.2</mu>
<mu2>1.2</mu2>
</ode>
</friction>
</surface>
则是更细致的物理参数:
- damping:阻尼,防止乱晃。
- friction:关节摩擦。
- mu / mu2:接触面摩擦系数。
这样,我们就创建里一个这样的小车:

以上。