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:接触面摩擦系数。

这样,我们就创建里一个这样的小车:

以上。