gazebo
Build and debug modern Gazebo simulations and their ROS 2 boundary.
Install
npx skills add https://github.com/robium-ai/robium/tree/main/skills/gazebo
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install robium-ai-robium@llmmart
git clone https://github.com/robium-ai/robium.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole robium-ai/robium collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Gazebo
Treat a Gazebo simulation as a contract between world, robot, sensors, transport, and time. Find the first contract that is not producing believable evidence.
Start from the actual stack
- For a new robotics app or first demo, read architect before creating worlds, launchers, or containers. Reuse its compatible reference-app selection if already made. Existing-world edits, simulator debugging, and explanations stay here.
- Confirm the installed
gzrelease, ROS distro,ros_gzpairing, render backend, and whether the run is graphical or headless. - Use modern Gazebo (
gz). Gazebo Classic andlibgazebo_ros_*tutorials are a different, end-of-life stack. - Keep upstream compatibility tables and SDF specifications as the authority for release-sensitive package names, tags, and plugin APIs.
Trace the simulation boundary
- World: the selected world loads and the expected systems are present.
- Robot: links, joints, frames, and drive plugins agree with the physical model and downstream interfaces.
- Sensors: rates, fields of view, ranges, noise, frames, and timestamps model the intended hardware rather than tutorial defaults.
- Transport: prove the Gazebo topic exists before debugging its ROS bridge.
- Bridge: keep a reviewable YAML bridge configuration for a real app; ad-hoc bridge commands are suitable only for diagnosis.
- ROS: confirm
/clock, commands, odometry, transforms, and sensor messages arrive with the expected direction and QoS.
Do not infer simulation correctness from a running process or visible GUI. A render-backed sensor may be silent even while physics continues.
Go deeper only when needed
- For SDF worlds, models, drive plugins, spawning, and headless server mode, read worlds and models.
- For lidar, camera, IMU, contact, noise, and rendering, read sensors.
- For
ros_gz_bridge, topic direction, types, QoS,/clock, and frame overrides, read the ROS 2 bridge guide. - For a silent world, sensor, bridge, or remote launch, read failures.
- Use the bundled SDF and bridge YAML together only as a starting pair; keep their topic and frame names synchronized and verify syntax upstream.
Move to navigation only after the robot pose, sensor, map, and velocity
interfaces are valid. Move to ros2 when Gazebo is publishing correctly and
the fault is in the ROS graph. Use environments for container GPU/display
setup and simulation only when the simulator itself has not been chosen.
Done
- Physics time advances and the expected world and model are present.
- Every required Gazebo topic has a matching, correctly directed ROS interface.
- Sensor rate, frame, timestamp, and render output are believable.
- The run works in its intended graphical or headless environment.
- Release-specific claims match current Gazebo and ros_gz documentation.
Files (robium)
-
examples
-
diffdrive-world-snippet.sdf 10.9 KB · in bundle
-
ros-gz-bridge-config.yaml 3.2 KB
# status: unverified # Format sourced directly from gazebosim/ros_gz's ros_gz_bridge/README.md # ("Example 5: Configuring the Bridge via YAML"), fetched via raw GitHub # curl on 2026-07-10; every field name here (ros_topic_name, gz_topic_name, # ros_type_name, gz_type_name, direction, qos_profile, frame_id) is copied # from that file's own documented shape, not invented. # # Pairs with ../examples/diffdrive-world-snippet.sdf: every gz_topic_name # below matches, character-for-character, a <topic>/<odom_topic>/<tf_topic> # set explicitly in that world's DiffDrive plugin, or a sensor's <topic> # tag; gz-transport topic matching is exact-string (a leading slash on one # side and not the other, e.g. "cmd_vel" vs. "/cmd_vel", is a silent # non-match, not a typo Gazebo warns about). Renaming a topic in one file # without the other silently breaks that bridge entry; see the "Bridge # bridge shape in ../references/ros2-bridge.md. # # frame_id overrides below are set to "base_link" for scan/imu because the # example world mounts both sensors at base_link's own origin with no # separate offset frame (see that file's header for why); a robot with an # offset sensor frame should instead publish a static base_link -> sensor # transform (the `ros2` skill's territory) and drop these overrides so the # sensor's own frame_id survives the bridge. # # Run with: # ros2 run ros_gz_bridge parameter_bridge \ # --ros-args -p config_file:=ros-gz-bridge-config.yaml # Gazebo is the sole /clock source once bridged; see # ../references/ros2-bridge.md's "/clock needs special handling" section. # Every use_sim_time:=true ROS 2 node (including Nav2) depends on this. - ros_topic_name: "/clock" gz_topic_name: "/clock" ros_type_name: "rosgraph_msgs/msg/Clock" gz_type_name: "gz.msgs.Clock" direction: GZ_TO_ROS # Velocity commands into the sim (matches the DiffDrive plugin's # <topic>/cmd_vel</topic> in the paired world file). - ros_topic_name: "/cmd_vel" gz_topic_name: "/cmd_vel" ros_type_name: "geometry_msgs/msg/Twist" gz_type_name: "gz.msgs.Twist" direction: ROS_TO_GZ # Wheel odometry out of the sim (matches <odom_topic>/odom</odom_topic>). # The navigation configuration and robot model must agree with the "odom" / # "base_link" frames set by the paired world's DiffDrive plugin. - ros_topic_name: "/odom" gz_topic_name: "/odom" ros_type_name: "nav_msgs/msg/Odometry" gz_type_name: "gz.msgs.Odometry" direction: GZ_TO_ROS # odom -> base_link transform (matches <tf_topic>/tf</tf_topic>). - ros_topic_name: "/tf" gz_topic_name: "/tf" ros_type_name: "tf2_msgs/msg/TFMessage" gz_type_name: "gz.msgs.Pose_V" direction: GZ_TO_ROS # Lidar scan (matches the gpu_lidar sensor's <topic>/scan</topic>). Bridge it to # the same ROS topic selected by the installed localization configuration. - ros_topic_name: "/scan" gz_topic_name: "/scan" ros_type_name: "sensor_msgs/msg/LaserScan" gz_type_name: "gz.msgs.LaserScan" direction: GZ_TO_ROS qos_profile: SENSOR_DATA frame_id: "base_link" # IMU (matches the imu sensor's <topic>/imu</topic>). - ros_topic_name: "/imu" gz_topic_name: "/imu" ros_type_name: "sensor_msgs/msg/Imu" gz_type_name: "gz.msgs.IMU" direction: GZ_TO_ROS qos_profile: SENSOR_DATA frame_id: "base_link"
-
-
references
-
ros2-bridge.md 5.6 KB
# The ros_gz bridge `ros_gz_bridge`'s `parameter_bridge` exchanges messages between ROS 2 topics/ services and Gazebo Transport topics/services. Everything below is sourced directly from the `gazebosim/ros_gz` repo's `ros_gz_bridge/README.md` (`ros2` branch), fetched via raw GitHub `curl` on 2026-07-10; the CLI syntax, the YAML field list, and the message-type table are copied from that file's own examples, not reconstructed from memory or search snippets. ## ROS 2 / Gazebo pairing (verify before installing) From the same repo's top-level `README.md`, fetched directly on 2026-07-10: | ROS 2 | Gazebo | Binaries | |---|---|---| | Jazzy | Harmonic | packages.ros.org (`jazzy` branch) | | Jazzy | Garden | source only; Garden is EOL | | Kilted | Ionic | packages.ros.org | | Lyrical | Jetty | packages.ros.org | | Lyrical | Harmonic / Ionic | source only | | Rolling | Jetty | packages.ros.org | | Rolling | Harmonic / Ionic | source only | The current `navigation` skill's proven TurtleBot3 profile targets Jazzy and Harmonic, so this reference's navigation examples use that row with prebuilt binaries (`sudo apt-get install ros-jazzy-ros-gz`). Re-fetch this table before picking a pairing for a different ROS 2 distro; it changes with every new named Gazebo or ROS 2 release. ## Ad-hoc CLI bridging (one-off use only) ```bash ros2 run ros_gz_bridge parameter_bridge \ /chatter@std_msgs/msg/String@gz.msgs.StringMsg ``` The `@` syntax is bidirectional; `[` (ROS→GZ dropped, i.e. GZ→ROS only) or `]` (ROS→GZ only) replace the middle `@` for a unidirectional bridge, e.g. `/clock@rosgraph_msgs/msg/Clock[gz.msgs.Clock` for the one-way `/clock` bridge Gazebo itself recommends (see below). This skill's key directives reserve ad-hoc CLI bridges for quick tests; a real project's bridge set belongs in a YAML config file (below). ## YAML config file (the recommended approach) Pass a YAML file via the `config_file` ROS parameter: ```bash ros2 run ros_gz_bridge parameter_bridge \ --ros-args -p config_file:=path/to/bridge.yaml ``` The file is a YAML array of maps, one per bridge. Every field below is copied from the README's own "Example 5" (`ros_gz_bridge/test/config/ full.yaml`'s documented shape): ```yaml - ros_topic_name: "ros_chatter" gz_topic_name: "gz_chatter" ros_type_name: "std_msgs/msg/String" gz_type_name: "gz.msgs.StringMsg" subscriber_queue: 5 # default 10 if qos_profile is empty publisher_queue: 6 # default 10 if qos_profile is empty lazy: true # default false; only bridge while someone's subscribed direction: BIDIRECTIONAL # or GZ_TO_ROS / ROS_TO_GZ qos_profile: SENSOR_DATA # default: a default-constructed QoS frame_id: "map" # optional: override the ROS message header's frame_id ``` `topic_name` sets both sides at once if `ros_topic_name`/`gz_topic_name` aren't given separately. `direction` defaults to `BIDIRECTIONAL`; sensor data and `/clock` should be `GZ_TO_ROS`, and command topics like `/cmd_vel` should be `ROS_TO_GZ`; see `examples/ros-gz-bridge-config.yaml`. YAML config does not support launch-time substitutions (e.g. a parametrized world/robot name); use the XML or Python launch-file forms (`<ros_gz_bridge>` tags, or a `parameter_bridge` `Node` with `bridges.<name>.<setting>` parameters) instead when that's needed; both are documented in the same README ("Example 6"/"Example 7") and can be combined with a YAML `config_file` on the same bridge instance. ## Message type table (subset relevant to this skill) Full table is much longer (services, geometry, actuator types); this is the subset this skill's examples use, copied verbatim from the README: | ROS type | Gazebo Transport type | |---|---| | `rosgraph_msgs/msg/Clock` | `gz.msgs.Clock` | | `geometry_msgs/msg/Twist` | `gz.msgs.Twist` | | `nav_msgs/msg/Odometry` | `gz.msgs.Odometry` | | `tf2_msgs/msg/TFMessage` | `gz.msgs.Pose_V` | | `sensor_msgs/msg/LaserScan` | `gz.msgs.LaserScan` | | `sensor_msgs/msg/Imu` | `gz.msgs.IMU` | | `sensor_msgs/msg/Image` | `gz.msgs.Image` | | `sensor_msgs/msg/CameraInfo` | `gz.msgs.CameraInfo` | See the upstream README for the complete table (batteries, GPS, point clouds, detections, and more) before bridging a type not listed here. ## `/clock` needs special handling If Gazebo detects another `/clock` publisher at startup, it falls back to a fully-qualified `/world/<world>/clock` topic instead, so bridge `/clock` explicitly and unidirectionally (`GZ_TO_ROS`) so Gazebo stays the sole `/clock` source: every `use_sim_time:=true` node (Nav2 included; see the `navigation` skill's TurtleBot3 compatibility card) depends on this topic actually publishing, or every TF/action timestamp check hangs waiting for a clock that never arrives. ## `frame_id` / `override_frame_id` Two related mechanisms, both from the README: the YAML config's `frame_id` field (shown above) overrides any bridged message's header `frame_id`. The bridge node's `override_frame_id` ROS parameter does the same thing for a single ad-hoc CLI-bridged topic, e.g. `--ros-args -p override_frame_id:=my_optical_frame`. Cameras are the most common case: Gazebo's camera frame convention isn't ROS's z-forward optical-frame convention (REP-103), so bridged `Image`/`CameraInfo` topics typically need this set to whatever optical frame a static transform publishes. ## Other bridge parameters `subscription_heartbeat` (default 1000 ms; how often the bridge checks for new subscribers on `lazy` bridges) and `expand_gz_topic_names` (default `false`; apply the bridge node's ROS namespace to the Gazebo-side topic name too, needed when spawning multiple namespaced robots in one world). -
sensors.md 6.2 KB
# Sensors SDF sensor tags for IMU, contact, lidar, and camera, the system plugins each depends on, and the shared `<noise>` element. All tags below were verified on 2026-07-10 by direct `curl` of the raw source files cited per section; re-verify before trusting a specific default in a real project. ## Shared pattern Every sensor is a `<sensor name="..." type="...">` element inside a `<link>`, with common tags `<always_on>` (update per `<update_rate>` if true), `<update_rate>` (Hz), `<visualize>` (show in the GUI), and `<topic>` (the gz topic data is published on; set explicitly for a flat, predictable name rather than relying on the model-scoped default). Source: Gazebo's Sensors tutorial (`harmonic/sensors.md`, `gazebosim/docs` repo, fetched directly on 2026-07-10; the tutorial that ships with the Harmonic release this skill's nav-facing examples target). ## IMU Requires the `gz-sim-imu-system` world plugin (`gz::sim::systems::Imu`). Outputs `orientation` (quaternion), `angular_velocity`, and `linear_acceleration`: ```xml <!-- under <world> --> <plugin filename="gz-sim-imu-system" name="gz::sim::systems::Imu" /> <!-- under a <link> --> <sensor name="imu_sensor" type="imu"> <always_on>1</always_on> <update_rate>100</update_rate> <visualize>true</visualize> <topic>imu</topic> </sensor> ``` No GPU/render engine dependency; IMU is computed directly from the physics engine's link state. ## Contact Requires `gz-sim-contact-system`. Reports when a named collision touches another: ```xml <plugin filename="gz-sim-contact-system" name="gz::sim::systems::Contact" /> <sensor name="sensor_contact" type="contact"> <contact> <collision>collision</collision> </contact> </sensor> ``` Commonly paired with `gz-sim-touchplugin-system` (fires when a named `<target>` model touches the sensor's owner, publishing to `<namespace>/touched`) and `gz-sim-triggered-publisher-system` (publishes a configured message when an input topic matches a pattern, e.g. stop the robot on contact). No GPU dependency. ## Lidar Use the current preferred type/tag `type="gpu_lidar"` with a `<lidar>` element; `type="ray"`/`type="gpu_ray"` with a `<ray>` element are legacy aliases, structurally identical but flagged for eventual deprecation in the SDF spec (source: `sdformat`'s `sensor.sdf` schema file, fetched directly on 2026-07-10: "It is preferred to use ... 'lidar', 'gpu_lidar' ... since 'ray', 'gpu_ray' ... will be deprecated"). Requires the `gz-sim-sensors- system` world plugin with an explicit render engine (GPU-rendered, unlike IMU/contact): ```xml <!-- under <world> --> <plugin filename="gz-sim-sensors-system" name="gz::sim::systems::Sensors"> <render_engine>ogre2</render_engine> </plugin> <!-- under a <link> --> <sensor name="gpu_lidar" type="gpu_lidar"> <topic>scan</topic> <update_rate>10</update_rate> <lidar> <scan> <horizontal> <samples>640</samples> <resolution>1</resolution> <min_angle>-1.396263</min_angle> <max_angle>1.396263</max_angle> </horizontal> <vertical> <samples>1</samples> <resolution>0.01</resolution> <min_angle>0</min_angle> <max_angle>0</max_angle> </vertical> </scan> <range> <min>0.08</min> <max>10.0</max> <resolution>0.01</resolution> </range> <noise> <type>gaussian</type> <mean>0.0</mean> <stddev>0.01</stddev> </noise> </lidar> <always_on>1</always_on> <visualize>true</visualize> </sensor> ``` `<samples>`/`<resolution>` under `<horizontal>`/`<vertical>` control ray count and interpolation; `<range>` bounds each ray's min/max distance and linear resolution. Source: Gazebo's Sensors tutorial for the `<lidar>` child tags, `sdformat`'s `lidar.sdf` schema file for the authoritative element list including `<noise>` (both fetched directly on 2026-07-10; the tutorial itself still shows the legacy `<ray>` tag; this reference recommends `<lidar>` per the schema's own deprecation note above). ## Camera Also requires `gz-sim-sensors-system` (GPU-rendered): ```xml <sensor name="camera" type="camera"> <camera> <horizontal_fov>1.047</horizontal_fov> <image> <width>640</width> <height>480</height> </image> <clip> <near>0.1</near> <far>100</far> </clip> </camera> <always_on>1</always_on> <update_rate>30</update_rate> <visualize>true</visualize> <topic>camera</topic> </sensor> ``` Source: `gz-sim`'s `examples/worlds/camera_sensor.sdf`, fetched directly via raw GitHub on 2026-07-10. Camera images bridge to ROS through `ros_gz_image` (unidirectional, `image_transport`-based) or `ros_gz_bridge`'s `sensor_msgs/msg/Image` mapping; see `references/ros2-bridge.md`. The bridge's `override_frame_id` parameter is the documented way to point a bridged `Image`/`CameraInfo` message's `frame_id` at a proper z-forward optical frame (REP-103), since Gazebo's own camera frame convention differs from ROS's. ## Noise (shared element) The `<noise type="...">` element (`sdformat`'s `noise.sdf` schema, fetched directly on 2026-07-10) is the general form used across sensor types beyond the lidar-specific block shown above: | Tag | Applies to `type="gaussian*"` | |---|---| | `<mean>` | mean of the sampled noise value | | `<stddev>` | standard deviation of the sampled noise value | | `<bias_mean>` / `<bias_stddev>` | a per-run constant bias, itself drawn from a Gaussian | | `<dynamic_bias_stddev>` / `<dynamic_bias_correlation_time>` | slow bias drift over time (e.g. IMU gyro drift) | `type="gaussian_quantized"` additionally rounds outputs to a `<precision>` step. A sensor left at the tutorial/example defaults (frequently zero noise) will not expose the same failure modes a downstream consumer will hit against the real sensor; set noise from the real sensor's datasheet and use the `simulation` skill's sensor-fidelity card when transfer matters. ## GPU dependency and headless rendering IMU and contact sensors have no render-engine dependency. Camera and `gpu_lidar` sensors do, via `gz-sim-sensors-system`'s render engine (OGRE2 by default); see this skill's [failure router](../FAILURES.md) for headless-rendering and GPU-vs-software-rendering guidance when running these sensor types without a display or GPU. -
worlds-and-models.md 7 KB
# Worlds and models SDF (Simulation Description Format) world/model anatomy for modern Gazebo (`gz`), how to spawn a model into a running simulation from ROS 2, and how to run headless. All tag names, plugin filenames, and CLI flags below were verified on 2026-07-10 by direct `curl` of the raw source files cited in each section; re-verify before trusting a specific value in a real project, per the source-of-truth rule in this skill's entrypoint. ## Release/pairing status (verify before using) Fetched directly via `curl https://gazebosim.org/docs/all/releases/` this session (HTML stripped, table read from the rendered text, not AI-summarized search snippets): | Codename | Start | EOL | Support | |----------|----------|----------|-------------| | Jetty | Sep 2025 | May 2031 | LTS | | Ionic | Sep 2024 | Dec 2026 | Standard | | Harmonic | Sep 2023 | May 2029 | LTS | | Fortress | Sep 2021 | May 2027 | LTS | | Garden | Sep 2022 | Nov 2024 | EOL | Re-fetch this page before starting a new project; Gazebo ships a new named release roughly every year, and the "current LTS" answer shifts each time (Jetty superseded Harmonic/Ionic as the newest LTS after the original reference was written). ## SDF world structure An SDF file's root is `<sdf version="...">`, containing one or more `<world name="...">` elements. A world needs at minimum a physics engine and the scene-broadcaster system plugin to be usable from the GUI or `gz topic`; adding *any* `<plugin>` to a world that had none disables Gazebo's implicit defaults, so an explicit minimal set is required once you add your first plugin (source: Gazebo's Sensors tutorial, `harmonic/sensors.md`, fetched directly on 2026-07-10): ```xml <sdf version="1.9"> <world name="demo"> <plugin filename="gz-sim-physics-system" name="gz::sim::systems::Physics" /> <plugin filename="gz-sim-scene-broadcaster-system" name="gz::sim::systems::SceneBroadcaster" /> <plugin filename="gz-sim-user-commands-system" name="gz::sim::systems::UserCommands" /> <!-- add gz-sim-sensors-system if the world has camera/gpu_lidar sensors; see references/sensors.md --> </world> </sdf> ``` `<physics name="1ms" type="ignored">` with `<max_step_size>`/ `<real_time_factor>` children controls the simulation step; a `<light type="directional">` and a static `ground_plane` model are the usual minimum for a visually sane world (source: `gazebosim/docs`' `harmonic/tutorials/moving_robot/moving_robot.sdf`, fetched directly this session). ## Model, link, and joint anatomy A `<model name="..." canonical_link="...">` contains `<link>` elements (each with `<inertial>`, `<visual>`, `<collision>`), `<joint>` elements connecting pairs of links (`<parent>`/`<child>`, an `<axis>` with `<limit>` for revolute joints), and optional `<frame>` elements for named offset poses (e.g. where a sensor attaches) without adding an extra link. Poses (`<pose relative_to="...">`) are relative by default to the model's own frame unless `relative_to` names another link or frame; this is how a sensor mounted on `chassis` gets positioned without redundant math. See `examples/diffdrive-world-snippet.sdf` for a worked single-link example; the upstream `moving_robot.sdf` (cited above) shows the fuller chassis+two-wheels+caster pattern this example is adapted from. ## Referencing models with `<include>` `<include><uri>...</uri></include>` pulls in a model either from a local path, a Fuel URL (`https://fuel.gazebosim.org/1.0/<owner>/models/<name>`), or a `package://<ros_pkg>/<path>` URI once a ROS package exports its model directory via `package.xml`'s `<export><gazebo_ros gazebo_model_path="${prefix}/../"/></export>` tag (source: `ros_gz_sim` README, fetched directly on 2026-07-10; see the tag's exact semantics there, including the `${prefix}` expansion to the package's installed share path). ## The `DiffDrive` system plugin `gz-sim-diff-drive-system` (`gz::sim::systems::DiffDrive`) is the standard differential-drive controller. Parameters (source: `gz-sim`'s `DiffDrive.hh` header comment, fetched directly via raw GitHub this session; this is the plugin's own doc, not a summary): | Tag | Default | Notes | |---|---|---| | `<left_joint>` / `<right_joint>` | (none) | required, repeatable for multi-wheel | | `<wheel_separation>` | 1.0 m | | | `<wheel_radius>` | 0.2 m | | | `<odom_publish_frequency>` | 50 Hz | | | `<topic>` | `/model/<model>/cmd_vel` | set explicitly for a flat, unscoped name | | `<odom_topic>` | `/model/<model>/odometry` | | | `<tf_topic>` | `/model/<model>/tf` | publishes `gz.msgs.Pose_V` | | `<frame_id>` | `<model>/odom` | the odometry/TF parent frame | | `<child_frame_id>` | `<model>/<link>` | the odometry/TF child frame | | `<min_velocity>`/`<max_velocity>`, `*_acceleration`, `*_jerk` | unset | optional linear/angular limits, each overridable per-axis | Setting `<topic>/cmd_vel</topic>`, `<odom_topic>/odom</odom_topic>`, `<frame_id>odom</frame_id>`, and `<child_frame_id>base_link</child_frame_id>` explicitly (rather than leaving the model-scoped defaults) is what makes a single-robot world's gz topics and TF frames line up with what Nav2 expects (`odom`→`base_link`) without a namespace prefix; see `examples/diffdrive-world-snippet.sdf`. ## Spawning a robot from SDF/URDF `ros_gz_sim`'s `create` executable (source: `ros_gz_sim/src/create.cpp`, fetched directly on 2026-07-10; flags read from its `gflags::DEFINE_*` declarations, not inferred) spawns an entity via a ROS 2 process rather than a raw Gazebo Transport service call: ```bash ros2 run ros_gz_sim create -world default -file /path/to/model.sdf \ -name my_robot -x 0 -y 0 -z 0.1 ``` Flags: `-world`, `-file` (path or Fuel URL), `-param` (load XML from a ROS parameter), `-string` (load XML from a literal string), `-topic` (subscribe to a latched `std_msgs/msg/String` publisher; the pattern for spawning a URDF that `robot_state_publisher` already published to `/robot_description`), `-name`, `-allow_renaming`, and `-x`/`-y`/`-z`/`-R`/ `-P`/`-Y` for the initial pose. `ros2 run ros_gz_sim create --helpshort` prints the live flag list; check it against a specific `ros_gz` version before scripting around it. Both input shapes occur in current vendor applications: TurtleBot3 supplies a model with `-file`, while TurtleBot4 publishes `robot_description` and spawns with `-topic`. Choose from the model ownership boundary rather than treating one form as merely illustrative. ## Running headless `gz sim -s -r <world>.sdf`: `-s` runs the server only ("headless mode", overriding `-g` if also given) and `-r` starts the simulation running immediately rather than paused. Add `-v 4` for debug-level console output, `--headless-rendering` if the world has GPU-rendered sensors and there is no display (see `references/sensors.md`), and `--record` (or `--record-path <dir>`) to log state for later playback. Source: `gz-sim`'s own CLI help text, `src/cmd/cmdsim.rb.in`, fetched directly via raw GitHub on 2026-07-10; this is the authoritative flag list, not a tutorial's subset of it.
-
-
FAILURES.md 3.1 KB
# Gazebo failures Use the symptom to locate the failed simulation contract. - **`gz` is not found in a ROS image** - Some ROS packages vendor the CLI beneath `/opt/ros/<distro>`; check that the ROS environment is sourced before installing another Gazebo. - `gz stats` belongs to Gazebo Classic. For modern Gazebo, inspect the world's stats topic and confirm syntax with the current CLI help. - **The server runs but camera or lidar data is absent** - Camera and `gpu_lidar` depend on the render engine even in server-only mode. - Check OGRE2/EGL or the software renderer and use the current headless- rendering option when no display is present. - Inspect vendor robot xacro and included model files before adding a Sensors system to the world. In the 2026-09-07 Silly TurtleBot Jazzy container, the Create 3 model already owned a Sensors system fixed to Ogre 1; adding a second Ogre2 system produced duplicate scene/HLMS failures. Patching that exact installed xacro to Ogre2 while retaining one Sensors system restored a roughly 3.5 Hz simulated OAK-D stream. Treat this as package-specific evidence and re-inspect the installed source before applying it elsewhere. - Physics or odometry continuing does not prove render-backed sensors work. - **A vendor demo always opens a GUI** - Some vendor top-level launch files hardcode a client or do not expose headless arguments. In Robium's 2026-07-11 TurtleBot3/Jazzy trial and a 2026-08-02 TurtleBot4 source review, this happened through two different launch structures. - Compose the upstream Gazebo server launch with explicit headless arguments and reuse the vendor's parameterized spawn, state-publisher, and bridge sub-launches rather than copying the entire top-level file. - **Gazebo topics exist but ROS topics do not** - Compare the exact Gazebo topic, ROS topic, message types, direction, and bridge configuration. - Treat `/clock` separately; without a healthy clock, downstream nodes using simulation time can look like bridge or TF failures. - **Clients repeat `Requesting list of world names.`** - Gazebo Transport discovery uses UDP multicast; cloud sandboxes, container networks, and some VPNs may drop it. - On a same-host deployment, Robium's 2026-07-12 Cloud Run trial restored unicast discovery with `GZ_RELAY=127.0.0.1` and `GZ_IP=127.0.0.1`. Multi-host setups require a real peer address and a network design. - That trial also found a sticky per-boot `SO_REUSEPORT` race: relayed announcements reached one of several local sockets and a failed boot did not recover. A roughly 120-second no-data watchdog that recycled the instance was more effective than an in-process retry loop. These values are evidence from those Cloud Run conditions, not universal defaults. - **It works natively on macOS but not with ROS** - Gazebo itself may have a native macOS package; the `ros_gz` path still requires a ROS 2 environment, which normally means Linux or a container. For exact headless flags and bridge fields, use the current CLI help and the official documentation linked from the main skill. -
SKILL.md 3.1 KB
--- name: gazebo description: Build and debug modern Gazebo simulations and their ROS 2 boundary. --- # Gazebo Treat a Gazebo simulation as a contract between world, robot, sensors, transport, and time. Find the first contract that is not producing believable evidence. ## Start from the actual stack - For a new robotics app or first demo, read [architect](../architect/SKILL.md) before creating worlds, launchers, or containers. Reuse its compatible reference-app selection if already made. Existing-world edits, simulator debugging, and explanations stay here. - Confirm the installed `gz` release, ROS distro, `ros_gz` pairing, render backend, and whether the run is graphical or headless. - Use modern Gazebo (`gz`). Gazebo Classic and `libgazebo_ros_*` tutorials are a different, end-of-life stack. - Keep upstream compatibility tables and SDF specifications as the authority for release-sensitive package names, tags, and plugin APIs. ## Trace the simulation boundary - **World:** the selected world loads and the expected systems are present. - **Robot:** links, joints, frames, and drive plugins agree with the physical model and downstream interfaces. - **Sensors:** rates, fields of view, ranges, noise, frames, and timestamps model the intended hardware rather than tutorial defaults. - **Transport:** prove the Gazebo topic exists before debugging its ROS bridge. - **Bridge:** keep a reviewable YAML bridge configuration for a real app; ad-hoc bridge commands are suitable only for diagnosis. - **ROS:** confirm `/clock`, commands, odometry, transforms, and sensor messages arrive with the expected direction and QoS. Do not infer simulation correctness from a running process or visible GUI. A render-backed sensor may be silent even while physics continues. ## Go deeper only when needed - For SDF worlds, models, drive plugins, spawning, and headless server mode, read [worlds and models](references/worlds-and-models.md). - For lidar, camera, IMU, contact, noise, and rendering, read [sensors](references/sensors.md). - For `ros_gz_bridge`, topic direction, types, QoS, `/clock`, and frame overrides, read [the ROS 2 bridge guide](references/ros2-bridge.md). - For a silent world, sensor, bridge, or remote launch, read [failures](FAILURES.md). - Use the bundled SDF and bridge YAML together only as a starting pair; keep their topic and frame names synchronized and verify syntax upstream. Move to `navigation` only after the robot pose, sensor, map, and velocity interfaces are valid. Move to `ros2` when Gazebo is publishing correctly and the fault is in the ROS graph. Use `environments` for container GPU/display setup and `simulation` only when the simulator itself has not been chosen. ## Done - Physics time advances and the expected world and model are present. - Every required Gazebo topic has a matching, correctly directed ROS interface. - Sensor rate, frame, timestamp, and render output are believable. - The run works in its intended graphical or headless environment. - Release-specific claims match current [Gazebo](https://gazebosim.org/docs/) and [ros_gz](https://github.com/gazebosim/ros_gz) documentation.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.