我们都知道,gazebo可以在自带的gui中创建模型、导入模型,然后将一批模型组成的仿真环境保存为一个world文件:

例如上图所示的场景,我们可以从模型库中导入一些模型,然后或直接输入坐标或使用拖拽功能,将模型放置在需要的位置。(模型库下载:3dgems模型库)在菜单中File->Save as World可以将这些模型组成的仿真环境保存成一个world文件:


下面我们打开保存的test_01.world进行分析:

首先第一部分是光线设置:

<light name='sun' type='directional'>
      <cast_shadows>1</cast_shadows>
      <pose frame=''>0 0 10 0 -0 0</pose>
      <diffuse>0.8 0.8 0.8 1</diffuse>
      <specular>0.2 0.2 0.2 1</specular>
      <attenuation>
        <range>1000</range>
        <constant>0.9</constant>
        <linear>0.01</linear>
        <quadratic>0.001</quadratic>
      </attenuation>
      <direction>-0.5 0.1 -0.9</direction>
    </light>


然后是地面ground plane:

<model name='ground_plane'>
      <static>1</static>
      <link name='link'>
        <collision name='collision'>
          <geometry>
            <plane>
              <normal>0 0 1</normal>
              <size>100 100</size>
            </plane>
          </geometry>
          <surface>
            <friction>
              <ode>
                <mu>100</mu>
                <mu2>50</mu2>
              </ode>
              <torsional>
                <ode/>
              </torsional>
            </friction>
            <contact>
              <ode/>
            </contact>
            <bounce/>
          </surface>
          <max_contacts>10</max_contacts>
        </collision>
        <visual name='visual'>
          <cast_shadows>0</cast_shadows>
          <geometry>
            <plane>
              <normal>0 0 1</normal>
              <size>100 100</size>
            </plane>
          </geometry>
          <material>
            <script>
              <uri>file://media/materials/scripts/gazebo.material</uri>
              <name>Gazebo/Grey</name>
            </script>
          </material>
        </visual>
        <self_collide>0</self_collide>
        <enable_wind>0</enable_wind>
        <kinematic>0</kinematic>
      </link>
    </model>


设置了地面的面积、摩擦力、颜色等性质,在此不做详细介绍。
下面一部分是物理引擎的参数、模拟的经纬度等等环境设置:

<gravity>0 0 -9.8</gravity>
    <magnetic_field>6e-06 2.3e-05 -4.2e-05</magnetic_field>
    <atmosphere type='adiabatic'/>
    <physics name='default_physics' default='0' type='ode'>
      <max_step_size>0.001</max_step_size>
      <real_time_factor>1</real_time_factor>
      <real_time_update_rate>1000</real_time_update_rate>
    </physics>
    <scene>
      <ambient>0.4 0.4 0.4 1</ambient>
      <background>0.7 0.7 0.7 1</background>
      <shadows>1</shadows>
    </scene>
    <wind/>
    <spherical_coordinates>
      <surface_model>EARTH_WGS84</surface_model>
      <latitude_deg>0</latitude_deg>
      <longitude_deg>0</longitude_deg>
      <elevation>0</elevation>
      <heading_deg>0</heading_deg>
    </spherical_coordinates>


接下来是导入的模型及其瞬时状态的部分,我们以其中一个模型“chair_1”为例,来看看gazebo gui保存world文件时,对模型是怎么处理的。首先是一系列的模型定义:

<model name='chair_1'>
      <link name='link_0'>
        <pose frame=''>-0.30674 -0.201817 0.000751 0 -0 0</pose>
        <visual name='visual'>
          <pose frame=''>0 0 0 0 -0 0</pose>
          <geometry>
            <mesh>
              <uri>model://chair_1/meshes/frame.dae</uri>
              <scale>1 1 1</scale>
            </mesh>
          </geometry>
          <material>
            <script>
              <uri>model://chair_1/materials/scripts</uri>
              <uri>model://chair_1/materials/textures</uri>
              <name>chair_1_frame</name>
            </script>
          </material>
          <cast_shadows>1</cast_shadows>
          <transparency>0</transparency>
        </visual>
        <collision name='collision'>
          <laser_retro>0</laser_retro>
          <max_contacts>10</max_contacts>
          <pose frame=''>0 0 0 0 -0 0</pose>
          <geometry>
            <mesh>
              <uri>model://chair_1/meshes/frame.dae</uri>
              <scale>1 1 1</scale>
            </mesh>
          </geometry>
          <surface>
            <contact>
              <ode/>
            </contact>
            <bounce/>
            <friction>
              <torsional>
                <ode/>
              </torsional>
              <ode/>
            </friction>
          </surface>
        </collision>
        <self_collide>0</self_collide>
        <enable_wind>0</enable_wind>
        <kinematic>0</kinematic>
      </link>
      <link name='link_1'>
        <pose frame=''>-0.301944 -0.200687 -0.000751 0 -0 0</pose>
        <visual name='visual'>
          <pose frame=''>0 0 0 0 -0 0</pose>
          <geometry>
            <mesh>
              <uri>model://chair_1/meshes/cushions.dae</uri>
              <scale>1 1 1</scale>
            </mesh>
          </geometry>
          <material>
            <script>
              <uri>model://chair_1/materials/scripts</uri>
              <uri>model://chair_1/materials/textures</uri>
              <name>chair_1_cushion</name>
            </script>
          </material>
          <cast_shadows>1</cast_shadows>
          <transparency>0</transparency>
        </visual>
        <collision name='collision'>
          <laser_retro>0</laser_retro>
          <max_contacts>10</max_contacts>
          <pose frame=''>0 0 0 0 -0 0</pose>
          <geometry>
            <mesh>
              <uri>model://chair_1/meshes/cushions.dae</uri>
              <scale>1 1 1</scale>
            </mesh>
          </geometry>
          <surface>
            <contact>
              <ode/>
            </contact>
            <bounce/>
            <friction>
              <torsional>
                <ode/>
              </torsional>
              <ode/>
            </friction>
          </surface>
        </collision>
        <self_collide>0</self_collide>
        <enable_wind>0</enable_wind>
        <kinematic>0</kinematic>
      </link>
      <static>1</static>
      <allow_auto_disable>1</allow_auto_disable>
      <pose frame=''>2.1281 -1.50707 0 0 -0 0</pose>
    </model>


