我们都知道,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文件,进行模型加载方式的转换。