Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Licenses

Check any licenses in repositories in vendor if you decide to use them. This repository’s owner holds no liability for misuse.

Folder structure

The folders below have special meanings, the others follow usual naming.

FolderMeaning
sdfBase Gazebo scene files.
vendorVendor models. Check the licenses if you decide to use them in your work. They are not shared in this repo
convertVendor-model to Gazebo-SDF conversion scripts. See convert/README.md for usage.

Vendor Setup

Vendor robot models are not shared in this repository. scripts/setup_vendor.sh clones them into ~/.sas/sas_robot_driver_gazebo/vendor, skipping any that are already present:

# Clone a single vendor's models
./scripts/setup_vendor.sh ur

# Clone all supported vendors (ur, unitree, bota, agilex)
./scripts/setup_vendor.sh all

ROS 2 Nodes & Parameters

Each node loads its parameters from a YAML configuration file. The default is config/config.yaml in this package; pass a different file with the config_file:= launch argument of the corresponding launch file.

Node: sas_robot_driver_ros_gazebo

PropertyValue
Executablesas_robot_driver_ros_gazebo_node (C++, default of robot_driver_server_launch.py), or sas_robot_driver_ros_gazebo.py (implementation:=python)
ROS node nameur3e_1 (set by the name launch argument of robot_driver_server_launch.py)
DescriptionBridges ROS and Gazebo. Subscribes to Gazebo joint states and publishes target joint positions, running the RobotDriverROS control loop.

Parameters

ParameterTypeMandatory / OptionalDefaultPurpose
joint_namesarray of stringsMandatorynone — must be providedNames of the Gazebo joints to control
joint_positions_topic_prefixstringMandatorynone — must be providedGazebo topic prefix for target joint positions
joint_states_topicstringMandatorynone — must be providedGazebo topic of the joint states to read
robot_namestringMandatorynone — must be providedName of the robot; used as the robot-driver topic prefix
thread_sampling_time_secdoubleOptional0.002Sampling period of the control-loop thread (see Bridge loop period)

Node: sas_object_server_gazebo_node

PropertyValue
Executablesas_object_server_gazebo_node
ROS node namesas_object_server_gazebo_node
DescriptionSynchronizes the poses of the configured world entities with ROS: sends poses to the Gazebo set_pose service and publishes them on the get_pose topic.

Parameters

ParameterTypeMandatory / OptionalDefaultPurpose
set_pose_service_namestringMandatorynone — must be providedGazebo set_pose service name
get_pose_topic_namestringMandatorynone — must be providedGazebo absolute-pose info topic name
entity_namesarray of stringsMandatorynone — must be providedWorld entities whose poses are synchronized
thread_sampling_time_secdoubleOptional0.01Sampling period of the synchronization loop

Node: sas_simulator_server_gazebo_node

PropertyValue
Executablesas_simulator_server_gazebo_node
ROS node namesas_simulator_server_gazebo_node
DescriptionBinds Gazebo world control services (start/stop/step simulation) to ROS.

Parameters

ParameterTypeMandatory / OptionalDefaultPurpose
service_namestringMandatorynone — must be providedGazebo world control service name (e.g. /world/<world>/control)
thread_sampling_time_secdoubleOptional0.01Sampling period of the control loop
autostartboolOptionaltrueStart the simulation on node startup

How mandatory/optional is determined in code:

Launch arguments (simulator_server_launch.py): name and config_file. The simulation is autostarted by default (autostart: true in the configuration file and in the node’s in-code default); to disable it, pass a configuration file that sets autostart: false (e.g. ros2 launch sas_robot_driver_gazebo simulator_server_launch.py config_file:=/path/to/no_autostart.yaml).

Sample launches

ros2 launch sas_robot_driver_gazebo robot_driver_server_launch.py
ros2 launch sas_robot_driver_gazebo object_server_launch.py
ros2 launch sas_robot_driver_gazebo simulator_server_launch.py

SDF Serial-Manipulator Loader

# Print the kinematics of a serial-manipulator SDF model as YAML.
sdf2manipulator sdf/r820.sdf

# Also print the per-joint limits.
sdf2manipulator sdf/r820.sdf --joint-limits

# Load the kinematic model from a world file (auto-detected).
sdf2manipulator sdf/ur3e_world.sdf

# Select a specific (possibly nested) model by its "::"-separated scope.
sdf2manipulator sdf/ur3e_world.sdf --model-scope "ur3e::ur3e_position_controller::ur3e"

r820.sdf (7 revolute joints) and ur30.sdf / ur3e.sdf (6 revolute joints each) are ready-made model-file test inputs, and r820_world.sdf / ur3e_world.sdf are ready-made world-file inputs that nest the same robots behind <include> chains; all have every joint acting about +z.

The C++ driver

RobotDriverGazebo is implemented in C++ (include/sas_robot_driver_gazebo/sas_robot_driver_gazebo.hpp). The bridge node sas_robot_driver_ros_gazebo_node uses it directly; Python scripts use it through its pybind11 binding, re-exported by the package:

from marinholab.sas.core import ShutdownSignaler
from sas_robot_driver_gazebo import RobotDriverGazebo, RobotDriverGazeboConfiguration

RobotDriverGazebo is a marinholab.sas.core.RobotDriver that sas_robot_driver.RobotDriverROS accepts, with the constructor, methods and configuration attribute of the former pure-Python driver, which it replaces; its getters return NumPy arrays. The binding is built with the pybind11 that sas_robot_driver and marinholab-sas-core use (the pybind11 submodule, branch v3.0, at sas_robot_driver’s commit; run git submodule update --init) and py::smart_holder, as they bind RobotDriver.

The pure-Python driver reported joint positions seconds behind Gazebo’s, or not at all: sas_robot_driver.RobotDriverROS.control_loop() holds the Python interpreter lock while its C++ loop sleeps, so the gz-transport thread running its Python joint-state callback rarely ran. The C++ driver’s callback takes no interpreter lock; with it, the bridge reports Gazebo’s joints with no measurable lag at 2 ms and 1 ms. The measurements, the profile and how to reproduce them are in BENCHMARK.md.

Considerations

Bridge loop period

The bridge runs its RobotDriverROS loop every thread_sampling_time_sec, 0.002 s (500 Hz) by default, as the C++ driver keeps up at that rate and at 0.001 s. With the former pure-Python driver it did not: its reported joint positions fell seconds behind Gazebo’s, because sas_robot_driver.RobotDriverROS.control_loop() holds the Python interpreter lock while its C++ loop sleeps, and #20 set 0.01 s as a mitigation. See The C++ driver and BENCHMARK.md.