在这里我们发现,gazebo gui把导入环境的模型的模型文件内容一股脑复制进了world文件中,具体我们可以打开/chair_1/model.sdf对比内容。而且经过进一步测试发现,在gazebo中将同一模型复制几次,在world文件中就会复制几次模型文件内容!

也许gazebo这样做的初衷是为了将所有模型的定义都放进同一个world文件中来便于加载,就不用准备外部的模型库了(也就是说拷贝走一个world文件就行,不用models文件夹里的东西)。但是观察上面的chair_1定义我们发现,这样一个三维可视化模型还是需要外部链接如model://chair_1/materials/textures、model://chair_1/meshes/cushions.dae等来呈现视觉效果,还是得原始models文件夹内容才行。

接下来是导入的chair_1等模型在保存world的时刻的瞬时状态:

<state world_name='default'>
      <sim_time>192 183000000</sim_time>
      <real_time>193 394273343</real_time>
      <wall_time>1610584549 902590326</wall_time>
      <iterations>192183</iterations>
      <model name='chair_1'>
        <pose frame=''>2.1281 -1.50707 0 0 -0 0</pose>
        <scale>1 1 1</scale>
        <link name='link_0'>
          <pose frame=''>1.82136 -1.70889 0.000751 0 -0 0</pose>
          <velocity>0 0 0 0 -0 0</velocity>
          <acceleration>0 0 0 0 -0 0</acceleration>
          <wrench>0 0 0 0 -0 0</wrench>
        </link>
        <link name='link_1'>
          <pose frame=''>1.82616 -1.70776 -0.000751 0 -0 0</pose>
          <velocity>0 0 0 0 -0 0</velocity>
          <acceleration>0 0 0 0 -0 0</acceleration>
          <wrench>0 0 0 0 -0 0</wrench>
        </link>
      </model>
      <!-- 在此省略其他模型状态 -->
    </state>


在state这个标签下,保存了时刻、迭代次数等公共信息,然后就是各个模型、模型中各个link的瞬时信息,包括位置、速度、加速度等。

gazebo这种重复保存模型文件内容、模型瞬时信息的模型加载方式,会使得world文件变得非常长,很不好阅读和维护,以3dgems模型库中一个400平方米办公室环境为例:

这个包含几百个模型规模的world文件,大小有1.4M,长度超过42000行,在用launch打开的时候,过程非常漫长(以我一台i7 cpu,32G内存的台式机来说,打开一次要5分钟以上)。

所以,如果仿真环境中绝大部分模型都是静态的,也就是不需要额外设置速度加速度,只需要一个位置信息的话,我们可以采用直接引用模型链接的方式加载模型,以第一张图内的仿真环境为例,所有的模型文件内容都不需要写,只需要写下位置信息,并且用include标签引入模型链接即可:

    

    <model name='chair_set_1'>
      <pose frame=''>0.472518 -1.58098 0 0 -0 0</pose>
      <include>
      <static>true</static>
        <uri>model://chair_set_1</uri>
     </include>
    </model>

    <model name='bookshelf_large'>
      <pose frame=''>2.00597 -0.051607 0 0 -0 0</pose>
      <include>
      <static>true</static>
        <uri>model://bookshelf_large</uri>
     </include>
    </model>

    <model name='chair_1'>
      <pose frame=''>2.1281 -1.50707 0 0 -0 0</pose>
      <include>
      <static>true</static>
        <uri>model://chair_1</uri>
     </include>
    </model>

用这种方式改写的world文件,长度是原文件的几分之一。以上环境的world文件test_01.world和修改后的test_02.world原文件我放在了:应用3dgems模型库的gazebo worlds示例,供大家参考。

但是由此就产生了另一个问题:我们在gazebo gui中通过导入、拖拽、旋转模型建立的仿真环境,怎么能转化成上面代码块中的引用形式(即test_02.world形式)呢?目前来讲我还没发现很方便的办法,只能先保存成test_01.world那种形式,再对各个模型逐一修改,手工修改成test_02.world的形式。当然,作为一类xml形式的文件,肯定也能通过程序自动化地处理world文件,进行模型加载方式的转换。