Skip to content

Model Migration Guide

acschang edited this page Oct 14, 2021 · 5 revisions

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.

URDF to SDF Conversion

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 command rosparam get robot_description -p > output.urdf

  • The robot_description parameter can be loaded into ROS using a command such as rosparam load <file_name>.xacro robot_description or by roslaunching the description.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 your output.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:// or package://. Relative URIs use a path that is relative to location of the model.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 and numpy.

  • 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.

Lights

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`)

Material tags

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.

Sensors

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 that gpu_ray and ray are deprecated. Use gpu_lidar and lidar 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 use sensor_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.

Rviz visualization

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:// to package:// in model.sdf.

  • Then run ign sdf -u model_package.sdf where model_package.sdf is the model.sdf file containing package:// 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.

Launch file setup

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

Model Submission

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.

Clone this wiki locally