Robot Simulation with Gazebo & Ignition | Robotics Development Hub

Gazebo (now Ignition/Gazebo) is the standard for high-fidelity robot simulation. This guide covers SDFormat modeling, sensor plugins, physics configuration, ROS 2 integration, headless CI/CD, and domain randomization for sim-to-real transfer. 1. Gazebo Versions & Architecture Version Series ROS 2 Distro Status Gazebo 11 Classic Humble EOL Ignition Fortress Ignition Humble/Iron EOL Ignition Garden Ignition Iron/Jazzy Supported Gazebo Harmonic Gazebo (new) Jazzy/Kilted Current Gazebo Ionic Gazebo (new) Kilted/Rolling Latest 2. SDFormat Models 2.1 Model Structure false 0 0 0.1 0 0 0 5.0 0.01 0 0 0.01 0 0.02 0 0 0.05 0 0 0 0.4 0.3 0.2 0.4 0.3 0.2 0.7 0.7 0.7 1 0.7 0.7 0.7 1 base_link wheel_left -0.2 0.15 0 0 0 0 0 1 0 -1e16 1e16 10 20 0.1 0.01 0.5 0.001 0.001 0.0005 0.05 0.04 0.05 0.04 2.2 Nested Models & Includes model://my_robot_description robot_1 0 0 0 0 0 0 # gz sdf -p robot.urdf > robot.sdf 3. Sensor Plugins 3.1 Camera Sensor 0.2 0 0.1 0 0 0 1.047 640 480 R8G8B8 0.1 100 gaussian 0.0 0.005 true 30 /camera/image_raw 3.2 IMU Sensor 0 0 0.1 0 0 0 true 200 /imu/data gaussian 0 0.001 0 0.0001 gaussian 0 0.01 0 0.001 /imu/data 3.3 Lidar Sensor 0 0 0.2 0 0 0 360 1 -3.14159 3.14159 1 1 0 0 0.1 30 0.01 gaussian 0 0.01 /scan 4. Physics Configuration 4.1 Physics Engine Settings 0.001 1.0 1000 quick 50 0 1.3 true 1e-5 0.2 100 0.001 dantzig 4.2 Contact Parameters 0.05 0.04 1.0 1.0 0.0 0.0 0.0 1e4 1e-5 0.2 1e8 1.0 0.01 0.001 5. ROS 2 - Gazebo Bridge (ros_gz) 5.1 Bridge Configuration bridges: # Clock - topic: /clock gz_topic: /clock type: rosgraph_msgs/msg/Clock direction: gz_to_ros # Joint states - topic: /joint_states gz_topic: /model/my_robot/joint_state type: sensor_msgs/msg/JointState direction: gz_to_ros # TF - topic: /tf gz_topic: /model/my_robot/tf type: tf2_msgs/msg/TFMessage direction: gz_to_ros # Odometry - topic: /odom gz_topic: /model/my_robot/odometry type: nav_msgs/msg/Odometry direction: gz_to_ros # Camera - topic: /camera/image_raw gz_topic: /camera type: sensor_msgs/msg/Image direction: gz_to_ros - topic: /camera/camera_info gz_topic: /camera_info type: sensor_msgs/msg/CameraInfo direction: gz_to_ros # IMU - topic: /imu/data gz_topic: /imu type: sensor_msgs/msg/Imu direction: gz_to_ros # Lidar - topic: /scan gz_topic: /lidar type: sensor_msgs/msg/LaserScan direction: gz_to_ros # Command velocity (ROS to Gazebo) - topic: /cmd_vel gz_topic: /model/my_robot/cmd_vel type: geometry_msgs/msg/Twist direction: ros_to_gz 5.2 Launch Bridge 5.3 Spawn Robot in Gazebo 6. Headless CI/CD Integration 6.1 Docker for Headless Simulation # Dockerfile FROM gazebo:harmonic # Install ROS 2 Jazzy RUN apt-get update && apt-get install -y \ ros-jazzy-ros-gz \ ros-jazzy-ros-gz-bridge \ ros-jazzy-ros-gz-sim \ && rm -rf /var/lib/apt/lists/* # Set up headless rendering ENV QT_QPA_PLATFORM=offscreen ENV GZ_RENDER_ENGINE=ogre2 ENV OGRE_RTT_MODE=Copy WORKDIR /workspace COPY . . # Run headless simulation CMD ["gz", "sim", "-v", "4", "-r", "world.sdf"] 6.2 GitHub Actions Workflow # .github/workflows/simulation-test.yml name: Simulation Tests on: [push, pull_request] jobs: simulation: runs-on: ubuntu-latest container: image: myorg/ros2-gazebo:harmonic-jazzy options: --shm-size=2g steps: - uses: actions/checkout@v4 - name: Build workspace run: | cd /workspace colcon build --symlink-install source install/setup.bash - name: Run headless simulation test run: | source install/setup.bash gz sim -v 4 -r worlds/empty.sdf & sleep 5 ros2 launch my_robot_bringup simulation.launch.py & sleep 10 ros2 run my_robot_test test_navigation.py kill %1 %2 - name: Upload logs if: always() uses: actions/upload-artifact@v4 with: name: simulation-logs path: ~/.gz/log/ 7. Domain Randomization for Sim-to-Real 7.1 Visual Randomization true 0.5 2.0 0.8 1.2 true /textures/random/ true 0.01 3 0.5 2.0 true 0.8 1.2 true 0.5 1.5 true -0.5 -0.5 -0.1 0.5 0.5 0.1 7.2 Randomization via ROS 2 # ROS 2 service to trigger randomization from std_srvs.srv import Trigger def randomize_simulation(): cli = node.create_client(Trigger, '/gazebo/randomize') req = Trigger.Request() future = cli.call_async(req) rclpy.spin_until_future_complete(node, future) return future.result() 8. Advanced: Custom Plugins 8.1 C++ Model Plugin // CustomPlugin.cc #include #include #include #include namespace my_plugins { class CustomController : public gz::sim::System, public gz::sim::ISystemConfigure, public gz::sim::ISystemPreUpdate { public: void Configure(const gz::sim::Entity& entity, const std::shared_ptr & sdf, gz::sim::EntityComponentManager& ecm, gz::sim::EventManager& event_mgr) override { model_ = gz::sim::Model(entity); joint_ = model_.JointByName(ecm, "wheel_joint"); } void PreUpdate(const gz::sim::UpdateInfo& info, gz::sim::EntityComponentManager& ecm) override { if (info.paused) return; // Simple velocity control double target_vel = 10.0; // rad/s double current_vel = joint_.Velocity(ecm); double error = target_vel - current_vel; double force = 5.0 * error; // P controller auto force_cmd = ecm.Component (joint_.Entity()); if (!force_cmd) { ecm.CreateComponent(joint_.Entity(), gz::sim::components::JointForceCmd({force})); } else { force_cmd->Data()[0] = force; } } private: gz::sim::Model model_; gz::sim::Joint joint_; }; GZ_ADD_PLUGIN(CustomController, gz::sim::System, CustomController::ISystemConfigure, CustomController::ISystemPreUpdate) } 8.2 Build Plugin # CMakeLists.txt cmake_minimum_required(VERSION 3.16) project(custom_plugins) find_package(gz-sim8 REQUIRED COMPONENTS gz-sim8) add_library(custom_controller SHARED CustomPlugin.cc) target_link_libraries(custom_controller gz-sim8::gz-sim8) target_include_directories(custom_controller PRIVATE ${GZ_SIM8_INCLUDE_DIRS}) install(TARGETS custom_controller LIBRARY DESTINATION lib) 9. Isaac Sim Integration 9.1 NVIDIA Isaac Sim (Alternative) # Isaac Sim provides: # - PhysX GPU physics (faster for large scenes) # - RTX rendering (photorealistic) # - ROS 2 bridge built-in # - ML-based domain randomization (Replicator) # Docker docker run --gpus all -it \ -e "ACCEPT_EULA=Y" \ -e "PRIVACY_CONSENT=Y" \ nvcr.io/nvidia/isaac-sim:4.2.0 # Python API from isaacsim import SimulationApp simulation_app = SimulationApp({"headless": True}) from omni.isaac.core import World world = World() world.scene.add_default_ground_plane() # Add robot from omni.isaac.core.articulations import Articulation robot = Articulation(prim_path="/World/robot", usd_path="robot.usd") world.reset() Summary SDFormat: Declarative model definition with nested includes Sensors: Camera, IMU, Lidar, GPU-accelerated plugins Physics: DART/ODE/Bullet, tune solver, contacts, friction ROS 2 Bridge: ros_gz parameter_bridge for bidirectional topics Headless CI: Docker + offscreen rendering + GitHub Actions Domain Randomization: Visual + physics randomization for sim-to-real Custom Plugins: C++ plugins for specialized behavior Start with ros_gz bridge for ROS 2 integration. Use headless Docker for CI. Add domain randomization before sim-to-real transfer. Consider Isaac Sim for GPU physics and photorealistic rendering. Related Articles ROS 2 Architecture Patterns Sim-to-Real Transfer Motion Planning Algorithms Robotics Software Testing

Comments

Popular posts from this blog

Firmware Testing Strategies: Hardware-in-the-Loop, Simulation, and CI/CD Pipelines | Embedded Systems Hub

IoT Protocols Compared: MQTT, CoAP, HTTP/2, WebSocket - Performance, Overhead, and Use Cases | Embedded Systems Hub

Embedded Security: Secure Boot, ARM TrustZone, and OTA Update Integrity | Embedded Systems Hub