Skip to main content
ClaudeWave
EN, English
Skill215 repo starsupdated 4mo ago

gz-ecs-overview

gz-ecs-overview is a concise reference for Gazebo Simulation's Entity-Component-System architecture that explains how Entities, Components, Systems, the EntityComponentManager, and SimulationRunner interact within the simulation loop. Use this skill when a user asks how gz-sim is organized, where to add custom code, or how the simulation steps through Configure, PreUpdate, Update, and PostUpdate phases.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/harunkurtdev/ros2-claude-code-template /tmp/gz-ecs-overview && cp -r /tmp/gz-ecs-overview/.claude/skills/gz-ecs-overview ~/.claude/skills/gz-ecs-overview
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# gz-sim ECS architecture in 5 minutes

```
                ┌──────────────────────────────────────────┐
                │                 Server                   │
                │  (one process, owns N runners)           │
                └──────┬───────────────────────────────────┘
                       │
            ┌──────────▼──────────────────────────┐
            │         SimulationRunner            │
            │  - drives the iteration loop        │
            │  - owns the EventManager            │
            │  - owns the EntityComponentManager  │
            │  - owns the System list             │
            └──────────┬──────────────────────────┘
                       │ every step
   ┌───────────────────┼───────────────────────────────────────┐
   ▼                   ▼                   ▼                   ▼
Configure*       PreUpdate*           Update*             PostUpdate*
(once on load)   (mutates ECM)        (mutates ECM)       (read-only)
                                      └── Reset hooks on /reset
```

## The core types

| Type | Header | Role |
|------|--------|------|
| `Entity` | `Entity.hh` | Just a `uint64_t` ID. Means nothing on its own. |
| `components::Component<T, id>` | `components/Component.hh` | Strongly-typed data attached to an Entity. |
| `EntityComponentManager` | `EntityComponentManager.hh` | The world's database. Add/remove/query components. |
| `EventManager` | `EventManager.hh` | Pub/sub for cross-system events. |
| `System` (`ISystemConfigure`, `ISystemPreUpdate`, `ISystemUpdate`, `ISystemPostUpdate`, `ISystemReset`) | `System.hh` | Behaviour. Plugins implement one or more of these. |
| `SimulationRunner` | (internal) | Owns and drives the above. |
| `Server` | `Server.hh` | Public façade — `Run()`, `SetUpdatePeriod()`, etc. |

## Adding behaviour: pick a system phase

* **`Configure()`** — once on plugin load. Read SDF, cache handles, register
  topics. Do *not* mutate the world here unless you're spawning fixtures.
* **`PreUpdate()`** — every iteration, *before* physics. Apply commands
  (forces, joint targets), spawn entities.
* **`Update()`** — every iteration, alongside physics. Most user code does
  *not* belong here.
* **`PostUpdate()`** — every iteration, *after* physics. **Read-only ECM**
  access — emit telemetry, publish state, write logs. The ECM is `const`
  here for a reason.
* **`Reset()`** — when the world resets (e.g. via `/world/<name>/control`).
  Restore any internal state cached in the system.

## The "Cmd / state / Reset" component triplet

A common pattern in gz-sim:

* `<X>` — current value (e.g. `JointPosition`).
* `<X>Cmd` — request from a user system, consumed and cleared in
  `PreUpdate` by the physics-coupled system.
* `<X>Reset` — value to apply at the next world reset.

Look at `components/JointPositionReset.hh` / `JointVelocityCmd.hh` for the
canonical examples.

## Querying the ECM efficiently

Prefer `Each<>` views — they're cached and re-used across iterations:

```cpp
_ecm.Each<components::Joint, components::JointVelocity>(
  [&](const Entity &_ent,
      const components::Joint *,
      const components::JointVelocity *_vel) -> bool
  {
    // do work with *_vel
    return true; // keep iterating
  });
```

`EachNew`, `EachRemoved`, `EachChanged` exist for change-detection passes.

## Where things live

* **Component definitions** — `include/gz/sim/components/<Name>.hh`
  (header-only). Register serialization in `src/ComponentFactory.cc` when
  you want the component to survive log replay / save.
* **System plugins** — `src/systems/<snake_case>/<CamelCase>.{hh,cc}` plus
  an entry in `src/systems/CMakeLists.txt`. Use the `new-system` skill to
  scaffold.
* **High-level wrappers** — `Model`, `Link`, `Joint`, `Light`, `Actor`,
  `Sensor` (`include/gz/sim/`). These wrap an `Entity` plus ECM accessors
  and are usually how user code touches a model.

## Threading model

* `PreUpdate` / `Update` / `PostUpdate` of *different* systems run
  sequentially inside one runner iteration — not in parallel.
* `Server::Run(true, n)` blocks; `Run(false, n)` runs the loop on a worker
  thread. Multi-runner setups exist (different worlds), each on its own
  thread.
* Don't add long-running work in `Update()`. Spawn a thread in `Configure`,
  communicate via lock-free queues, drain in `PreUpdate`.

## Read the source, not just this

If you only have time for two files, read:

1. `include/gz/sim/EntityComponentManager.hh` — every comment matters.
2. `include/gz/sim/System.hh` — the interfaces are tiny and tell you
   exactly what each phase guarantees.
behaviortree_node_creationSkill

Scaffold a Behavior Tree leaf node — plain BehaviorTree.CPP (SyncActionNode / StatefulActionNode / ConditionNode) or a BehaviorTree.ROS2 wrapper (RosActionNode / RosServiceNode / RosTopicPubNode / RosTopicSubNode) — with ports, factory/plugin registration, and XML v4 usage. Trigger when the user asks to write a behavior-tree node (not Nav 2-specific).

behaviortree-reviewerSubagent

Use proactively before opening a PR that adds or changes BehaviorTree.CPP nodes or BehaviorTree.ROS2 wrappers (RosActionNode/RosServiceNode/RosTopicPub/SubNode, TreeExecutionServer). Reviews a diff against BT.CPP v4 conventions — node base-class choice, non-blocking ticks, ports/blackboard typing, factory/plugin registration, XML v4, and the ROS 2 wrapper contract. Returns a punch list with file:line anchors, not a rewrite.

buildSlash Command

Build the colcon workspace (optionally a single package) and report the outcome.

clean-arch-architectSubagent

Use when a design decision touches Clean Architecture boundaries in a ROS 2 project — which layer a new behaviour belongs to, whether a port belongs in domain or application, whether a new node should be lifecycle-managed, whether to compose nodes or split packages. Returns an architectural recommendation with trade-offs, not implementation.

commandsSkill

Author a new skill under .claude/skills/<name>/SKILL.md with valid frontmatter, following the template conventions, and index it in CLAUDE.md + README.md.

ecs-architectSubagent

Use when a design decision touches the gz-sim ECS — where new state should live, which system phase should write it, how to avoid coupling, whether to add a component vs. a member variable, whether a new system should be split or merged with an existing one. Returns an architectural recommendation with trade-offs, not implementation.

extending_claude_configSkill

Author a new asset for this .claude/ template — a rule, skill, slash command, or sub-agent — following the project's conventions and wiring it into the CLAUDE.md / README.md indexes. Trigger when the user wants to add or extend a command, skill, agent, or rule (make the template itself extensible).

gz-buildSkill

Configure and build gz-sim from source using either CMake/Ninja directly or a colcon workspace. Trigger when the user asks to build, recompile, configure, or set up the gz-sim binary tree.