> ## Documentation Index
> Fetch the complete documentation index at: https://aegean.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Gazebo worlds and the SDF/Xacro format

> World file anatomy, the SDF + Xacro specification, and a walkthrough of the AWS RoboMaker Small House environment with 68 residential models.

This page explains how Gazebo simulation worlds are structured, introduces the SDF and Xacro file formats, and walks through the AWS RoboMaker Small House world used in the TurtleBot3 demos.

## Prerequisites

* Basic familiarity with XML
* Docker and Docker Compose installed ([install guide](https://docs.docker.com/engine/install/ubuntu/))
* Clone the [turtlebot-maze repository](https://github.com/pantelis/turtlebot-maze)

***

## The SDF world file format

Simulation Description Format (SDF) is the XML schema used by Gazebo to describe simulation worlds. Every world file has the following top-level structure:

```xml theme={null}
<?xml version="1.0"?>
<sdf version="1.6">
  <world name="my_world">
    {/* Plugins */}
    {/* Physics */}
    {/* Lighting */}
    {/* Models */}
  </world>
</sdf>
```

### Plugins

Gazebo Sim (Harmonic) is plugin-based. Each capability, physics, sensors, scene rendering, is a loadable shared library declared in the world file:

```xml theme={null}
<plugin filename="gz-sim-physics-system"
        name="gz::sim::systems::Physics"/>
<plugin filename="gz-sim-sensors-system"
        name="gz::sim::systems::Sensors">
  <render_engine>ogre2</render_engine>
</plugin>
<plugin filename="gz-sim-imu-system"
        name="gz::sim::systems::Imu"/>
```

<Note>
  `gz-sim-scene-broadcaster-system` is only loaded when the simulation is not running headless. The Small House world uses an Xacro argument to control this:

  ```xml theme={null}
  <xacro:unless value="$(arg headless)">
    <plugin filename="gz-sim-scene-broadcaster-system"
            name="gz::sim::systems::SceneBroadcaster"/>
  </xacro:unless>
  ```
</Note>

### Physics

The `<physics>` element configures the ODE (Open Dynamics Engine) solver. The Small House world uses a 3 ms time step:

```xml theme={null}
<physics name="3ms" type="ode">
  <max_step_size>0.003</max_step_size>
  <real_time_factor>1</real_time_factor>
</physics>
```

A smaller time step improves simulation accuracy at the cost of computational load. For navigation tasks with a TurtleBot, 3 ms is a good balance.

### Lighting

Gazebo supports directional, point, and spot lights. The Small House world uses a single directional sun:

```xml theme={null}
<light name="sun" type="directional">
  <cast_shadows>0</cast_shadows>
  <pose>0 0 10 0 0 0</pose>
  <diffuse>0.8 0.8 0.8 1</diffuse>
  <specular>0.2 0.2 0.2 1</specular>
  <attenuation>
    <range>1000</range>
    <constant>0.9</constant>
    <linear>0.01</linear>
    <quadratic>0.001</quadratic>
  </attenuation>
  <direction>-0.5 0.1 -0.9</direction>
</light>
```

The `<direction>` vector `(-0.5, 0.1, -0.9)` angles the light from upper-left, creating natural shadow contrast on furniture.

***

## Xacro: parameterized world files

[Xacro](http://wiki.ros.org/xacro) (XML Macros) extends plain SDF with:

* **Arguments**, pass parameters when loading the world
* **Conditionals**, `<xacro:if>` / `<xacro:unless>` for optional blocks
* **Macros**, reusable XML templates with local variables

World files that use Xacro carry the `.sdf.xacro` extension. To process them, ROS 2 runs the Xacro preprocessor at launch time, producing a pure SDF file that Gazebo can load.

The Small House world declares one argument:

```xml theme={null}
<sdf version="1.6" xmlns:xacro="http://www.ros.org/wiki/xacro">
  <xacro:arg name="headless" default="false"/>
  ...
```

The launch file passes this argument when spawning the world:

```python theme={null}
# tb_demo_world.launch.py (excerpt)
world_name = LaunchConfiguration("world_name",
    default="house_world.sdf.xacro")
```

***

## Model anatomy

Every Gazebo model lives in its own directory with two required files:

```
tb_worlds/models/aws_robomaker_residential_ChairA_01/
├── model.config      ← metadata (name, version, author)
├── model.sdf         ← geometry, physics, visuals
├── meshes/
│   ├── aws_ChairA_01_visual.DAE    ← visual mesh (detailed)
│   └── aws_ChairA_01_collision.DAE ← collision mesh (simplified)
└── materials/        ← textures and material scripts
```

### `model.config`

```xml theme={null}
<?xml version="1.0" ?>
<model>
  <name>aws_robomaker_residential_ChairA_01</name>
  <version>1.0</version>
  <sdf version="1.6">model.sdf</sdf>
  <author>
    <name></name>
    <email></email>
  </author>
  <description></description>
</model>
```

### `model.sdf`

A static furniture model needs three things: inertial properties, a collision shape for physics, and a visual mesh for rendering. Here is the complete `ChairA_01` model:

```xml theme={null}
<?xml version="1.0"?>
<sdf version="1.6">
  <model name="aws_robomaker_residential_ChairA_01">
    <link name="link_ChairA_01">
      <inertial>
        <mass>7.5</mass>
        <inertia>
          <ixx>0.217</ixx>  {/* rotation resistance around X axis */}
          <iyy>0.217</iyy>
          <izz>0.067</izz>  {/* lowest: chair spins easily around vertical */}
          <ixy>0</ixy>
          <ixz>0</ixz>
          <iyz>0</iyz>
        </inertia>
      </inertial>

      {/* Collision: simplified mesh for fast physics */}
      <collision name="collision">
        <geometry>
          <mesh>
            <uri>model://aws_robomaker_residential_ChairA_01/meshes/aws_ChairA_01_collision.DAE</uri>
            <scale>1 1 1</scale>
          </mesh>
        </geometry>
      </collision>

      {/* Visual: full-detail mesh for rendering */}
      <visual name="visual">
        <geometry>
          <mesh>
            <uri>model://aws_robomaker_residential_ChairA_01/meshes/aws_ChairA_01_visual.DAE</uri>
          </mesh>
        </geometry>
      </visual>
    </link>
  </model>
</sdf>
```

<Tip>
  The **collision mesh** is deliberately simpler than the visual mesh, fewer polygons mean faster contact detection. For furniture that the robot will not physically interact with (static obstacles), a convex hull approximation is sufficient.
</Tip>

***

## The AWS RoboMaker Small House world

The Small House world is a residential interior adapted from [AWS RoboMaker's open-source assets](https://github.com/aws-robotics/aws-robomaker-small-house-world). It contains **68 `aws_robomaker_residential_*` models** covering:

| Category   | Examples                                                        |
| ---------- | --------------------------------------------------------------- |
| Furniture  | `Bed_01`, `ChairA_01`, `ChairD_01`, `CoffeeTable_01`, `Sofa_01` |
| Appliances | `AirconditionerA_01`, `AirconditionerB_01`, `CookingBench_01`   |
| Décor      | `Carpet_01`, `Curtain_01`, `Chandelier_01`, `Ball_01`           |
| Portraits  | `DeskPortraitA_01`, `DeskPortraitB_01`, `DeskPortraitC_01`      |
| Storage    | `NightStand_01`, `Wardrobe_01`, `Board_01`                      |

### Placing models in the world

Each model is placed with a `<model>` block in the world file. The `<include>` element references the model directory by name; `<pose>` gives its position (x, y, z) and orientation (roll, pitch, yaw in radians):

```xml theme={null}
<model name='Bed_01_001'>
  <static>true</static>
  <include>
    <uri>model://aws_robomaker_residential_Bed_01</uri>
  </include>
  <pose frame=''>-6.165067 2.030560 -0.000010 0 -0 0</pose>
</model>

<model name='AirconditionerA_01_001'>
  <static>true</static>
  <include>
    <uri>model://aws_robomaker_residential_AirconditionerA_01</uri>
  </include>
  <pose frame=''>-9.199410 2.411230 2.084544 0 -0 0</pose>
</model>
```

Setting `<static>true</static>` tells Gazebo the model does not move, the physics engine skips dynamics for it, saving computation.

### Launching the house world

```bash theme={null}
# Clone the repo
git clone https://github.com/pantelis/turtlebot-maze
cd turtlebot-maze

# Build Docker images (first time only, ~5 GB)
docker compose build

# Start the house world
docker compose up demo-world-house
```

Gazebo will open showing the residential interior. The TurtleBot spawns near the entrance.

### Navigation map

The house world ships with a pre-built Nav2 occupancy grid map at 5 cm/px resolution (`tb_worlds/maps/house_world_map.yaml`). The map was generated by running GMapping SLAM in the world and recording the result.

When a new model is added or furniture is rearranged, **the map must be regenerated**, Nav2 will plan routes through walls it does not know about.

***

## Available worlds

The turtlebot-maze project includes four Gazebo environments:

| Service                | Scene                                 | Models              | Map resolution |
| ---------------------- | ------------------------------------- | ------------------- | -------------- |
| `demo-world`           | Original TurtleBot maze               | Built-in            | 5 cm/px        |
| `demo-world-enhanced`  | Maze + textured walls + ArUco markers | ArUco, PBR textures | 5 cm/px        |
| `demo-world-house`     | AWS residential house                 | 68 residential      | 5 cm/px        |
| `demo-world-bookstore` | AWS bookstore                         | 34 retail           | 5 cm/px        |
| `demo-world-warehouse` | AWS warehouse (no roof)               | 14 industrial       | 2 cm/px        |

All worlds use the same Docker images and behavior demo services, only the world service name changes.

***

## Key SDF elements reference

| Element       | Purpose                                                      |
| ------------- | ------------------------------------------------------------ |
| `<world>`     | Top-level container for the entire scene                     |
| `<model>`     | A named, poseable assembly of links and joints               |
| `<link>`      | A rigid body with mass, inertia, collision, and visual       |
| `<joint>`     | A constraint between two links (fixed, revolute, prismatic…) |
| `<collision>` | Shape used for physics contact detection                     |
| `<visual>`    | Shape used for rendering only                                |
| `<inertial>`  | Mass and moment-of-inertia tensor                            |
| `<geometry>`  | Primitive (box, sphere, cylinder) or mesh reference          |
| `<plugin>`    | Gazebo system plugin (physics, sensors, GUI)                 |
| `<pose>`      | 6-DOF placement: `x y z roll pitch yaw`                      |
| `<static>`    | If `true`, the model is fixed to the world frame             |
| `<include>`   | Reference an external model by URI                           |

***

<Callout icon="pen-to-square" iconType="regular">
  [Edit this page on GitHub](https://github.com/aegean-ai/eaia/edit/main/src/aiml-common/lectures/simulation/gazebo-worlds.mdx) or [file an issue](https://github.com/aegean-ai/eaia/issues/new/choose).
</Callout>
