What You’ll Build
By the end of this tutorial, you’ll have:- An enhanced maze world (
sim_house_enhanced.sdf.xacro) with 3-meter walls and colored PBR textures - ArUco marker models that can optionally spawn in the environment
- A parameterized launch system that lets you switch between the original and enhanced worlds
- A new Docker Compose service (
demo-world-enhanced) for one-command launch
This tutorial assumes familiarity with ROS 2 (Jazzy), Gazebo Sim (gz-sim), SDF/Xacro world files, and Docker Compose. The complete source is in the turtlebot-maze repository on the
feature/enhanced-maze branch.Prerequisites
- A working clone of the turtlebot-maze repository
- Docker and Docker Compose installed
- Basic familiarity with SDF world files and ROS 2 launch files
Step 1: Cherry-Pick Assets from a Feature Branch
Real-world repositories often accumulate useful assets in PRs that aren’t merge-ready. In our case, PR #3 contained ArUco marker models and wall textures mixed with broken spawners and hardcoded paths. Rather than merging the entire PR, we selectively cherry-pick the assets we need.Fix Model Mesh URIs
The cherry-picked ArUco models referenced meshes via Gazebo Fuel URLs, which require network access and fail inside Docker containers:model:// URIs that gz-sim resolves via GZ_SIM_RESOURCE_PATH:
Step 2: Create the Enhanced World SDF
The enhanced world is a copy of the originalsim_house.sdf.xacro with three modifications:
- Taller walls — height increased from 1m to 3m
- Shifted wall centers — z-center moved from 0.5 to 1.5 to match the new height
- PBR textures with color fallbacks — replacing the default
Gazebo/Woodmaterial
Understanding the Original SDF Structure
Each wall in the original SDF follows this pattern:Adding the Xacro Texture Argument
Add atexture_base xacro argument after the existing headless argument. This will receive the absolute path to the texture directory at launch time:
Transforming Wall Geometry
For all 30 walls, make these changes:| Property | Original | Enhanced |
|---|---|---|
<size> height (3rd component) | 1 | 3 |
<pose> z-center (3rd component) | 0.5 | 1.5 |
Replacing Materials with PBR Textures and Color Fallbacks
Gazebo Sim (gz-sim) uses the ogre2 render engine which supports PBR materials. Replace eachGazebo/Wood material block with a PBR <albedo_map> texture reference. Crucially, also add <ambient> and <diffuse> color tags as fallbacks — these ensure walls are visually distinct even when PBR textures don’t render:
| Texture | Ambient Color | Diffuse Color |
|---|---|---|
bricks.png | 0.7 0.3 0.2 (brick red) | 0.8 0.4 0.3 |
concrete.png | 0.6 0.6 0.6 (gray) | 0.7 0.7 0.7 |
wood_.png | 0.5 0.35 0.2 (brown) | 0.6 0.4 0.25 |
Step 3: Create the ArUco Marker Spawner
The ArUco marker spawner is a ROS 2 launch file that usesros_gz_sim’s gz_spawn_model.launch.py to place markers in the simulation.
Key Implementation Details
Why entity_name instead of name?
Why entity_name instead of name?
The
gz_spawn_model.launch.py in ROS 2 Jazzy declares its launch argument as entity_name, which it then maps to the name ROS parameter internally. Passing name directly as a launch argument has no effect — the parameter stays empty and the spawn fails.Why write temp SDF files?
Why write temp SDF files?
The
ros_gz_sim create node reads the SDF file and sends its content to the gz-sim server. If the SDF contains model:// URIs, the create node may crash (SIGABRT) because it tries to resolve them locally before the server does. Writing a temp SDF with absolute paths avoids this issue entirely.Why the 10-second delay?
Why the 10-second delay?
The ArUco spawner launches in parallel with gz-sim. If the
create service call arrives before gz-sim’s entity creation service is ready, the spawner crashes. A TimerAction delay gives gz-sim time to initialize.Step 4: Parameterize the Launch File
The existingtb_demo_world.launch.py hardcodes the world file. We add two new launch arguments to make it configurable:
tb_world.launch.py:
Passing texture_base Through to Xacro
Thetb_world.launch.py runs xacro on the world file. It needs to forward the texture_base argument so the enhanced SDF can resolve texture paths. Add a texture_base launch argument with a sensible default:
The default value
sim_house.sdf.xacro means existing users of demo-world are completely unaffected — the original world loads exactly as before.Step 5: Add the Docker Compose Service
Add a new service afterdemo-world in docker-compose.yaml:
GZ_SIM_RESOURCE_PATH environment variable is critical — it tells gz-sim where to find model:// URIs for mesh and texture resolution.
Step 6: Build and Launch
Verification
After launch, you should see:- Gazebo Sim — Tall colored walls (brick red, concrete gray, wood brown) instead of the original short gray walls
- RViz2 — The map loads and Nav2 shows “active” for both Navigation and Localization
- Nav2 — Fully functional; the same
sim_house_map.yamland costmaps work because the wall footprint is unchanged
lifecycle_manager_localization and one for lifecycle_manager_navigation.
Summary of Files Changed
| File | Change |
|---|---|
tb_worlds/worlds/textures/*.png | Cherry-picked wall textures |
tb_worlds/models/aruco_id_\{60,80\}/ | Cherry-picked ArUco models with fixed mesh URIs |
tb_worlds/worlds/sim_house_enhanced.sdf.xacro | New enhanced world (3m walls, PBR + colors) |
tb_worlds/launch/aruco_marker_spawner.launch.py | New ArUco spawner with temp SDF and delay |
tb_worlds/launch/tb_demo_world.launch.py | Added world_name and use_aruco params |
tb_worlds/launch/tb_world.launch.py | Added texture_base xacro passthrough |
docker-compose.yaml | Added demo-world-enhanced service |
Next Steps
- Add more textures — Download additional PBR texture packs and assign them per-room
- Fix ArUco spawning — The OBJ mesh spawning via
ros_gz_sim createcurrently triggers SIGABRT in some gz-sim versions; consider embedding markers directly in the world SDF instead - Create a SLAM-friendly variant — The taller walls improve SLAM performance since the lidar has more surface to reflect off of at different heights
- Add ceiling and floor textures — Complete the visual environment for camera-based navigation testing

