-
Notifications
You must be signed in to change notification settings - Fork 98
Model Migration Guide
This section describes the process of converting simulation files used in the Gazebo9 version of SubT to a format that is compatible with Ignition Gazebo. In Gazebo9, robots were defined in URDF/xacro files possibly spread across multiple ROS packages. In Ignition Gazebo, robots will be defined using SDF instead and the model.sdf
file along with other resources, such as meshes, materials and launch files will be placed in a single ROS package.
The reference material involves migrating a Clearpath Husky Gazebo9 model with the file directory structure shown below:
.
└── husky_description
├── launch
│ └── description.launch
├── meshes
│ ├── base_link.dae
│ ├── bumper.dae
│ ├── lms1xx_mount.dae
│ ├── meshes.txt
│ ├── sick-lms1xx.dae
│ ├── top_chassis.dae
│ ├── top_plate.dae
│ ├── user_rail.dae
│ └── wheel.dae
├── urdf
│ └── husky.urdf.xacro
├── CHANGELOG.rst
├── CMakeList.txt
└── package.xml
3 directories, 14 files
1. Generate a single URDF from all the xacro files.
-
One way to do this is to launch the simulation containing the desired vehicle in Gazebo9 and to retrieve the urdf from the
robot_description
parameter using the commandrosparam get robot_description -p > output.urdf
-
The
robot_description
parameter can be loaded into ROS using a command such asrosparam load <file_name>.xacro robot_description
or by roslaunching thedescription.launch
file in a vehicle's description package.
2. Before converting to SDF, remove namespaces from link names. Eg., X1/base_link
becomes base_link
.
- This can be automated by running
python remove_urdf_namespaces.py input.urdf > ouput.urdf
3. Convert the URDF to SDF.
- Run
ign sdf -p input.urdf > output.sdf
- There may be the following error shown:
Error [parser.cc:458] Missing <sdf> element.
Disregard it as long as youroutput.sdf
file was successfully populated. - For wheeled vehicles, ensure that the orientations of the wheel collisions use high precision numbers for values of PI (3.1415926535897932) or PI/2 (1.5707963267948966). An example of this conversion is shown below:
<collision name='front_left_wheel_link_collision'>
<pose frame=''>0 0 0 1.57079 -0 0</pose>
to:
<collision name='front_left_wheel_link_collision'>
<pose frame=''>0 0 0 1.5707963267948966 -0 0</pose>
- This can be automated by running
python high_precision_constants.py input.sdf > ouput.sdf
. The tolerance for equality checks and the precision of the constants can be set using command line arguments. The default values should suffice for most cases.
4. Create a ROS package for the model, where the sdf file and other resources will reside. The name of the package should follow the pattern <organization_name>_<robot_name>_sensor_config_<#>
. As an example, we'll call our model husky_migration_sensor_config_1
5. Create a model.config
file with metadata about the model. Example metadata from husky_migration_sensor_config_1
is shown below:
<?xml version="1.0"?>
<model>
<sdf version="1.6">model.sdf</sdf>
</model>
6. Gather all meshes used by the model and place them in a directory called meshes
.
- In the case of the Clearpath Husky example, simply copy the
meshes
folder over.
7. Gather all materials used by the model and place them in a directory called materials
. Some meshes may refer to textures that are co-located with the mesh file. In this case, the texture files can be kept in the meshes
directory.
- In the case of the Clearpath Husky example, there are no materials included so this step is skipped.
Below is the final directory structure for the example model
submitted_models
└── husky_migration_sensor_config_1
├── CMakeLists.txt
├── launch
│ ├── description.launch
│ ├── example.ign
│ ├── spawner.rb
│ └── vehicle_topics.launch
├── meshes
│ ├── base_link.dae
│ ├── bumper.dae
│ ├── lms1xx_mount.dae
│ ├── meshes.txt
│ ├── sick-lms1xx.dae
│ ├── top_chassis.dae
│ ├── top_plate.dae
│ ├── user_rail.dae
│ └── wheel.dae
├── model.config
├── model.sdf
├── package.xml
├── thumbnails
│ ├── 1.png
│ ├── 2.png
│ ├── 3.png
│ ├── 4.png
│ └── 5.png
├── urdf
│ ├── husky_migration.orig.urdf
│ └── robot_from_sdf.xacro
└── worlds
└── example.sdf
3 directories, 13 files
8. Use relative URIs:
- URIs for meshes and textures need to be specified using relative URIs instead of
model://
orpackage://
. Relative URIs use a path that is relative to location of themodel.sdf
file. For example, instead of:
<mesh>
<uri>model://husky_description/meshes/wheel.dae</uri>
</mesh>
use:
<mesh>
<uri>meshes/wheel.dae</uri>
</mesh>
9. Use the script flatten_nested_model.py
to flatten the model file.
-
Flattening the model file is necessary because nested models are not yet supported.
-
This script requires
lxml
,tf
andnumpy
. -
Example usage:
python flatten_nested_model.py model_old.sdf > model.sdf
Note This and other migration scripts are currently located in Arthur's private repo in the
migration_script
branch.
Note: Light brightness in ign-gazebo is lower than in Gazebo9 for a given light configuration. This is a known issue. The workaround is to increase the brightness by adjusting the parameters under
<attenuation>
.
1. Remove all light plugins (libLightVisualPlugin.so
and ``libRosFlashLightPlugin.so`)
Note
<material>
tags in URDF are not converted to SDF automatically. Manual conversion is necessary.
1. Remove empty <material>
tags.
2. Migrate <material>
tags with <script>
elements.
-
<material>
tags with<script>
elements are not yet supported in ignition-gazebo. Either remove the<material>
tag completely or replace the<script>
tag with an equivalent value using<ambient>
<diffuse>
and<specular>
tags for solid colors. If the underlying element is not a solid color, a mesh with a texture must be used.
Remove/replace unsupported plugins, skip any step that does not apply to your model:
1. Remove the libgazebo_ros_control.so
plugin.
- For example in the case of a wheeled vehicle, diff drive functionality is provided by
libignition-gazebo-diff-drive-system.so
plugin included in the environment/vehicle launch file.
2. Replace the IMU plugin with a IMU sensor which goes inside the base_link
link.
3. Remove the libgazebo_ros_p3d.so
plugin.
- Ground truth poses are available via the
libignition-gazebo-pose-publisher-system.so
by setting$enableGroundTruth=true
in ignition launch files.
4. Replace libgazebo_ros_openni_kinect.so
with the sensor type rgbd_camera
.
- RGBD cameras are natively supported via the sensor type
rgbd_camera
for example:
<sensor name="camera_front" type="rgbd_camera">
<pose>0.1 0 0 0 0 0</pose>
<always_on>1</always_on>
<update_rate>20</update_rate>
<camera name="camera_front">
<horizontal_fov>1.0472</horizontal_fov>
</camera>
...
</sensor>
5. Replace the libgazebo_ros_velodyne_gpu_laser.so
plugin with the sensor type gpu_lidar
.
- Lidar sensors are natively supported via sensor type
gpu_lidar
. Note thatgpu_ray
andray
are deprecated. Usegpu_lidar
andlidar
instead.
6. Remove the libhector_gazebo_ros_gps.so
plugin.
- GPS is not yet supported in ignition-gazebo
7. Replace the libgazebo_ros_range.so
plugin with a lidar
sensor with a narrow field of view.
- Currently the
sensor_msgs::Range
message type is not output by the the lidar. Instead usesensor_msgs::LaserScan
.
8. Replace RealSense cameras with a single rgbd_camera
sensor.
- The IR component of the camera is supported in ignition-gazebo (Dome)
For sensors and plugins not addressed in this section, check the Ignition Dome Migration Documentation to find the corresponding plugin.
For visualization in RVIZ, the sdf must be manually converted back to URDF for visualization. During the initial transition from URDF to SDF, links used in the original URDF may have been collapsed and will result in an incomplete render inside of RViz.
- Create a URDF from the SDF. The
sdf_to_urdf_sdf8
branch of osrf/sdformat contains a tool to help with this. To build this tool, open a new terminal and run the following commands:
git clone https://github.com/osrf/sdformat -b sdf_to_urdf_sdf8 sdf2urdf
cd sdf2urdf
export SDF_SRC_PATH=$PWD
mkdir build/
cd build/
cmake .. -DCMAKE_INSTALL_PREFIX=$SDF_SRC_PATH/install
make install
export IGN_CONFIG_PATH=$SDF_SRC_PATH/install/share/ignition
-
Note that there is no need to uninstall the pre-existing sdformat library since the installation directory used in these instructions does not interfere with the sdformat library installed as part of SubT. However, make sure not to use the same terminal used for SDF to URDF conversion for running SubT.
-
The first step in the conversion process is to change all
model://
topackage://
in model.sdf. -
Then run
ign sdf -u model_package.sdf
wheremodel_package.sdf
is the model.sdf file containingpackage://
as described above. -
Convert the urdf to a xacro file. Add a
name
parameter which will be used to namespace each link. -
Prefix each link in the xacro file with
${name}
, eg.base_link -> ${name}/base_link
.
Below is an example directory structure of the launch subdirecotry of the migrated Husky in Ignition.
submitted_models
└── husky_migration_sensor_config_1
└── launch
├── description.launch (roslaunch file to load xacro data onto the rosparam server)
├── example.ign (Ignition launch file)
├── spawner.rb (Ruby script that contains the functions `spawner` and `rosExecutables` that return the SDF string for spawning the model into ignition-gazebo)
└── vehicle_topics.launch (roslaunch file to pass sensor data from Ignition to ROS)
1. Set up a launch file to support your model description. In this case, the file description.launch
contains the following:
<?xml version="1.0"?>
<launch>
<arg name="name" doc="Name of Vehicle"/>
<param name="$(arg name)/robot_description" command="$(find xacro)/xacro '$(find husky_migration_sensor_config_1)/urdf/robot_from_sdf.xacro' name:=$(arg name)"/>
</launch>
This corresponds to the .xacro
generated from the "Rviz Visualization" section of this guide where the process of going from sdf
->urdf
->xacro
is documented.
2. Set up the ROS bridge
between Ignition topics and ROS topics for sensor data and controls (i.e. velocity commands) through a launch file similar to the following vehicle_topics.launch
file.
<?xml version="1.0"?>
<launch>
<arg name="world_name"/>
<arg name="name"/>
<arg name="link_name" value="base_link"/>
<arg name="sensor_prefix" value="/world/$(arg world_name)/model/$(arg name)/link/$(arg link_name)/sensor"/>
<group ns="$(arg name)">
<!--Create multipe bridges so that it can run in parallel-->
<node
pkg="ros1_ign_bridge"
type="parameter_bridge"
name="ros1_ign_bridge_imu"
args="$(arg sensor_prefix)/imu_sensor/imu@sensor_msgs/Imu[ignition.msgs.IMU">
<remap from="$(arg sensor_prefix)/imu_sensor/imu" to="imu/data"/>
</node>
<node
pkg="ros1_ign_bridge"
type="parameter_bridge"
name="ros1_ign_bridge_gpu_lidar"
args="$(arg sensor_prefix)/front_laser/scan@sensor_msgs/LaserScan[ignition.msgs.LaserScan">
<remap from="$(arg sensor_prefix)/front_laser/scan" to="front_scan"/>
</node>
<node
pkg="ros1_ign_bridge"
type="parameter_bridge"
name="ros1_ign_bridge_pose"
args="/model/$(arg name)/pose@geometry_msgs/TransformStamped[ignition.msgs.Pose">
<remap from="/model/$(arg name)/pose" to="pose"/>
</node>
<node
pkg="ros1_ign_bridge"
type="parameter_bridge"
name="ros1_ign_bridge_twist"
args="/model/$(arg name)/cmd_vel_relay@geometry_msgs/Twist]ignition.msgs.Twist">
<remap from="/model/$(arg name)/cmd_vel_relay" to="cmd_vel"/>
</node>
<node
pkg="ros1_ign_bridge"
type="parameter_bridge"
name="ros1_ign_bridge_odom"
args="/model/$(arg name)/odometry@nav_msgs/Odometry[ignition.msgs.Odometry">
<remap from="/model/$(arg name)/odometry" to="odom"/>
</node>
<node
pkg="ros1_ign_bridge"
type="parameter_bridge"
name="ros1_ign_bridge_battery_state"
args="/model/$(arg name)/battery/linear_battery/state@sensor_msgs/BatteryState[ignition.msgs.BatteryState">
<remap from="/model/$(arg name)/battery/linear_battery/state" to="battery_state"/>
</node>
<node
pkg="subt_ros"
type="pose_tf_broadcaster"
name="pose_tf_broadcaster"/>
</group>
</launch>
3. To spawn a vehicle into one of the SubT world environments, add two functions similar to the following to include all necessary plugins (e.g. diff-drive for the husky model) to spawner.rb
. Integrating a custom model into the vehicle and sensor configuration case statements requires creating and including a custom vehicle spawning function.
def spawner(_name, _modelURI, _worldName, _x, _y, _z, _roll, _pitch, _yaw)
<<-HEREDOC
<spawn name='#{_name}'>
<name>#{_name}</name>
<allow_renaming>false</allow_renaming>
<pose>#{_x} #{_y} #{_z + 0.2} #{_roll} #{_pitch} #{_yaw}</pose>
<world>#{_worldName}</world>
<is_performer>true</is_performer>
<sdf version='1.6'>
<include>
<name>#{_name}</name>
<uri>#{_modelURI}</uri>
<!-- Diff drive -->
<plugin filename="libignition-gazebo-diff-drive-system.so"
name="ignition::gazebo::systems::DiffDrive">
<left_joint>front_left_wheel</left_joint>
<left_joint>rear_left_wheel</left_joint>
<right_joint>front_right_wheel</right_joint>
<right_joint>rear_right_wheel</right_joint>
<wheel_separation>#{0.45649 * 1.5}</wheel_separation>
<wheel_radius>0.1651</wheel_radius>
<topic>/model/#{_name}/cmd_vel_relay</topic>
</plugin>
<!-- Publish robot state information -->
<plugin filename="libignition-gazebo-pose-publisher-system.so"
name="ignition::gazebo::systems::PosePublisher">
<publish_link_pose>true</publish_link_pose>
<publish_sensor_pose>true</publish_sensor_pose>
<publish_collision_pose>false</publish_collision_pose>
<publish_visual_pose>false</publish_visual_pose>
<publish_nested_model_pose>#{$enableGroundTruth}</publish_nested_model_pose>
</plugin>
<!-- Battery plugin -->
<plugin filename="libignition-gazebo-linearbatteryplugin-system.so"
name="ignition::gazebo::systems::LinearBatteryPlugin">
<battery_name>linear_battery</battery_name>
<voltage>12.694</voltage>
<open_circuit_voltage_constant_coef>12.694</open_circuit_voltage_constant_coef>
<open_circuit_voltage_linear_coef>-3.1424</open_circuit_voltage_linear_coef>
<initial_charge>78.4</initial_charge>
<capacity>78.4</capacity>
<resistance>0.061523</resistance>
<smooth_current_tau>1.9499</smooth_current_tau>
<power_load>6.6</power_load>
<start_on_motion>true</start_on_motion>
</plugin>
</include>
</sdf>
</spawn>
HEREDOC
end
def rosExecutables(_name, _worldName)
<<-HEREDOC
<executable name='robot_description'>
<command>roslaunch --wait husky_migration_sensor_config_1 description.launch world_name:=#{_worldName} name:=#{_name}</command>
</executable>
<executable name='topics'>
<command>roslaunch --wait husky_migration_sensor_config_1 vehicle_topics.launch world_name:=#{_worldName} name:=#{_name}</command>
</executable>
HEREDOC
end
Note that the launch files created earlier for description
and vehicle_topics
are added here to launch with the spawned model.
4. Finally, the vehicle can be launched with the following where the robotConfig# variable is passed into the custom vehicle spawn function from step 3.
ign launch <your_ign_launch_file_here>.ign robotName1:=husky_migration robotConfig1:=HUSKY_MIGRATION_SENSOR_CONFIG_1
Follow the Model Submission Process once your model is ready. In particular, make sure to go through the Robot Model Checklist. You may need to add optical frame publishers, static transform publishers, limit velocity and acceleration, and add wheel slip plugin if the robot is a wheeled vehicle.