diff --git a/.agents/skills/add-task-executor/SKILL.md b/.agents/skills/add-task-executor/SKILL.md
index ab91e6529..d5570c591 100644
--- a/.agents/skills/add-task-executor/SKILL.md
+++ b/.agents/skills/add-task-executor/SKILL.md
@@ -260,9 +260,9 @@ int main(int argc, char* argv[]) {
Only switch to `MultiThreadedExecutor` if you have independent callbacks that genuinely need concurrent execution **and** all shared resources are thread-safe. Nodes that use OpenGL, CUDA, or other thread-affine resources **must** use `rclcpp::spin()` to keep callbacks serialized.
-### 3. Add remap to bringup launch
+### 3. Add remap in the stack entry file
-In the layer bringup launch file (e.g., `global_bringup/launch/global.launch.xml`):
+In the module's canonical launch file, declare the action endpoint as a topic arg with the canonical default; any deviation is wired in the stack entry file (e.g., `stacks/full_default/launch/stack.launch.xml` — the single-locus rule):
```xml
```
diff --git a/.agents/skills/configure-multi-robot/SKILL.md b/.agents/skills/configure-multi-robot/SKILL.md
index bcddf7c7b..1dbec3094 100644
--- a/.agents/skills/configure-multi-robot/SKILL.md
+++ b/.agents/skills/configure-multi-robot/SKILL.md
@@ -191,7 +191,7 @@ robot-desktop:
replicas: ${NUM_ROBOTS:-1}
```
-So `NUM_ROBOTS=3 airstack up` produces **three** robot containers (`airstack-robot-desktop-1`, `-2`, `-3`), each with its own `ROBOT_NAME` and its own `ROS_DOMAIN_ID`. Each container runs the full autonomy stack independently. Cross-robot communication, when needed, goes through the DDS router (see [`onboard_all/config/dds_router.yaml`](../../../robot/ros_ws/src/autonomy_bringup/onboard_all/config/dds_router.yaml)) which bridges allowlisted topics from each per-robot domain into a shared GCS domain.
+So `NUM_ROBOTS=3 airstack up` produces **three** robot containers (`airstack-robot-desktop-1`, `-2`, `-3`), each with its own `ROBOT_NAME` and its own `ROS_DOMAIN_ID`. Each container runs the full autonomy stack independently. Cross-robot communication, when needed, goes through the DDS router (see the shared allowlist [`autonomy_bringup/config/dds_router.yaml`](../../../robot/ros_ws/src/autonomy_bringup/config/dds_router.yaml)) which bridges allowlisted topics from each per-robot domain into a shared GCS domain.
```bash
airstack up --sim isaac --robots 3 # sets NUM_ROBOTS and the multi-drone Isaac script together
@@ -203,18 +203,17 @@ docker ps --format '{{.Names}}' | grep robot-desktop
The simulator side has to spawn matching vehicles — see [Sim-Side Robot Spawning](#sim-side-robot-spawning).
-### `onboard_all` vs `onboard_local_offboard_global` (legacy roles)
+### Full vs. lite vs. split topologies (stacks)
-The stack-shaped successors: `--stack full_default` / `--stack lite_default` replace the role values, `--stack lite_offload_global:onboard|:offboard` replaces the split pair, and a fleet entry's `hosts: {offboard: }` replaces choosing the split *placement* by hand (see Fleet-First above). The role dispatch below still works without a stack/fleet.
+Topology is selected by **stack** — the legacy `AUTONOMY_ROLE` role dispatch was removed (a set `AUTONOMY_ROLE` is now a preflight error): `--stack full_default` (the no-stack default) runs everything on the machine, `--stack lite_default` runs the lite set, `--stack lite_offload_global:onboard|:offboard` is the split pair, and a fleet entry's `hosts: {offboard: }` declares the split *placement* (see Fleet-First above).
-[`autonomy_bringup`](../../../robot/ros_ws/src/autonomy_bringup/) ships two layouts, selected by the `role` arg / `AUTONOMY_ROLE` env var:
+| Stack | What runs onboard | What runs offboard | When to use |
+|-------|-------------------|--------------------|-------------|
+| `full_default` | interface, sensors, perception, local, **global**, behavior, logging | nothing | Sim/dev desktop, autonomous Jetson with enough compute, single-machine deployments |
+| `lite_default` | interface, sensors, perception, local, behavior | nothing (no global anywhere) | Compute-constrained vehicle flying task-driven missions |
+| `lite_offload_global` (`:onboard` + `:offboard`) | interface, sensors, perception, local, behavior | global planning + mapping | VOXL / lite Jetson where global planning is offloaded to a ground station; `desktop_split` profile for debugging the split |
-| Variant | Role values | What runs onboard | What runs offboard | When to use |
-|--------|-------------|-------------------|--------------------|-------------|
-| `onboard_all` | `role:=full` | interface, sensors, perception, local, **global**, behavior | nothing | Sim/dev desktop, autonomous Jetson with enough compute, single-machine deployments |
-| `onboard_local_offboard_global` | `role:=onboard` (lite) + `role:=offboard` (GCS) | interface, sensors, perception, local, behavior | global planning + mapping | VOXL / lite Jetson where global planning is offloaded to a ground station; `desktop_split` profile for debugging the split |
-
-The split is significant for multi-robot: with `onboard_local_offboard_global`, **one offboard container is launched per robot** (also via `replicas: ${NUM_ROBOTS}`), all on `ROS_DOMAIN_ID=0`, and each bridges into its own per-robot onboard domain via the domain bridge config in `onboard_local_offboard_global/config/dds_router.yaml`. See [`docs/robot/autonomy_modes.md`](../../../docs/robot/autonomy_modes.md) for the profile matrix.
+The split is significant for multi-robot: with `lite_offload_global`, **one offboard container is launched per robot** (also via `replicas: ${NUM_ROBOTS}`), all on `ROS_DOMAIN_ID=0`, and each bridges into its own per-robot onboard domain via the DDS-router config generated from the stack's `bridge.yaml` (`python3 tools/gen_dds_router.py stacks/lite_offload_global/bridge.yaml` — the generated allowlist deliberately drops the legacy split's `set_trajectory_mode` crossing, doctor hard gate #2). See [`docs/robot/autonomy_modes.md`](../../../docs/robot/autonomy_modes.md) for the profile matrix.
## Topic and TF Namespacing
@@ -449,7 +448,7 @@ Before merging a change that touches anything robot-namespaced:
- [ ] If you added a new module to a layer bringup, you tested it with `NUM_ROBOTS=2` and confirmed both robots' namespaces look identical under `ros2 node list`
- [ ] If you added a sim launch script, it reads `NUM_ROBOTS` and spawns vehicles named `robot_1`, `robot_2`, … with matching `vehicle_id` / `domain_id`
- [ ] If you added a system test that addresses a robot, it loops over `range(1, num_robots + 1)` and uses `domain_id=n` in `ros2_exec(...)`
-- [ ] DDS router allowlists in `onboard_all/config/dds_router.yaml` (or the split equivalent) include any new cross-domain topic your module exposes — otherwise it will not appear on the GCS
+- [ ] DDS router allowlists in `autonomy_bringup/config/dds_router.yaml` (or the split stack's `bridge.yaml`) include any new cross-domain topic your module exposes — otherwise it will not appear on the GCS
- [ ] Verified end-to-end: `NUM_ROBOTS=3 airstack up`, then `docker exec airstack-robot-desktop-2 bash -c 'ros2 topic list | grep robot_2'` shows the same topics that `airstack-robot-desktop-1` shows under `robot_1`
## Verification Commands
@@ -486,7 +485,7 @@ docker exec -e ROS_DOMAIN_ID=1 airstack-robot-desktop-1 bash -c \
- [`docs/robot/autonomy_modes.md`](../../../docs/robot/autonomy_modes.md) — profile matrix (`desktop`, `desktop_split`, `voxl`, `l4t`, `offboard`)
- [`robot/docker/robot_name_map/`](../../../robot/docker/robot_name_map/) — mapping YAMLs and `resolve_robot_name.py`
- [`robot/ros_ws/src/autonomy_bringup/launch/robot.launch.xml`](../../../robot/ros_ws/src/autonomy_bringup/launch/robot.launch.xml) — top-level `push_ros_namespace`
-- [`robot/ros_ws/src/autonomy_bringup/onboard_all/config/dds_router.yaml`](../../../robot/ros_ws/src/autonomy_bringup/onboard_all/config/dds_router.yaml) — cross-domain allowlist pattern
+- [`robot/ros_ws/src/autonomy_bringup/config/dds_router.yaml`](../../../robot/ros_ws/src/autonomy_bringup/config/dds_router.yaml) — cross-domain allowlist pattern
- [`simulation/ms-airsim/config/generate_settings.py`](../../../simulation/ms-airsim/config/generate_settings.py) and [`settings.json.j2`](../../../simulation/ms-airsim/config/settings.json.j2)
- [`simulation/isaac-sim/launch_scripts/example_multi_px4_pegasus_launch_script.py`](../../../simulation/isaac-sim/launch_scripts/example_multi_px4_pegasus_launch_script.py)
- [`tests/conftest.py`](../../../tests/conftest.py) — `airstack_env` fixture and `--num-robots` parametrization
diff --git a/.agents/skills/integrate-module-into-layer/SKILL.md b/.agents/skills/integrate-module-into-layer/SKILL.md
index 2ac6b1dca..f02efcaf1 100644
--- a/.agents/skills/integrate-module-into-layer/SKILL.md
+++ b/.agents/skills/integrate-module-into-layer/SKILL.md
@@ -16,13 +16,13 @@ integrate it into a **stack** — the self-contained folder under `stacks/`
whose entry launch file is the single wiring document for a running topology
(RFC #379 §3–4). This replaces the legacy layer-bringup workflow.
-> **The layer-bringup workflow is LEGACY.** Editing
-> `local_bringup/launch/*.launch.xml` (or any `*_bringup` launch file) to add
-> modules is the old monolith pattern. Those files are frozen: the launch
-> lint (`tests/meta/test_launch_single_locus.py`) forbids new ``s
-> outside `stacks/*/launch/`, and the grandfather allowlist
-> (`tests/meta/launch_lint_allowlist.txt`) only shrinks. Integrate into a
-> stack instead.
+> **The layer-bringup workflow is GONE.** The legacy layer bringup launch
+> files (`local/perception/sensors/global/behavior *.launch.xml`) were
+> deleted along with the AUTONOMY_ROLE dispatch — there is nothing left to
+> edit there. The launch lint (`tests/meta/test_launch_single_locus.py`)
+> forbids ``s outside `stacks/*/launch/`, and the grandfather
+> allowlist (`tests/meta/launch_lint_allowlist.txt`) only shrinks. Integrate
+> into a stack.
## The model (read this first)
diff --git a/.agents/skills/update-documentation/SKILL.md b/.agents/skills/update-documentation/SKILL.md
index 999a8b3e3..5144fb3d1 100644
--- a/.agents/skills/update-documentation/SKILL.md
+++ b/.agents/skills/update-documentation/SKILL.md
@@ -726,7 +726,7 @@ feat: Add YourModule local planner
- Implement algorithm based on XYZ paper
- Add configuration and launch files
-- Integrate into local_bringup
+- Integrate into the stack entry file (stacks/full_default)
- Add comprehensive README documentation
- Update mkdocs navigation
```
diff --git a/.agents/skills/visualize-in-foxglove/SKILL.md b/.agents/skills/visualize-in-foxglove/SKILL.md
index 37071426a..a3ad4b424 100644
--- a/.agents/skills/visualize-in-foxglove/SKILL.md
+++ b/.agents/skills/visualize-in-foxglove/SKILL.md
@@ -20,7 +20,7 @@ running in the GCS container.
Robot container (domain: ROS_DOMAIN_ID)
└─ publishes topics
-DDS Router (onboard_all)
+DDS Router (shared allowlist)
└─ bridges allowlisted topics to GCS domain
GCS container (domain: 0)
@@ -35,7 +35,7 @@ in the GCS before it will appear in Foxglove. Missing either step = nothing show
## Step 1 — Bridge the Topic in DDS Router
-**File:** `robot/ros_ws/src/autonomy_bringup/onboard_all/config/dds_router.yaml`
+**File:** `robot/ros_ws/src/autonomy_bringup/config/dds_router.yaml`
Add an entry to the `allowlist` for every topic you want on the GCS:
diff --git a/.agents/skills/write-launch-file/SKILL.md b/.agents/skills/write-launch-file/SKILL.md
index 3afec001d..18d0fe8c0 100644
--- a/.agents/skills/write-launch-file/SKILL.md
+++ b/.agents/skills/write-launch-file/SKILL.md
@@ -101,8 +101,8 @@ A stack folder (`stacks//`) is the unit of topology — see [docs/developm
```xml
-
-
+
```
@@ -189,7 +189,7 @@ If `ros2 node info` shows a node subscribing to `/odometry` instead of `//dev/null; then
+ log_warn "checkout has root-owned files (created in-container) — removing via docker..."
+ if ! docker run --rm -v "$MODULE_CHECKOUT_DIR:/m" ubuntu:24.04 \
+ bash -c "rm -rf /m/$name"; then
+ log_error "could not remove modules/$name — remove it manually, e.g.:"
+ log_error " docker run --rm -v \"$MODULE_CHECKOUT_DIR:/m\" ubuntu:24.04 rm -rf /m/$name"
+ return 1
+ fi
+ fi
fi
# regenerate the overlay without it (also prunes links + compose entries)
diff --git a/.env b/.env
index 12399f94b..1289afacd 100644
--- a/.env
+++ b/.env
@@ -12,7 +12,7 @@ PROJECT_NAME="airstack"
# If you've run ./airstack.sh setup, then this will auto-generate from the git commit hash every time a change is made
# to a Dockerfile or docker-compose.yaml file. Otherwise this can also be set explicitly to make a release version.
# auto-generated from git commit hash
-VERSION="0.20.0-alpha.6"
+VERSION="0.20.0-alpha.7"
# Choose "dev" or "prebuilt". "dev" is for mounted code that must be built live. "prebuilt" is for built ros_ws baked into the image
DOCKER_IMAGE_BUILD_MODE="dev"
# Where to push and pull images from. Can replace with your docker hub username if using docker hub.
diff --git a/.github/workflows/deploy_docs_from_develop.yaml b/.github/workflows/deploy_docs_from_develop.yaml
index 7a10cb9a5..df39ffd1f 100644
--- a/.github/workflows/deploy_docs_from_develop.yaml
+++ b/.github/workflows/deploy_docs_from_develop.yaml
@@ -1,13 +1,23 @@
-name: Build/Publish Develop Docs
+name: Build/Publish Develop Docs
on:
push:
paths:
- "docs/**"
- "mkdocs.yml"
- "*.md"
+ - "stacks/**"
+ - "tools/gen_docs_catalog.py"
- ".github/workflows/deploy_docs_from_develop.yaml"
branches:
- develop
+ # Module-docs freshness (RFC #379 §9): the catalog is regenerated from the
+ # live registry on every deploy, so a registry change only reaches the site
+ # when a deploy runs. Until registry-driven repository_dispatch lands
+ # ("within a day, exact at releases"), a weekly rebuild plus manual dispatch
+ # keeps the develop catalog from going stale.
+ workflow_dispatch:
+ schedule:
+ - cron: "17 6 * * 1" # weekly, Mondays 06:17 UTC
permissions:
contents: write
jobs:
@@ -16,14 +26,42 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
+ # schedule/workflow_dispatch run on the default branch; this
+ # workflow always publishes the develop docs.
+ ref: develop
fetch-depth: 0
- uses: actions/setup-python@v4
with:
python-version: 3.10.6
- name: Install Dependencies
run: |
- pip install mkdocs-material mkdocs-same-dir mkdocs-redirects
+ pip install mkdocs-material mkdocs-same-dir mkdocs-redirects pyyaml
pip install pillow cairosvg mike
+ # RFC #379 §9: module docs ride the docs deploy. Shallow-clone the
+ # registry index and each REGISTERED module repo at its registered_ref
+ # into the gitignored modules/ dir, then regenerate docs/modules/ so
+ # the published catalog is fresh even when the committed pages lag.
+ # FAILURE ISOLATION: nothing in this step may fail the deploy — an
+ # unreachable registry or module repo degrades to the committed pages /
+ # a stub note on the module's page.
+ - name: Fetch registry index and registered module repos
+ run: |
+ rm -rf .modules-index modules
+ git clone --depth 1 https://github.com/castacks/airstack-modules-index .modules-index \
+ || echo "skipped: registry index unreachable (committed catalog pages will be served)"
+ if [ -d .modules-index/modules ]; then
+ mkdir -p modules
+ python3 tools/gen_docs_catalog.py --index .modules-index --list-refs |
+ while IFS=$'\t' read -r name repo ref; do
+ ( git init -q "modules/$name" \
+ && git -C "modules/$name" remote add origin "$repo" \
+ && git -C "modules/$name" fetch -q --depth 1 origin "$ref" \
+ && git -C "modules/$name" checkout -q FETCH_HEAD ) \
+ || { rm -rf "modules/$name"; echo "skipped: module $name ($repo @ $ref) unreachable — its page keeps the stub note"; }
+ done
+ python3 tools/gen_docs_catalog.py --index .modules-index --modules-dir modules \
+ || echo "skipped: catalog regeneration failed (committed pages will be served)"
+ fi
- name: Setup Docs Deploy
run: |
git config --global user.name "Docs Deploy"
diff --git a/.github/workflows/deploy_docs_from_main.yaml b/.github/workflows/deploy_docs_from_main.yaml
index 96a6d86e2..493c678b7 100644
--- a/.github/workflows/deploy_docs_from_main.yaml
+++ b/.github/workflows/deploy_docs_from_main.yaml
@@ -1,10 +1,12 @@
-name: Build/Publish Main Docs
+name: Build/Publish Main Docs
on:
push:
paths:
- "docs/**"
- "mkdocs.yml"
- "*.md"
+ - "stacks/**"
+ - "tools/gen_docs_catalog.py"
- ".github/workflows/deploy_docs_from_main.yaml"
branches:
- main
@@ -22,8 +24,33 @@ jobs:
python-version: 3.10.6
- name: Install Dependencies
run: |
- pip install mkdocs-material mkdocs-same-dir mkdocs-redirects
+ pip install mkdocs-material mkdocs-same-dir mkdocs-redirects pyyaml
pip install pillow cairosvg mike
+ # RFC #379 §9: module docs ride the docs deploy. Shallow-clone the
+ # registry index and each REGISTERED module repo at its registered_ref
+ # into the gitignored modules/ dir, then regenerate docs/modules/ so
+ # the published catalog is fresh even when the committed pages lag.
+ # FAILURE ISOLATION: nothing in this step may fail the deploy — an
+ # unreachable registry or module repo degrades to the committed pages /
+ # a stub note on the module's page.
+ - name: Fetch registry index and registered module repos
+ run: |
+ rm -rf .modules-index modules
+ git clone --depth 1 https://github.com/castacks/airstack-modules-index .modules-index \
+ || echo "skipped: registry index unreachable (committed catalog pages will be served)"
+ if [ -d .modules-index/modules ]; then
+ mkdir -p modules
+ python3 tools/gen_docs_catalog.py --index .modules-index --list-refs |
+ while IFS=$'\t' read -r name repo ref; do
+ ( git init -q "modules/$name" \
+ && git -C "modules/$name" remote add origin "$repo" \
+ && git -C "modules/$name" fetch -q --depth 1 origin "$ref" \
+ && git -C "modules/$name" checkout -q FETCH_HEAD ) \
+ || { rm -rf "modules/$name"; echo "skipped: module $name ($repo @ $ref) unreachable — its page keeps the stub note"; }
+ done
+ python3 tools/gen_docs_catalog.py --index .modules-index --modules-dir modules \
+ || echo "skipped: catalog regeneration failed (committed pages will be served)"
+ fi
- name: Setup Docs Deploy
run: |
git config --global user.name "Docs Deploy"
diff --git a/.github/workflows/deploy_docs_from_release.yaml b/.github/workflows/deploy_docs_from_release.yaml
index 428432c62..0842172b1 100644
--- a/.github/workflows/deploy_docs_from_release.yaml
+++ b/.github/workflows/deploy_docs_from_release.yaml
@@ -18,9 +18,33 @@ jobs:
python-version: 3.10.6
- name: Install Dependencies
run: |
- pip install mkdocs-material mkdocs-same-dir mkdocs-redirects
-
+ pip install mkdocs-material mkdocs-same-dir mkdocs-redirects pyyaml
pip install pillow cairosvg mike
+ # RFC #379 §9: module docs ride the docs deploy. Shallow-clone the
+ # registry index and each REGISTERED module repo at its registered_ref
+ # into the gitignored modules/ dir, then regenerate docs/modules/ so
+ # the published catalog matches the registry at release time.
+ # FAILURE ISOLATION: nothing in this step may fail the deploy — an
+ # unreachable registry or module repo degrades to the committed pages /
+ # a stub note on the module's page.
+ - name: Fetch registry index and registered module repos
+ run: |
+ rm -rf .modules-index modules
+ git clone --depth 1 https://github.com/castacks/airstack-modules-index .modules-index \
+ || echo "skipped: registry index unreachable (committed catalog pages will be served)"
+ if [ -d .modules-index/modules ]; then
+ mkdir -p modules
+ python3 tools/gen_docs_catalog.py --index .modules-index --list-refs |
+ while IFS=$'\t' read -r name repo ref; do
+ ( git init -q "modules/$name" \
+ && git -C "modules/$name" remote add origin "$repo" \
+ && git -C "modules/$name" fetch -q --depth 1 origin "$ref" \
+ && git -C "modules/$name" checkout -q FETCH_HEAD ) \
+ || { rm -rf "modules/$name"; echo "skipped: module $name ($repo @ $ref) unreachable — its page keeps the stub note"; }
+ done
+ python3 tools/gen_docs_catalog.py --index .modules-index --modules-dir modules \
+ || echo "skipped: catalog regeneration failed (committed pages will be served)"
+ fi
- name: Setup Docs Deploy
run: |
git config --global user.name "Docs Deploy"
diff --git a/AGENTS.md b/AGENTS.md
index 744f0e3b0..0b28549d0 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -21,6 +21,14 @@ AirStack provides a complete end-to-end system for autonomous drone operations i
The architecture is designed to allow easy swapping of algorithm modules (e.g., different planners, controllers, perception systems) through a standardized ROS 2 interface pattern.
+### Modular AirStack (RFC #379/#380)
+
+AirStack is transitioning from a monolith to **modules** (thin external repos with a small `module.yaml`, pulled on demand: `airstack module add --version `), **stacks** (self-contained topology folders under [`stacks/`](stacks/) — pinned `modules.repos`, XML launch entry points, CI-observed `wiring.md`), and **fleets** ([`config/fleets/`](config/fleets/) — who exists, which vehicle, which stack, which ground hosts). Before touching bringup launch files, read:
+
+- Docs: [Module & Stack Catalog](docs/modules/index.md) (marketplace, generated by [`tools/gen_docs_catalog.py`](tools/gen_docs_catalog.py)) · [Modules](docs/development/modules.md) · [Stacks](docs/development/stacks.md) · [Fleets](docs/development/fleets.md) · [Module CI](docs/development/module_ci.md) · [Modular AirStack Walkthrough](docs/getting_started/modular_airstack.md)
+- Skills: `create-module`, `create-stack`, `integrate-module-into-layer` (now stack-centric), `configure-multi-robot` (fleets)
+- Registry: [castacks/airstack-modules-index](https://github.com/castacks/airstack-modules-index) — one YAML per registered module/stack; DECLARED compat in the entry, VERIFIED compat CI-stamped under `compat/`
+
## Repository Architecture
### High-Level Structure
@@ -85,7 +93,9 @@ For detailed step-by-step instructions, refer to the **`.agents/skills/`** direc
|-------|------------|
| [add-ros2-package](.agents/skills/add-ros2-package) | Creating a new algorithm module package |
| [add-task-executor](.agents/skills/add-task-executor) | Implementing a task executor as a ROS 2 action server |
-| [integrate-module-into-layer](.agents/skills/integrate-module-into-layer) | Adding module to layer bringup |
+| [create-module](.agents/skills/create-module) | Packaging a capability as a standalone module repo (thin `module.yaml` manifest, canonical-default launch args, `test_stack/`, CI caller) per RFC #379 |
+| [create-stack](.agents/skills/create-stack) | Creating a stack folder (`airstack stack new`), editing entry launch files, bootstrapping `wiring.md`, split stacks + `bridge.yaml` |
+| [integrate-module-into-layer](.agents/skills/integrate-module-into-layer) | Integrating a ROS 2 module into a **stack** (entry launch file, single-locus wiring rule, wiring.md regeneration) — the old layer-bringup workflow is legacy |
| [write-launch-file](.agents/skills/write-launch-file) | Authoring ROS 2 launch files with AirStack conventions (ROBOT_NAME namespacing, topic remapping, allow_substs) |
| [write-isaac-sim-scene](.agents/skills/write-isaac-sim-scene) | Creating custom simulation scenes |
| [visualize-in-foxglove](.agents/skills/visualize-in-foxglove) | Adding topic visualization to Foxglove/GCS |
@@ -164,9 +174,18 @@ airstack install # Install Docker and dependencies
# Container management
airstack up [service] # Start services (robot, isaac-sim, gcs)
airstack up --sim isaac|airsim --robots N # Intent flags: derive profiles/URDF/sim script (add --headless, --play/--no-play, --no-autolaunch, --wait, --dry-run)
+airstack up --stack [:] --sim isaac # Stack launch (RFC #379): stacks//launch/.launch.xml — the ONLY dispatch (no --stack = full_default; legacy AUTONOMY_ROLE was removed); see docs/development/stacks.md
airstack up --fleet --sim isaac # Fleet launch (RFC #380): config/fleets/.yaml drives identity/placement/spawns; see docs/development/fleets.md
airstack fleet list|generate # Fleet files: table / per-robot compose for heterogeneous fleets
airstack sync # Sync from airstack.yaml: modules, external stack repos, fleet validation
+
+# Modules, stacks, doctor (RFC #379; catalog: docs/modules/index.md)
+airstack module add --version # Pin + sync an external module (branches refused); local paths allowed
+airstack module list|sync|remove # Table / (re)clone+validate+overlay+hooks / drop entry and artifacts
+airstack module create --in-tree # Scaffold a module boundary in a fork (RFC #379 §11)
+airstack module doctor [--drift] # Validate manifests+overlay; --drift classifies fork changes (never blocks)
+airstack stack list|new |diff # Stacks: table / copy a reference stack / compare generated wiring
+airstack doctor [--live|--snapshot] [--stack NAME] # Observe-and-report checks; --live diffs the RUNNING graph vs wiring.md
airstack ready # Wait until the stack is flight-ready (containers → sim /clock → nodes → PX4); --json for scripts
airstack down [service] # Stop services
airstack status # Show container status
@@ -385,7 +404,7 @@ Each major component has its own Docker container:
**Configuration:**
- Main compose file: `docker-compose.yaml` (includes all component compose files)
-- Environment variables: top-level [`.env`](.env) (image tags, `VERSION`, `NUM_ROBOTS`, `ROBOT_NAME_MAP_CONFIG_FILE`, `ISAAC_SIM_SCRIPT_NAME`, `AUTONOMY_ROLE`, etc.)
+- Environment variables: top-level [`.env`](.env) (image tags, `VERSION`, `NUM_ROBOTS`, `ROBOT_NAME_MAP_CONFIG_FILE`, `ISAAC_SIM_SCRIPT_NAME`, `AIRSTACK_STACK_DIR`, etc.)
- Per-container shell init: [`robot/docker/.bashrc`](robot/docker/.bashrc) — resolves `ROBOT_NAME` and `ROS_DOMAIN_ID` at startup (see Multi-Robot Configuration below)
**Networking:** Custom bridge network (172.31.0.0/24) for inter-container communication.
@@ -398,11 +417,11 @@ Legacy multi-robot is implemented via Docker Compose **replicas**, not multiple
`ROBOT_NAME` is **not** set directly in `.env`. Each container computes it at startup: [`robot/docker/.bashrc`](robot/docker/.bashrc) reads `ROBOT_NAME_SOURCE` (`container_name` or `hostname`) and runs [`resolve_robot_name.py`](robot/docker/robot_name_map/resolve_robot_name.py) against the mapping in [`robot/docker/robot_name_map/`](robot/docker/robot_name_map/) (default: [`default_robot_name_map.yaml`](robot/docker/robot_name_map/default_robot_name_map.yaml)). The resolver exports both `ROBOT_NAME` and `ROS_DOMAIN_ID` — robot N gets domain N by default, so each robot is on its own DDS partition.
-The autonomy bringup variant is selected by `AUTONOMY_ROLE` (`full` | `onboard` | `offboard`), dispatched in [`robot/ros_ws/src/autonomy_bringup/launch/robot.launch.xml`](robot/ros_ws/src/autonomy_bringup/launch/robot.launch.xml):
+The autonomy topology is selected by a **stack** (`AIRSTACK_STACK_DIR` / `AIRSTACK_STACK_ENTRY`, exported by `airstack up --stack [:]`), dispatched in [`robot/ros_ws/src/autonomy_bringup/launch/robot.launch.xml`](robot/ros_ws/src/autonomy_bringup/launch/robot.launch.xml). The legacy `AUTONOMY_ROLE` dispatch was removed — a set `AUTONOMY_ROLE` is a preflight error. Reference stacks (see [docs/development/stacks.md](docs/development/stacks.md)):
-- **full** — every autonomy module runs on this machine (sim/dev desktop, autonomous Jetson)
-- **onboard** — lite modules only (interface, sensors, perception, local planning, behavior); pairs with **offboard**
-- **offboard** — global planning only; runs on GCS paired with onboard robots
+- **full_default** — every autonomy module runs on this machine (sim/dev desktop, autonomous Jetson); the default when no stack is selected, machine-proven graph-identical to the old `AUTONOMY_ROLE=full`
+- **lite_default** — lite modules only (interface, sensors, perception, local planning, behavior); no global/logging
+- **lite_offload_global** — split stack: `:onboard` (lite, on the vehicle) + `:offboard` (global planning on a ground host), bridged per its `bridge.yaml` (generated DDS-router config)
For Isaac Sim, the default `ISAAC_SIM_SCRIPT_NAME=example_one_px4_pegasus_launch_script.py` only spawns a single drone. Multi-robot Isaac Sim requires `ISAAC_SIM_SCRIPT_NAME=example_multi_px4_pegasus_launch_script.py` (the system test harness sets this automatically when `--num-robots > 1`).
diff --git a/airstack.sh b/airstack.sh
index f1344d84e..a64373c80 100755
--- a/airstack.sh
+++ b/airstack.sh
@@ -160,9 +160,11 @@ function print_command_help {
echo "Options:"
echo " --build Build images before starting containers"
echo " --recreate Recreate containers even if their configuration and image haven't changed"
- echo " --stack NAME Launch a stack folder (stacks/NAME/launch/stack.launch.xml) instead of"
- echo " the legacy AUTONOMY_ROLE dispatch. NAME:ENTRY selects an alternate entry"
- echo " file (launch/ENTRY.launch.xml). See docs/development/stacks.md."
+ echo " --stack NAME Launch a stack folder (stacks/NAME/launch/stack.launch.xml)."
+ echo " Stacks are the only launch dispatch; no --stack (and no stack"
+ echo " env) launches the trunk reference stack full_default."
+ echo " NAME:ENTRY selects an alternate entry file"
+ echo " (launch/ENTRY.launch.xml). See docs/development/stacks.md."
echo " --fleet NAME Launch a fleet (config/fleets/NAME.yaml): exports FLEET_CONFIG_FILE,"
echo " derives NUM_ROBOTS, selects the Isaac fleet spawner, and (for"
echo " heterogeneous fleets) includes the generated per-robot services."
@@ -1225,6 +1227,19 @@ function apply_launch_intent {
apply_stack_intent || return 1
fi
+ # Stacks are the ONLY launch dispatch (the legacy AUTONOMY_ROLE dispatch
+ # was removed): no stack selected anywhere (--stack / env / --env-file /
+ # .env) = the trunk reference stack full_default. Exported here so the
+ # effective config always names the stack that will actually launch.
+ # (Fleet runs re-resolve per container: robot/docker/.bashrc overrides
+ # these from the fleet file when FLEET_CONFIG_FILE is set.)
+ if [[ -z "$(resolve_launch_var AIRSTACK_STACK_DIR "$@")" ]]; then
+ export AIRSTACK_STACK_DIR="/root/AirStack/stacks/full_default"
+ fi
+ if [[ -z "$(resolve_launch_var AIRSTACK_STACK_ENTRY "$@")" ]]; then
+ export AIRSTACK_STACK_ENTRY="stack"
+ fi
+
if [[ -n "$AIRSTACK_INTENT_SIM" ]]; then
local sim_profile urdf
case "$AIRSTACK_INTENT_SIM" in
@@ -1440,16 +1455,13 @@ function preflight_up {
log_warn "LAUNCH_NATNET is gone — OptiTrack moved to the asm_optitrack module (airstack module add https://github.com/castacks/asm_optitrack --version ); see docs/development/modules.md"
fi
- # 8. Stack vs legacy AUTONOMY_ROLE dispatch. AUTONOMY_ROLE counts as
- # "explicitly set" only via env / --env-file / .env — the compose files'
- # own `${AUTONOMY_ROLE:-full}` default is invisible here, by design.
- local _pf_stack_dir _pf_role
- _pf_stack_dir=$(resolve_launch_var AIRSTACK_STACK_DIR "${_pf_global[@]}")
+ # 8. AUTONOMY_ROLE was REMOVED (stacks — RFC #379 — are the only launch
+ # dispatch). A set value counts only via env / --env-file / .env; nothing
+ # in the compose files defaults it anymore.
+ local _pf_role
_pf_role=$(resolve_launch_var AUTONOMY_ROLE "${_pf_global[@]}")
- if [[ -n "$_pf_role" && -z "$_pf_stack_dir" ]]; then
- log_warn "AUTONOMY_ROLE is the legacy dispatch; stacks replace it in 0.21 — try: airstack up --stack full_default"
- elif [[ -n "$_pf_role" && -n "$_pf_stack_dir" ]]; then
- log_warn "Both a stack ($_pf_stack_dir) and AUTONOMY_ROLE=$_pf_role are set — the stack wins: robot.launch.xml ignores the role when a stack dir is set."
+ if [[ -n "$_pf_role" ]]; then
+ _pf_error "AUTONOMY_ROLE was removed — select a stack: airstack up --stack (see docs/development/stacks.md). Migration: full → full_default (the no-stack default), onboard → lite_default, onboard/offboard split → lite_offload_global:onboard / :offboard."
fi
unset -f _pf_error
diff --git a/common/ros_packages/airstack_common/launch/playback.launch.xml b/common/ros_packages/airstack_common/launch/playback.launch.xml
index 2e097bd74..b6e3b803e 100644
--- a/common/ros_packages/airstack_common/launch/playback.launch.xml
+++ b/common/ros_packages/airstack_common/launch/playback.launch.xml
@@ -1,7 +1,13 @@
+
-
-
+
+
diff --git a/common/ros_packages/coordination/coordination_bringup/launch/gcs_gossip_bridge.launch.py b/common/ros_packages/coordination/coordination_bringup/launch/gcs_gossip_bridge.launch.py
index 499a217bc..42435fb9f 100644
--- a/common/ros_packages/coordination/coordination_bringup/launch/gcs_gossip_bridge.launch.py
+++ b/common/ros_packages/coordination/coordination_bringup/launch/gcs_gossip_bridge.launch.py
@@ -1,4 +1,8 @@
-"""Launches the GCS-side DDS Router bridging /gossip/peers between GCS domain (0) and gossip domain (99)."""
+"""Launches the GCS-side DDS Router bridging /gossip/peers between GCS domain (0) and gossip domain (99).
+
+Included by: desktop_bringup gcs.launch.xml (gcs container entry).
+STATUS: canonical module launch for the GCS gossip bridge.
+"""
import os
diff --git a/common/ros_packages/desktop_bringup/launch/gcs.launch.xml b/common/ros_packages/desktop_bringup/launch/gcs.launch.xml
index aabf229ca..0a69c2a1e 100644
--- a/common/ros_packages/desktop_bringup/launch/gcs.launch.xml
+++ b/common/ros_packages/desktop_bringup/launch/gcs.launch.xml
@@ -1,24 +1,15 @@
-
+
-
-
-
-
-
+
+
-
-
- ?>
-
-
- ?>
-
+
-
-
-
-
+
+
+
-
diff --git a/common/ros_packages/desktop_bringup/launch/static_transforms.launch.xml b/common/ros_packages/desktop_bringup/launch/static_transforms.launch.xml
index e28cda2ce..1b2c58668 100644
--- a/common/ros_packages/desktop_bringup/launch/static_transforms.launch.xml
+++ b/common/ros_packages/desktop_bringup/launch/static_transforms.launch.xml
@@ -1,9 +1,8 @@
+
-
-
-
-
-
diff --git a/common/ros_packages/logging/bag_recorder_pid/launch/bag_record_pid.launch.py b/common/ros_packages/logging/bag_recorder_pid/launch/bag_record_pid.launch.py
index 63b13b405..79a35ecae 100644
--- a/common/ros_packages/logging/bag_recorder_pid/launch/bag_record_pid.launch.py
+++ b/common/ros_packages/logging/bag_recorder_pid/launch/bag_record_pid.launch.py
@@ -1,3 +1,8 @@
+"""Convenience wrapper around bag_record_pid_node.launch.py (no namespace).
+
+Included by: nothing in trunk; run manually (see the package README).
+STATUS: standalone utility.
+"""
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
from launch.substitutions import LaunchConfiguration
diff --git a/common/ros_packages/logging/bag_recorder_pid/launch/bag_record_pid_namespaced.launch.py b/common/ros_packages/logging/bag_recorder_pid/launch/bag_record_pid_namespaced.launch.py
index 180f29f9f..5fee526d6 100644
--- a/common/ros_packages/logging/bag_recorder_pid/launch/bag_record_pid_namespaced.launch.py
+++ b/common/ros_packages/logging/bag_recorder_pid/launch/bag_record_pid_namespaced.launch.py
@@ -1,3 +1,9 @@
+"""Namespaced wrapper around bag_record_pid_node.launch.py (pushes a
+robot namespace before including it).
+
+Included by: nothing in trunk; run manually (see the package README).
+STATUS: standalone utility.
+"""
from launch import LaunchDescription
from launch.actions import (
DeclareLaunchArgument,
diff --git a/common/ros_packages/logging/bag_recorder_pid/launch/bag_record_pid_node.launch.py b/common/ros_packages/logging/bag_recorder_pid/launch/bag_record_pid_node.launch.py
index f31865383..cc78480bb 100644
--- a/common/ros_packages/logging/bag_recorder_pid/launch/bag_record_pid_node.launch.py
+++ b/common/ros_packages/logging/bag_recorder_pid/launch/bag_record_pid_node.launch.py
@@ -1,3 +1,9 @@
+"""Starts the bag_record_node PID-supervised rosbag recorder.
+
+Included by: bag_record_pid.launch.py and bag_record_pid_namespaced.launch.py
+(logging.launch.xml starts the node directly instead).
+STATUS: standalone utility.
+"""
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
diff --git a/common/ros_packages/logging/logging_bringup/launch/logging.launch.xml b/common/ros_packages/logging/logging_bringup/launch/logging.launch.xml
index 7b56ca1b9..42797a4f1 100644
--- a/common/ros_packages/logging/logging_bringup/launch/logging.launch.xml
+++ b/common/ros_packages/logging/logging_bringup/launch/logging.launch.xml
@@ -1,8 +1,12 @@
-
+
-
-
-
+
+
diff --git a/common/ros_packages/robot_descriptions/launch/robot_state_publisher.launch.py b/common/ros_packages/robot_descriptions/launch/robot_state_publisher.launch.py
index 6e2f4fac5..4305685b8 100644
--- a/common/ros_packages/robot_descriptions/launch/robot_state_publisher.launch.py
+++ b/common/ros_packages/robot_descriptions/launch/robot_state_publisher.launch.py
@@ -1,5 +1,10 @@
#!/usr/bin/env python3
+"""Starts robot_state_publisher with the URDF selected by urdf_file_path.
+Included by: autonomy_bringup robot.launch.xml (the shared preamble that
+runs before the stack entry file).
+STATUS: canonical module launch (included by the robot entry point).
+"""
import os
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, OpaqueFunction
diff --git a/docs/development/fleets.md b/docs/development/fleets.md
index 5a64d679e..bedc920f4 100644
--- a/docs/development/fleets.md
+++ b/docs/development/fleets.md
@@ -160,7 +160,8 @@ ground:
validation errors. Doctor's bridge hard-gate (no control-setpoint /
trajectory-group names in any `bridge.yaml`) holds unchanged for every
split stack a fleet places.
-- Legacy `AUTONOMY_ROLE` never enters: the entry point *is* the role.
+- The removed legacy `AUTONOMY_ROLE` never enters: the entry point *is* the
+ role.
## Simulation
@@ -204,7 +205,7 @@ launch-time fleet default from `airstack.yaml` (select fleets explicitly with
|---|---|---|
| `NUM_ROBOTS` | **Derived** from the fleet's robot count | explicit env still wins (banner) |
| `ROBOT_NAME_MAP_CONFIG_FILE` | Absorbed: identity resolves from the fleet entry | legacy resolver remains the no-fleet default |
-| `AUTONOMY_ROLE` | Absorbed: derived from `hosts:` + the stack's entry points | legacy dispatch remains without a stack/fleet |
+| `AUTONOMY_ROLE` | **Removed** — the stack entry point *is* the role, derived from `hosts:` | no-fleet/no-stack default is `stacks/full_default`; a set `AUTONOMY_ROLE` is a preflight error |
| `URDF_FILE` | From the vehicle's `airframe.base_urdf` (pass-through form; xacro generation is future) | explicit env still wins |
| `ISAAC_SIM_SCRIPT_NAME` | **Derived**: the generic fleet spawner | explicit env still wins |
| `ROBOT_NAME` / `ROS_DOMAIN_ID` | Resolved per robot (`domain_policy: auto` = robot N → domain N) | pre-set env still wins |
diff --git a/docs/development/intermediate/testing/end_to_end_testing.md b/docs/development/intermediate/testing/end_to_end_testing.md
index 37dc0d376..d0c89d0cf 100644
--- a/docs/development/intermediate/testing/end_to_end_testing.md
+++ b/docs/development/intermediate/testing/end_to_end_testing.md
@@ -362,7 +362,7 @@ The workflow auto-prepends `build_packages` when not already specified.
| Layer | Location | Examples |
| ----- | -------- | -------- |
-| Tracker params | `robot/ros_ws/src/local/controls/trajectory_controller/config/trajectory_controller.yaml` (stack path; legacy `local.launch.xml` keeps its inline copy) | `sphere_radius`, `look_ahead_time`, `search_ahead_factor`, `min_virtual_tracking_velocity` |
+| Tracker params | `robot/ros_ws/src/local/controls/trajectory_controller/config/trajectory_controller.yaml` (selected by the stack entry file) | `sphere_radius`, `look_ahead_time`, `search_ahead_factor`, `min_virtual_tracking_velocity` |
| Tracker implementation | Replace or fork `trajectory_controller` node | Alternative pure-pursuit, different intersection logic |
| Low-level control | Swap `pid_controller` for `attitude_controller` in launch | Changes end-to-end error, not tracker-only |
@@ -385,7 +385,7 @@ airstack test -m "build_packages or autonomy" \
--trajectory-types Circle -v
BASELINE=$(ls -1t tests/results/ | head -1)
-# 2. Edit tracker params in local.launch.xml, rebuild
+# 2. Edit tracker params in trajectory_controller.yaml, rebuild
airstack test -m build_packages -v
# 3. Candidate run
@@ -448,7 +448,7 @@ Action server: `/{robot_name}/tasks/fixed_trajectory` — see also [Tasks and Ta
| [`robot/.../fixed_trajectory_task.cpp`](../../../../robot/ros_ws/src/local/controls/trajectory_controller/src/fixed_trajectory_task.cpp) | C++ reference path generators |
| [`robot/.../trajectory_controller.cpp`](../../../../robot/ros_ws/src/local/controls/trajectory_controller/src/trajectory_controller.cpp) | Pure-pursuit path tracker |
| [`robot/.../trajectory_library.cpp`](../../../../robot/ros_ws/src/local/planners/trajectory_library/src/trajectory_library.cpp) | Trajectory math, sphere intersection |
-| [`robot/.../local.launch.xml`](../../../../robot/ros_ws/src/local/local_bringup/launch/local.launch.xml) | Tracker + PID params |
+| [`robot/.../trajectory_controller.yaml`](../../../../robot/ros_ws/src/local/controls/trajectory_controller/config/trajectory_controller.yaml) + [`pid_controller.yaml`](../../../../robot/ros_ws/src/local/controls/pid_controller/config/pid_controller.yaml) | Tracker + PID params |
---
diff --git a/docs/development/stacks.md b/docs/development/stacks.md
index c76bf8880..14cbcb971 100644
--- a/docs/development/stacks.md
+++ b/docs/development/stacks.md
@@ -25,27 +25,25 @@ Anatomy is enforced by a unit test: `tests/meta/test_stack_layout_contract.py`
| Stack | Topology |
|-------|----------|
-| [`full_default`](https://github.com/castacks/AirStack/tree/develop/stacks/full_default) | The current full-autonomy topology (GPU `droan_gl` planner) — baseline, graph-identical to legacy `AUTONOMY_ROLE=full`. |
+| [`full_default`](https://github.com/castacks/AirStack/tree/develop/stacks/full_default) | The current full-autonomy topology (GPU `droan_gl` planner) — baseline; machine-proven graph-identical to the removed legacy `AUTONOMY_ROLE=full` dispatch, and what launches when no stack is selected. |
| [`full_droan_cpu`](https://github.com/castacks/AirStack/tree/develop/stacks/full_droan_cpu) | CPU DROAN planner + live `disparity_expansion` — absorbs `local_droan_cpu.launch.xml`. |
| [`full_macvo`](https://github.com/castacks/AirStack/tree/develop/stacks/full_macvo) | MAC-VO as the planner's disparity source — supersedes (and fixes) the broken `local_macvo_obstacle_avoidance.launch.xml` variant. Requires the `asm_macvo` module (`airstack module add asm_macvo`). |
-| [`lite_default`](https://github.com/castacks/AirStack/tree/develop/stacks/lite_default) | Onboard-lite topology, unsplit — the `AUTONOMY_ROLE=onboard` equivalent: interface, sensors, perception, flat Local layer, behavior; **no global, no logging**. |
-| [`lite_offload_global`](https://github.com/castacks/AirStack/tree/develop/stacks/lite_offload_global) | The first **split stack** (RFC #380 §2): `onboard.launch.xml` (= lite topology) + `offboard.launch.xml` (global layer only) + `bridge.yaml`. Replaces the `onboard`/`offboard` role pair. |
+| [`lite_default`](https://github.com/castacks/AirStack/tree/develop/stacks/lite_default) | Onboard-lite topology, unsplit — the equivalent of the removed `AUTONOMY_ROLE=onboard` role: interface, sensors, perception, flat Local layer, behavior; **no global, no logging**. |
+| [`lite_offload_global`](https://github.com/castacks/AirStack/tree/develop/stacks/lite_offload_global) | The first **split stack** (RFC #380 §2): `onboard.launch.xml` (= lite topology) + `offboard.launch.xml` (global layer only) + `bridge.yaml`. Replaced the removed `onboard`/`offboard` role pair. |
## Wrap vs. flatten — current status
-Stack adoption is a two-step migration:
+The wrap→flatten migration is COMPLETE: every reference stack composes its
+graph as flat module-launch includes, the legacy layer bringup launch files
+(`local/perception/sensors/global/behavior *.launch.xml`) are deleted, and
+the AUTONOMY_ROLE dispatch is gone from `autonomy_bringup` — stacks are the
+only dispatch. Two blocks remain wrapped **by design**: `interface.launch.py`
+(the safety boundary, until RFC #380 Part 2) and the
+`interpolate_dds_router` / gossip helpers (their wiring lives in YAML
+configs). The lint allowlist (below) is down to that deliberate remainder.
-- **Wrap form (now):** each reference stack's `stack.launch.xml` *includes*
- the existing layer bringup files (`interface_bringup`, `local_bringup`, …),
- capturing today's topology without moving any wiring. The remaps still live
- inside those layer bringups.
-- **Flatten (next phases):** the layer bringups' nodes and remaps move into
- the stack entry files, the legacy files shrink, and their lines disappear
- from the lint allowlist (below). `autonomy_bringup` thins until the
- AUTONOMY_ROLE dispatch is gone.
-
-In both forms, the stack's `wiring.md` — snapshotted from the running system —
-is the observed truth of the graph.
+The stack's `wiring.md` — snapshotted from the running system — is the
+observed truth of the graph.
## Running a stack
@@ -58,9 +56,12 @@ Mechanics: `--stack ` validates `stacks//launch/stack.launch.xml`
exists, then exports `AIRSTACK_STACK_DIR=/root/AirStack/stacks/` (the
*container* path — `stacks/` is bind-mounted into every robot container) and
`AIRSTACK_STACK_ENTRY=stack`. Inside the container,
-`autonomy_bringup/launch/robot.launch.xml` still runs the shared preamble
+`autonomy_bringup/launch/robot.launch.xml` runs the shared preamble
(ROBOT_NAME namespace, `use_sim_time`, `robot_state_publisher`, world→map TF),
-then includes the stack entry file *instead of* the legacy role groups.
+then includes the stack entry file. With no stack selected anywhere
+(`--stack` / env / `--env-file` / `.env`), the trunk reference stack
+`full_default` launches — `AIRSTACK_STACK_DIR` is always set in the
+effective config.
`--stack :` selects an alternate entry file
(`launch/.launch.xml`) — reserved for split stacks (RFC #380 §2).
@@ -68,6 +69,35 @@ then includes the stack entry file *instead of* the legacy role groups.
Stack launch files need no `colcon build` — they are read from the bind mount;
edit and re-launch.
+### Why stacks don't launch standalone
+
+It is tempting to `ros2 launch` a stack entry file directly and delete the
+dispatcher. Three reasons the thin `robot.launch.xml` earns its ~50 lines:
+
+1. **Namespace scoping is mechanical, not stylistic.** `push_ros_namespace`
+ only scopes what sits *inside* its enclosing scope, so an included
+ "preamble" file cannot namespace the sibling includes that follow it. The
+ dispatcher pushes `/$ROBOT_NAME` and includes the stack entry *within*
+ that scope — the one arrangement where every stack node lands namespaced
+ without each stack author hand-rolling (and occasionally fumbling) a
+ wrapper group. A stack that wraps the dispatcher instead recurses
+ infinitely; the layout contract test enforces the direction.
+2. **Stack files stay pure wiring documents.** The preamble — `use_sim_time`,
+ `robot_state_publisher`/URDF plumbing, the world→map TF — is *platform*
+ infrastructure, not topology. Keeping it out of stack entries preserves
+ the "entry file *is* the wiring diagram" property, and keeps
+ vehicle-driven URDF generation (RFC #380 §1) a one-file change instead of
+ an every-stack (and every external stack repo) migration.
+3. **It is the seed of the platform module.** RFC #380 Part 2 extracts
+ "interface + controller + safety + preamble" as the `px4_multirotor`
+ platform module; this dispatcher is precisely the file that becomes that
+ platform's bringup. The Directory Atlas (#385) says `autonomy_bringup`
+ *thins* — it does not disappear.
+
+Practically it is also the single point where `AIRSTACK_STACK_DIR`/`_ENTRY`
+resolution happens, so compose, the fleet resolver, and the CLI converge on
+one contract.
+
## wiring.md: generation and drift-checking
The wiring-snapshot system test brings the stack up in sim, waits for the node
@@ -86,8 +116,9 @@ airstack test -m wiring --stack full_default --sim isaacsim --num-robots 1
the observed graph — a PR that changes wiring must regenerate `wiring.md`,
so the review diff shows the topology change.
-Legacy runs without `--stack` keep using the golden at
-`tests/goldens/wiring/full_default..robot.md`.
+Runs without `--stack` launch the default dispatch — `full_default` — and
+drift-check against its `stacks/full_default/wiring.md` (there is no separate
+goldens tree; each stack folder owns its baseline).
## The single-locus rule (and its lint)
@@ -101,7 +132,9 @@ global_plan stacks/my_stack/` answers "who touches this".
Enforced by `tests/meta/test_launch_single_locus.py` (`unit` mark, runs in CI):
1. No ``/`remappings=` outside `stacks/*/launch/` — except files frozen
- in `tests/meta/launch_lint_allowlist.txt` (the wrap-form legacy set).
+ in `tests/meta/launch_lint_allowlist.txt` (down to the deliberate remainder:
+ a standalone utility, a vendored driver, one module launch awaiting its
+ canonical rewrite, and the interface safety boundary).
2. The allowlist only shrinks: an entry whose file no longer carries a remap
fails the lint until its line is deleted.
3. Stack launch files must describe every `` they declare.
@@ -145,9 +178,9 @@ stacks/lite_offload_global/
- **Run each half** with the `NAME:ENTRY` form:
`airstack up --stack lite_offload_global:onboard` on the vehicle,
- `... :offboard` on the ground host. Today's coarse `AUTONOMY_ROLE`
- trichotomy becomes "which entry point does this host run"; a third machine
- is just another entry file + bridge section.
+ `... :offboard` on the ground host. The old coarse `AUTONOMY_ROLE`
+ trichotomy (removed) became "which entry point does this host run"; a
+ third machine is just another entry file + bridge section.
- **Or declare the placement in a fleet** (RFC #380 §2): a fleet entry with
`stack: stacks/lite_offload_global` and `hosts: {offboard: gcs}` derives
both halves — the robot's service gets the `onboard` entry, the named
@@ -236,18 +269,19 @@ airstack doctor --snapshot --stack
`airstack up` with the stack flag). `airstack module doctor` remains the
module-scoped subset (manifests + overlay + `--drift`).
-## AUTONOMY_ROLE deprecation
+## AUTONOMY_ROLE: removed
-`AUTONOMY_ROLE` remains fully functional while stacks land, but it is the
-legacy dispatch — stacks replace it in 0.21. `airstack up` warns when it sees
-an explicitly set `AUTONOMY_ROLE`:
+`AUTONOMY_ROLE` was **removed on this branch** — stacks are the only launch
+dispatch. `airstack up` hard-errors (preflight) when it sees an explicitly
+set `AUTONOMY_ROLE` (env / `.env` / `--env-file`), naming this page.
+Migration:
-| You have | What happens | Migration |
-|----------|--------------|-----------|
-| No stack, no explicit `AUTONOMY_ROLE` | Legacy dispatch, compose default role (`full`) — unchanged, no warning | `airstack up --stack full_default` when ready |
-| Explicit `AUTONOMY_ROLE=full` (env / `.env` / `--env-file`) | Legacy dispatch + deprecation warning | `--stack full_default` |
+| You had | Now | Migration |
+|---------|-----|-----------|
+| No stack, no `AUTONOMY_ROLE` (compose default role `full`) | `full_default` launches by default — machine-proven graph-identical to the old `full` role | Nothing to do (or be explicit: `--stack full_default`) |
+| Explicit `AUTONOMY_ROLE=full` | Preflight error | `--stack full_default` (or drop the variable — same stack launches) |
| `local_droan_cpu.launch.xml` variant | **Deleted in P5-E2** — the CPU-DROAN topology lives only in the stack | `--stack full_droan_cpu` |
| `local_macvo_obstacle_avoidance.launch.xml` | **Deleted in P5-E2** (was broken: wrong arg names, stale topic) | `--stack full_macvo` (fixed) |
-| `AUTONOMY_ROLE=onboard` (lite, no split) | Legacy dispatch + warning | `--stack lite_default` |
-| `AUTONOMY_ROLE=onboard` / `offboard` (split) | Legacy dispatch + warning | `--stack lite_offload_global:onboard` / `:offboard` (+ `bridge.yaml`) |
-| `--stack X` **and** explicit `AUTONOMY_ROLE` | Stack wins — the role is ignored by the launch dispatch; warning says so | Drop `AUTONOMY_ROLE` |
+| `AUTONOMY_ROLE=onboard` (lite, no split) | Preflight error. (On the desktop profile this role was unreachable anyway — `robot-desktop` hardcoded `AUTONOMY_ROLE=full`.) | `--stack lite_default` |
+| `AUTONOMY_ROLE=onboard` / `offboard` (split) | Preflight error | `--stack lite_offload_global:onboard` / `:offboard` (+ `bridge.yaml`; generate the router config — the generated config deliberately drops the legacy split's `set_trajectory_mode` crossing, doctor hard gate #2) |
+| `--stack X` **and** `AUTONOMY_ROLE` | Preflight error (no silent "stack wins" anymore) | Drop `AUTONOMY_ROLE` |
diff --git a/docs/gcs/foxglove.md b/docs/gcs/foxglove.md
index ec2c6dd90..fff842c6c 100644
--- a/docs/gcs/foxglove.md
+++ b/docs/gcs/foxglove.md
@@ -114,7 +114,7 @@ if plan is not None and boot is not None:
### 6. Bridge the source topic across DDS domains
-The visualizer can only subscribe to topics that crossed the DDS bridge. Add the source topic to `robot/ros_ws/src/autonomy_bringup/onboard_all/config/dds_router.yaml` under `allowlist`:
+The visualizer can only subscribe to topics that crossed the DDS bridge. Add the source topic to `robot/ros_ws/src/autonomy_bringup/config/dds_router.yaml` under `allowlist` (or, for the split stack, to `stacks/lite_offload_global/bridge.yaml` and regenerate):
```yaml
allowlist:
diff --git a/docs/getting_started/index.md b/docs/getting_started/index.md
index d00a10d02..9fc6746ea 100644
--- a/docs/getting_started/index.md
+++ b/docs/getting_started/index.md
@@ -119,4 +119,6 @@ To shutdown and remove docker containers:
airstack down # This will stop and remove the docker containers
```
-Congratulations! You did it.
+Congratulations! You did it.
+
+**Next:** the [Modular AirStack Walkthrough](modular_airstack.md) — fly a reference stack, add a module from the [catalog](../modules/index.md), build your own stack, and scale to a fleet.
diff --git a/docs/getting_started/modular_airstack.md b/docs/getting_started/modular_airstack.md
new file mode 100644
index 000000000..659c1f382
--- /dev/null
+++ b/docs/getting_started/modular_airstack.md
@@ -0,0 +1,144 @@
+# Modular AirStack Walkthrough
+
+The end-to-end journey through Modular AirStack
+([RFC #379](https://github.com/castacks/AirStack/discussions/379) /
+[RFC #380](https://github.com/castacks/AirStack/discussions/380)) for a
+developer new to the project: fly a **reference stack**, read its **wiring**,
+pull in a **module**, make a **stack of your own**, scale to a **fleet**, and
+let **doctor** check your work. Every command below is real and current.
+
+Prerequisites: the base [Getting Started](index.md) setup (clone, install,
+images pulled or built).
+
+## 1. Clone and set up
+
+```bash
+git clone --recursive -j8 git@github.com:castacks/AirStack.git
+cd AirStack
+./airstack.sh install # docker, compose, NVIDIA Container Toolkit
+./airstack.sh setup # enables the `airstack` command
+source ~/.bashrc # or ~/.zshrc
+airstack image-pull # or: airstack image-build
+```
+
+## 2. Fly a reference stack
+
+A **stack** is a self-contained folder under [`stacks/`](../development/stacks.md)
+— pinned module list, plain ROS 2 launch entry points, compose file, README.
+Trunk ships five reference stacks; start from the baseline:
+
+```bash
+airstack up --stack full_default --sim isaac
+airstack ready # waits: containers → sim /clock → nodes → PX4 ready
+```
+
+`--stack full_default` launches
+[`stacks/full_default/launch/stack.launch.xml`](../../stacks/full_default/README.md)
+— a flat list of module includes where every connection is written down.
+(Stacks are the only dispatch: with no `--stack`, `full_default` launches
+anyway; this makes the choice explicit.) Command the drone from
+Foxglove exactly as in [Getting Started](index.md#move-robot).
+
+## 3. Read the wiring — the map of the system
+
+Each stack commits a
+[`wiring.md`](../../stacks/full_default/wiring.md) snapshotted **from the
+running graph in CI** (nodes grouped by module, edges labeled
+topic/type/QoS). It cannot lie or rot: CI fails when the running system
+drifts from the committed diagram. When you wonder "who publishes this
+topic?", the answer is two greps away:
+
+```bash
+grep -r global_plan stacks/full_default/ # every connection is in the source
+```
+
+Read `stacks//wiring.md` first whenever you meet a new stack — it is
+the system diagram, observed rather than drawn.
+
+## 4. Add a module
+
+**Modules** are thin external repos, discovered in the
+[Module & Stack Catalog](../modules/index.md) and pulled on demand — pinned
+to a tag or SHA, never a branch. Example: the
+[dfm2_disturbances](../modules/dfm2_disturbances.md) Isaac Sim disturbance
+library (fans, vents, strobes, lens flare):
+
+```bash
+airstack module add https://github.com/castacks/asm_dfm2_disturbances \
+ --version af3daa783248b07b82165833d419d322ab3137fe
+airstack module list # name, type, pin, targets, valid?
+```
+
+`module add` records the pin in `modules.repos`, syncs the repo into the
+gitignored `modules/` dir, validates its `module.yaml`, places overlay
+symlinks, and regenerates a compose override that mounts the module into the
+right containers. Then bring the stack up with the module mounts, selecting
+one of the module's Isaac scene scripts:
+
+```bash
+ISAAC_SIM_SCRIPT_NAME=modules/dfm2_disturbances/one_px4_pegasus_fan_force_field.py \
+ airstack up --stack full_default --sim isaac \
+ -f .airstack/generated/docker-compose.modules.yaml
+```
+
+Full CLI reference (sync, remove, hooks, the pinning rule):
+[AirStack Modules](../development/modules.md).
+
+## 5. Make your own stack
+
+Never edit a reference stack — copy one and rewire it:
+
+```bash
+airstack stack new full_default my_stack
+```
+
+1. Edit `stacks/my_stack/launch/stack.launch.xml`: swap, add, or remove
+ module ``s and their topic args. All cross-module wiring lives in
+ this one file (the single-locus rule), so the file *is* your topology.
+2. Pin any external modules in `stacks/my_stack/modules.repos`.
+3. Run it: `airstack up --stack my_stack --sim isaac`.
+4. Snapshot its wiring: `airstack test -m wiring --stack my_stack`, validate
+ the observed file, commit it as `stacks/my_stack/wiring.md`.
+
+Guide: [AirStack Stacks](../development/stacks.md); agent workflow: the
+[create-stack skill](https://github.com/castacks/AirStack/blob/develop/.agents/skills/create-stack/SKILL.md).
+
+## 6. Scale to a fleet
+
+A **fleet file** (`config/fleets/*.yaml`) declares a whole deployment: which
+robots exist, which vehicle each flies, which stack each runs, and which
+ground hosts run split-stack offboard halves. The reference heterogeneous
+fleet flies three quads with three different brains:
+
+```bash
+airstack up --fleet sim_three_mixed --sim isaac
+airstack ready
+```
+
+`robot_1` runs `full_default`, `robot_2` runs `lite_default`, and `robot_3`
+runs the split `lite_offload_global` stack with its global layer placed on
+the GCS host. Guide: [AirStack Fleets](../development/fleets.md).
+
+## 7. Let doctor check your work
+
+`airstack doctor` observes and reports — it never generates or edits wiring:
+
+```bash
+airstack doctor # manifests, overlay, dep conflicts, stack anatomy,
+ # bridge safety placement (the two hard gates)
+airstack doctor --live # diff the RUNNING graph against the stack's wiring.md
+airstack module doctor --drift # fork research: module-contained vs trunk edits
+```
+
+Use `doctor` after every `module add`, stack edit, or fleet change; use
+`doctor --live` when a running system misbehaves.
+
+## Where to go next
+
+- [Module & Stack Catalog](../modules/index.md) — what's registered today
+- [AirStack Modules](../development/modules.md) ·
+ [Stacks](../development/stacks.md) ·
+ [Fleets](../development/fleets.md) ·
+ [Module CI](../development/module_ci.md)
+- [Interface Conventions Spec](../robot/autonomy/interface_conventions.md) —
+ the canonical topic names/types/QoS that make bare includes "just wire"
diff --git a/docs/modules/dfm2_disturbances.md b/docs/modules/dfm2_disturbances.md
new file mode 100644
index 000000000..9dca06420
--- /dev/null
+++ b/docs/modules/dfm2_disturbances.md
@@ -0,0 +1,56 @@
+# dfm2_disturbances
+
+
+
+> Isaac Sim disturbance library (fan/vent force fields, strobe lights, lens flare)
+
+| | |
+|---|---|
+| Repository | [castacks/asm_dfm2_disturbances](https://github.com/castacks/asm_dfm2_disturbances) |
+| Type | `isaac_extension` |
+| Maintainer | maintainers@theairlab.org |
+| License | MIT |
+| Registered ref | [`af3daa783248`](https://github.com/castacks/asm_dfm2_disturbances/tree/af3daa783248b07b82165833d419d322ab3137fe) |
+| Declared compat | `>=0.19.0-alpha.18 <0.20.0` |
+| Registry entry | [modules/dfm2_disturbances.yaml](https://github.com/castacks/airstack-modules-index/blob/main/modules/dfm2_disturbances.yaml) |
+
+## Install
+
+From an AirStack checkout ([AirStack Modules guide](../development/modules.md)):
+
+```bash
+airstack module add https://github.com/castacks/asm_dfm2_disturbances --version af3daa783248b07b82165833d419d322ab3137fe
+airstack up -f .airstack/generated/docker-compose.modules.yaml
+```
+
+`module add` pins the module in `modules.repos` and syncs it into the
+gitignored `modules/` overlay; the generated compose file mounts it into
+the containers.
+
+## Compatibility: declared vs verified
+
+The range `>=0.19.0-alpha.18 <0.20.0` is **DECLARED** by the module author
+(copied from the module's `module.yaml`). The **VERIFIED** record — rows
+stamped exclusively by CI runs of the reusable
+[module-system-tests workflow](../development/module_ci.md) — lives in the
+registry's [compat/ matrix](https://github.com/castacks/airstack-modules-index/tree/main/compat)
+([compat/dfm2_disturbances.yaml](https://github.com/castacks/airstack-modules-index/blob/main/compat/dfm2_disturbances.yaml) once stamped).
+A compatibility claim that isn't CI-verified rots (RFC #379 §5): trust the
+matrix, read the declaration as intent.
+
+## Documentation
+
+- [Module README on GitHub @ `af3daa783248`](https://github.com/castacks/asm_dfm2_disturbances/blob/af3daa783248b07b82165833d419d322ab3137fe/README.md)
+- *The module repo was not fetched when this page was generated — the*
+ *links above go to GitHub at the registered ref (RFC #379 §9 failure*
+ *isolation: an unreachable module repo never fails the docs deploy).*
+
+## Registered stacks using this module
+
+- None yet. Any stack can pin it in its `modules.repos` ([AirStack Stacks](../development/stacks.md)).
+
+## Registry notes
+
+> Pilot module of RFC #379, hand-built before the tooling existed (see the repo's FRICTION_LOG.md). registered_ref is a commit SHA because no release tag exists yet: v0.1.0 is pending the first green module-system-tests.yml CI run. Validated end-to-end locally on 2026-08-20 (extraction campaign) — declared marks liveliness + takeoff_hover_land in the module's test_stack on Isaac Sim.
diff --git a/docs/modules/index.md b/docs/modules/index.md
new file mode 100644
index 000000000..26c94f0f2
--- /dev/null
+++ b/docs/modules/index.md
@@ -0,0 +1,51 @@
+# Module & Stack Catalog
+
+
+
+The **marketplace catalog** of registered AirStack modules and stacks
+([RFC #379 §7/§9](https://github.com/castacks/AirStack/discussions/379)), rendered from the
+[airstack-modules-index](https://github.com/castacks/airstack-modules-index) registry — one YAML entry per
+module or stack, rosdistro-style. Getting listed = a PR to the registry
+(see the [registry README](https://github.com/castacks/airstack-modules-index#how-to-register-a-module)).
+
+Compatibility shown here is the author-**DECLARED** semver range; the
+**VERIFIED** matrix is CI-stamped into the registry's [compat/](https://github.com/castacks/airstack-modules-index/tree/main/compat)
+directory and is never hand-edited.
+
+## Registered modules
+
+| Module | Description | Type | Maintainer | Declared compat | Links |
+|--------|-------------|------|------------|-----------------|-------|
+| [dfm2_disturbances](dfm2_disturbances.md) | Isaac Sim disturbance library (fan/vent force fields, strobe lights, lens flare) | `isaac_extension` | maintainers@theairlab.org | `>=0.19.0-alpha.18 <0.20.0` | [repo](https://github.com/castacks/asm_dfm2_disturbances) |
+| [macvo](macvo.md) | MAC-VO learned stereo visual odometry (ICRA 2025 best paper) — macvo_ros2 wrapper around the MAC-VO network, publishing odometry, a covariance-aware point cloud, and the disparity image the local planner can consume | `ros_package` | maintainers@theairlab.org | `>=0.19.0-alpha.18 <0.20.0` | [repo](https://github.com/castacks/asm_macvo) · [full_macvo](../../stacks/full_macvo/README.md) |
+| [optitrack](optitrack.md) | OptiTrack NatNet mocap integration — natnet_ros2 client + PX4 external-vision fusion bridges on the robot, and the Motive-compatible NatNet server emulator for Isaac Sim | `ros_package` | maintainers@theairlab.org | `>=0.19.0-alpha.18 <0.20.0` | [repo](https://github.com/castacks/asm_optitrack) |
+
+## Registered stacks
+
+A stack is a self-contained topology folder; its pinned `modules.repos`
+*is* a tested-together release set ([RFC #379 §3](https://github.com/castacks/AirStack/discussions/379)).
+Trunk's reference stacks below are also in the site nav under
+**Modules → Reference Stacks**.
+
+| Stack | Description | Declared compat | Wiring | Registry entry |
+|-------|-------------|-----------------|--------|----------------|
+| [full_default](../../stacks/full_default/README.md) | The current full-autonomy topology as a self-contained stack folder — the baseline most users start from and the stack other stacks are copied from | `>=0.19.0-alpha.18 <0.21.0` | [wiring.md](../../stacks/full_default/wiring.md) | [full_default.yaml](https://github.com/castacks/airstack-modules-index/blob/main/stacks/full_default.yaml) |
+| [full_droan_cpu](../../stacks/full_droan_cpu/README.md) | Full autonomy with the CPU DROAN local planner (droan_local_planner + live disparity_expansion world model) instead of the GPU droan_gl node | `>=0.19.0-alpha.18 <0.21.0` | [wiring.md](../../stacks/full_droan_cpu/wiring.md) | [full_droan_cpu.yaml](https://github.com/castacks/airstack-modules-index/blob/main/stacks/full_droan_cpu.yaml) |
+| [full_macvo](../../stacks/full_macvo/README.md) | Full autonomy with MAC-VO learned stereo visual odometry as the disparity source for the local planner (droan_gl consumes /$ROBOT_NAME/perception/macvo/disparity) | `>=0.19.0-alpha.18 <0.21.0` | [wiring.md](../../stacks/full_macvo/wiring.md) | [full_macvo.yaml](https://github.com/castacks/airstack-modules-index/blob/main/stacks/full_macvo.yaml) |
+
+## See also
+
+- [Modular AirStack walkthrough](../getting_started/modular_airstack.md) — the
+ new-developer journey: reference stack → add a module → own stack → fleet
+- [AirStack Modules](../development/modules.md) — `airstack module` CLI, the
+ pinning rule, hooks, and the overlay
+- [AirStack Stacks](../development/stacks.md) — stack anatomy, `stack new|diff`,
+ wiring snapshots, `doctor`
+- [AirStack Fleets](../development/fleets.md) — fleet files composing stacks
+ into deployments (RFC #380)
+- [Module CI](../development/module_ci.md) — the reusable system-test workflow
+ module repos call; how compat badges are earned
+- [Interface Conventions Spec](../robot/autonomy/interface_conventions.md) —
+ the canonical names/types/QoS modules default to
diff --git a/docs/modules/macvo.md b/docs/modules/macvo.md
new file mode 100644
index 000000000..19e9be1de
--- /dev/null
+++ b/docs/modules/macvo.md
@@ -0,0 +1,56 @@
+# macvo
+
+
+
+> MAC-VO learned stereo visual odometry (ICRA 2025 best paper) — macvo_ros2 wrapper around the MAC-VO network, publishing odometry, a covariance-aware point cloud, and the disparity image the local planner can consume
+
+| | |
+|---|---|
+| Repository | [castacks/asm_macvo](https://github.com/castacks/asm_macvo) |
+| Type | `ros_package` |
+| Maintainer | maintainers@theairlab.org |
+| License | MIT |
+| Registered ref | [`7d5763936122`](https://github.com/castacks/asm_macvo/tree/7d5763936122ffcbb7173a635c9d5711e25c2414) |
+| Declared compat | `>=0.19.0-alpha.18 <0.20.0` |
+| Registry entry | [modules/macvo.yaml](https://github.com/castacks/airstack-modules-index/blob/main/modules/macvo.yaml) |
+
+## Install
+
+From an AirStack checkout ([AirStack Modules guide](../development/modules.md)):
+
+```bash
+airstack module add https://github.com/castacks/asm_macvo --version 7d5763936122ffcbb7173a635c9d5711e25c2414
+airstack up -f .airstack/generated/docker-compose.modules.yaml
+```
+
+`module add` pins the module in `modules.repos` and syncs it into the
+gitignored `modules/` overlay; the generated compose file mounts it into
+the containers.
+
+## Compatibility: declared vs verified
+
+The range `>=0.19.0-alpha.18 <0.20.0` is **DECLARED** by the module author
+(copied from the module's `module.yaml`). The **VERIFIED** record — rows
+stamped exclusively by CI runs of the reusable
+[module-system-tests workflow](../development/module_ci.md) — lives in the
+registry's [compat/ matrix](https://github.com/castacks/airstack-modules-index/tree/main/compat)
+([compat/macvo.yaml](https://github.com/castacks/airstack-modules-index/blob/main/compat/macvo.yaml) once stamped).
+A compatibility claim that isn't CI-verified rots (RFC #379 §5): trust the
+matrix, read the declaration as intent.
+
+## Documentation
+
+- [Module README on GitHub @ `7d5763936122`](https://github.com/castacks/asm_macvo/blob/7d5763936122ffcbb7173a635c9d5711e25c2414/README.md)
+- *The module repo was not fetched when this page was generated — the*
+ *links above go to GitHub at the registered ref (RFC #379 §9 failure*
+ *isolation: an unreachable module repo never fails the docs deploy).*
+
+## Registered stacks using this module
+
+- [full_macvo](../../stacks/full_macvo/README.md)
+
+## Registry notes
+
+> registered_ref is a commit SHA because no release tag exists yet: v0.1.0 is pending the first green module-system-tests.yml CI run. RFC #379's dogfood case for Docker dependency tiers 2/3: MAC-VO's heavy deps (TensorRT, torch, model weights) live in the module's Dockerfile.module, out of trunk's Dockerfile.robot. Composed-image CI validation (declared marks build_docker + liveliness against the tier-2 layer chain) is still pending — unlike dfm2_disturbances/optitrack, this module has not yet been validated end-to-end. Consumed by trunk reference stack full_macvo.
diff --git a/docs/modules/optitrack.md b/docs/modules/optitrack.md
new file mode 100644
index 000000000..69fa0f8bd
--- /dev/null
+++ b/docs/modules/optitrack.md
@@ -0,0 +1,56 @@
+# optitrack
+
+
+
+> OptiTrack NatNet mocap integration — natnet_ros2 client + PX4 external-vision fusion bridges on the robot, and the Motive-compatible NatNet server emulator for Isaac Sim
+
+| | |
+|---|---|
+| Repository | [castacks/asm_optitrack](https://github.com/castacks/asm_optitrack) |
+| Type | `ros_package` |
+| Maintainer | maintainers@theairlab.org |
+| License | MIT |
+| Registered ref | [`2ae094f4b308`](https://github.com/castacks/asm_optitrack/tree/2ae094f4b308b49127761c0605a17ad4ef4f6e31) |
+| Declared compat | `>=0.19.0-alpha.18 <0.20.0` |
+| Registry entry | [modules/optitrack.yaml](https://github.com/castacks/airstack-modules-index/blob/main/modules/optitrack.yaml) |
+
+## Install
+
+From an AirStack checkout ([AirStack Modules guide](../development/modules.md)):
+
+```bash
+airstack module add https://github.com/castacks/asm_optitrack --version 2ae094f4b308b49127761c0605a17ad4ef4f6e31
+airstack up -f .airstack/generated/docker-compose.modules.yaml
+```
+
+`module add` pins the module in `modules.repos` and syncs it into the
+gitignored `modules/` overlay; the generated compose file mounts it into
+the containers.
+
+## Compatibility: declared vs verified
+
+The range `>=0.19.0-alpha.18 <0.20.0` is **DECLARED** by the module author
+(copied from the module's `module.yaml`). The **VERIFIED** record — rows
+stamped exclusively by CI runs of the reusable
+[module-system-tests workflow](../development/module_ci.md) — lives in the
+registry's [compat/ matrix](https://github.com/castacks/airstack-modules-index/tree/main/compat)
+([compat/optitrack.yaml](https://github.com/castacks/airstack-modules-index/blob/main/compat/optitrack.yaml) once stamped).
+A compatibility claim that isn't CI-verified rots (RFC #379 §5): trust the
+matrix, read the declaration as intent.
+
+## Documentation
+
+- [Module README on GitHub @ `2ae094f4b308`](https://github.com/castacks/asm_optitrack/blob/2ae094f4b308b49127761c0605a17ad4ef4f6e31/README.md)
+- *The module repo was not fetched when this page was generated — the*
+ *links above go to GitHub at the registered ref (RFC #379 §9 failure*
+ *isolation: an unreachable module repo never fails the docs deploy).*
+
+## Registered stacks using this module
+
+- None yet. Any stack can pin it in its `modules.repos` ([AirStack Stacks](../development/stacks.md)).
+
+## Registry notes
+
+> registered_ref is a commit SHA because no release tag exists yet: v0.1.0 is pending the first green module-system-tests.yml CI run. Validated end-to-end locally on 2026-08-20 (extraction campaign) — declared marks integration + liveliness + optitrack (full EV-fusion flight e2e) in the module's test_stack on Isaac Sim. Builds against the proprietary OptiTrack NatNet SDK, fetched host-side by hooks.host_setup (never in git, never in images); CI passes NATNET_ACCEPT_LICENSE=1 via hook_env.
diff --git a/docs/real_world/index.md b/docs/real_world/index.md
index 9d5d89a91..59256cf66 100644
--- a/docs/real_world/index.md
+++ b/docs/real_world/index.md
@@ -107,13 +107,14 @@ See: [Robot Identity Configuration](../robot/docker/robot_identity.md)
## Autonomy Modes for Real World
-AirStack supports multiple autonomy modes for different scenarios:
+AirStack supports multiple autonomy topologies (stacks) for different
+scenarios:
-- **`onboard_all`**: All processing on robot (no ground station needed)
-- **`onboard_local`**: Local planning onboard, global planning offboard
-- **`offboard_global`**: Heavy computation on ground station
+- **`full_default`**: All processing on robot (no ground station needed)
+- **`lite_default`**: Lite modules only — no global planning anywhere
+- **`lite_offload_global`**: Split stack — local planning onboard, global planning on the ground station
-See: [Autonomy Modes Tutorial](../tutorials/autonomy_modes.md)
+See: [Onboard/Offboard Distributed Computing](../robot/autonomy_modes.md) and [Stacks](../development/stacks.md)
## Data Collection
diff --git a/docs/robot/autonomy/behavior/index.md b/docs/robot/autonomy/behavior/index.md
index 17bf102f6..1515d5d2a 100644
--- a/docs/robot/autonomy/behavior/index.md
+++ b/docs/robot/autonomy/behavior/index.md
@@ -2,7 +2,9 @@
The behavior module is responsible for the high-level decision making of the robot. This includes deciding what actions to take based on the current state of the robot and the world around it. The behavior module is responsible for coordinating the actions of the local and global modules to achieve the robot's goals.
## Launch
-Launch files are under `src/robot/autonomy/behavior/behavior_bringup/launch`.
-
-The main launch command is `ros2 launch behavior_bringup behavior.launch.xml`.
+Behavior modules ship their own canonical launch files and are composed by
+the stack entry file (the legacy `behavior_bringup` package was removed with
+the AUTONOMY_ROLE dispatch), e.g.
+`ros2 launch drone_safety_monitor drone_safety_monitor.launch.xml` — see
+`stacks/full_default/launch/stack.launch.xml` for the composed wiring.
diff --git a/docs/robot/autonomy/dds_router.md b/docs/robot/autonomy/dds_router.md
index b6b619abe..c3dfe766c 100644
--- a/docs/robot/autonomy/dds_router.md
+++ b/docs/robot/autonomy/dds_router.md
@@ -65,7 +65,7 @@ The launch file recognises the same `$(...)` token syntax used in ROS 2 XML laun
```xml
+ value="$(find-pkg-share autonomy_bringup)/config/dds_router.yaml" />
```
@@ -107,11 +107,13 @@ some_key: !reset
## Config files
-### `onboard_all/config/dds_router.yaml` — base config
+### `autonomy_bringup/config/dds_router.yaml` — shared allowlist
-**Location:** [`robot/ros_ws/src/autonomy_bringup/onboard_all/config/dds_router.yaml`](../../../../robot/ros_ws/src/autonomy_bringup/onboard_all/config/dds_router.yaml)
+**Location:** [`robot/ros_ws/src/autonomy_bringup/config/dds_router.yaml`](../../../../robot/ros_ws/src/autonomy_bringup/config/dds_router.yaml)
-Used when the robot role is `full` or `onboard` and both robot and GCS share the same physical machine or are bridged by this router.
+Selected by the `full_*` stacks and `lite_default` (their entry files pass it
+to `interpolate_dds_router.launch.py`). Historically this lived at
+`onboard_all/config/` under the removed AUTONOMY_ROLE dispatch.
**Participants:**
@@ -139,35 +141,24 @@ Used when the robot role is `full` or `onboard` and both robot and GCS share the
---
-### `onboard_local_offboard_global/config/dds_router.yaml` — extended config
+### Split-stack router config — generated from `bridge.yaml`
-**Location:** [`robot/ros_ws/src/autonomy_bringup/onboard_local_offboard_global/config/dds_router.yaml`](../../../../robot/ros_ws/src/autonomy_bringup/onboard_local_offboard_global/config/dds_router.yaml)
+The split stack (`stacks/lite_offload_global`) does NOT use a hand-written
+router config: its [`bridge.yaml`](../../../../stacks/lite_offload_global/bridge.yaml)
+is the authoritative boundary document, and `tools/gen_dds_router.py`
+generates `.airstack/generated/dds_router.lite_offload_global.yaml` from it
+(loaded by the stack's `onboard` entry).
-Used for the `desktop_split`, `l4t_lite + offboard`, and `voxl + offboard` deployment profiles where local planning runs onboard and global planning runs on the GCS.
-
-This config **inherits from `onboard_all`** via `extends:` and appends additional topics:
-
-```yaml
-extends: "$(find-pkg-share autonomy_bringup)/onboard_all/config/dds_router.yaml"
-```
-
-**Additional topics (appended to the base allowlist):**
-
-| Topic |
-|---|
-| `rt//sensors/front_stereo/left/image_rect` |
-| `rt//sensors/front_stereo/left/camera_info` |
-| `rt//sensors/front_stereo/right/image_rect` |
-| `rt//sensors/front_stereo/right/camera_info` |
-| `rt//global_plan` |
-
-
-The stereo image topics let the global planner on the GCS observe the robot's environment. `global_plan` carries the resulting path back to the onboard local planner.
+The generated config replaced the legacy split's committed
+`onboard_local_offboard_global/config/dds_router.yaml` (removed with the
+AUTONOMY_ROLE dispatch) — deliberately minus the `set_trajectory_mode`
+crossing that config carried: command authority stays onboard
+(`airstack doctor` hard gate #2).
---
## Adding a new bridged topic
-1. Decide which config applies (`onboard_all` for all roles, `onboard_local_offboard_global` for split-only).
-2. Add the topic to the appropriate `allowlist`, using the correct DDS prefix (`rt/`, `rq/`, `rr/`, etc.) and the `$(env ROBOT_NAME)` substitution for the robot namespace.
+1. Decide where it belongs: the shared `autonomy_bringup/config/dds_router.yaml` allowlist (full/lite stacks), or the split stack's `bridge.yaml` (then regenerate with `tools/gen_dds_router.py`; the doctor hard gate rejects control-setpoint / trajectory-group names).
+2. For the shared allowlist, add the topic using the correct DDS prefix (`rt/`, `rq/`, `rr/`, etc.) and the `$(env ROBOT_NAME)` substitution for the robot namespace.
3. If overriding inherited list entries is needed, use the `!override` tag on the list.
diff --git a/docs/robot/autonomy/global/index.md b/docs/robot/autonomy/global/index.md
index eaffb424b..cbd4a423a 100644
--- a/docs/robot/autonomy/global/index.md
+++ b/docs/robot/autonomy/global/index.md
@@ -4,7 +4,11 @@ The global packages include global world models and planners.
## Launch
-Launch files are under `src/robot/autonomy/global/global_bringup/launch`.
-
-The main launch command is `ros2 launch global_bringup global.launch.xml`.
+The global layer is composed by the stack entry file (the legacy
+`global_bringup` layer launch was removed with the AUTONOMY_ROLE dispatch):
+the trunk stacks include `vdb_mapping_ros2.py` (with
+`global_bringup/config/vdb_params.yaml`) and
+`random_walk_planner.launch.xml` directly — see
+`stacks/full_default/launch/stack.launch.xml`. The `global_bringup` package
+remains as the owner of the cross-package VDB config files.
diff --git a/docs/robot/autonomy/local/index.md b/docs/robot/autonomy/local/index.md
index cae708c0e..bfb91dc57 100644
--- a/docs/robot/autonomy/local/index.md
+++ b/docs/robot/autonomy/local/index.md
@@ -2,7 +2,9 @@
The local module includes packages that are specific to the local autonomy of the robot. This includes local mapping, planning, and control.
## Launch
-Launch files are under `src/robot/autonomy/local/local_bringup/launch`.
-
-The main launch command is `ros2 launch local_bringup local.launch.xml`.
+Local modules ship their own canonical launch files and are composed flat by
+the stack entry file (the legacy `local_bringup` package was removed with
+the AUTONOMY_ROLE dispatch): `takeoff_landing_planner`, the trajectory
+controller, `droan_gl`, and the PID controller are included directly — see
+`stacks/full_default/launch/stack.launch.xml` for the composed wiring.
diff --git a/docs/robot/autonomy/perception/index.md b/docs/robot/autonomy/perception/index.md
index 30e297524..d81dc289e 100644
--- a/docs/robot/autonomy/perception/index.md
+++ b/docs/robot/autonomy/perception/index.md
@@ -12,11 +12,13 @@ Perception forms the foundation of the autonomy stack by:
## Launch
-Launch files are located under `robot/ros_ws/src/perception/perception_bringup/launch/`.
-
-The main launch command is:
+Launch files are located under `robot/ros_ws/src/perception/perception_bringup/launch/`
+(module launch files; the legacy `perception.launch.xml` layer wrapper was
+removed with the AUTONOMY_ROLE dispatch). The stack entry files include them
+directly:
```bash
-ros2 launch perception_bringup perception.launch.xml
+ros2 launch perception_bringup stereo_image_proc.launch.xml
+ros2 launch perception_bringup topic_keepalive.launch.xml
```
## Key Topics
diff --git a/docs/robot/autonomy/sensors/index.md b/docs/robot/autonomy/sensors/index.md
index a5142333a..d4d2237ad 100644
--- a/docs/robot/autonomy/sensors/index.md
+++ b/docs/robot/autonomy/sensors/index.md
@@ -12,15 +12,16 @@ The sensors layer is responsible for:
## Launch
-Launch files are located under `robot/ros_ws/src/sensors/sensors_bringup/launch/`.
-
-The main launch command is:
+Sensor modules ship their own canonical launch files and are composed by the
+stack entry file under the `sensors` namespace (the legacy `sensors_bringup`
+layer launch was removed with the AUTONOMY_ROLE dispatch). E.g. the trunk
+stacks include:
```bash
-ros2 launch sensors_bringup sensors.launch.xml
+ros2 launch lidar_point_cloud_filter lidar_point_cloud_filter.launch.xml
```
-The bringup group uses the `sensors` namespace under each robot; see that package for which nodes are started.
+See `stacks/full_default/launch/stack.launch.xml` for the composed wiring.
## Key Topics
diff --git a/docs/robot/autonomy/system_architecture.md b/docs/robot/autonomy/system_architecture.md
index afc14e342..053f74778 100644
--- a/docs/robot/autonomy/system_architecture.md
+++ b/docs/robot/autonomy/system_architecture.md
@@ -365,7 +365,6 @@ graph TB
- **Planners:**
- `random_walk`: Random exploration planner
- - `ensemble_planner`: Multi-planner coordination
**Topics:**
diff --git a/docs/robot/autonomy_modes.md b/docs/robot/autonomy_modes.md
index 00f11a2ce..646a0bb43 100644
--- a/docs/robot/autonomy_modes.md
+++ b/docs/robot/autonomy_modes.md
@@ -1,13 +1,17 @@
-# Onboard/Offboard Distributed Computing
+# Onboard/Offboard Distributed Computing
-AirStack uses a **role** system to control which planning modules launch inside each container.
-The role is hardcoded per compose service — no environment variables need to be set by hand.
+AirStack uses **stacks** ([docs/development/stacks.md](../development/stacks.md))
+to control which autonomy modules launch inside each container. Each compose
+service carries a default stack — no environment variables need to be set by
+hand. (The legacy `AUTONOMY_ROLE` role system was removed; a set
+`AUTONOMY_ROLE` is a preflight error.)
-| Role | Value | What runs |
-|---|---|---|
-| **Full** | `full` | Every autonomy module: interface, sensors, perception, local planning, global planning, behavior |
-| **Onboard** | `onboard` | Lite modules only: interface, sensors, perception, local planning, behavior — no global planner |
-| **Offboard** | `offboard` | Global planner only — runs on the GCS paired with onboard robots |
+| Stack | What runs |
+|---|---|
+| **`full_default`** | Every autonomy module: interface, sensors, perception, local planning, global planning, behavior, logging — the default when no stack is selected |
+| **`lite_default`** | Lite modules only: interface, sensors, perception, local planning, behavior — no global planner |
+| **`lite_offload_global:onboard`** | The lite set on the vehicle, bridged to an offboard global half per the stack's `bridge.yaml` |
+| **`lite_offload_global:offboard`** | Global planner + world model only — runs on the GCS paired with onboard robots |
---
@@ -17,14 +21,17 @@ Profiles are split into **deployment** and **simulator** categories.
**Deployment profiles:**
-| Profile | Machine | Services started | Role(s) |
+| Profile | Machine | Services started | Default stack(s) |
|---|---|---|---|
-| `desktop` | Dev desktop | `robot-desktop` + `gcs` | `full` |
-| `desktop_split` | Dev desktop | `robot-desktop-onboard` + `robot-offboard` + `gcs` | `onboard` + `offboard` |
-| `l4t` | Jetson | `robot-l4t` + `zed-l4t` | `full` |
-| `l4t_lite` | Jetson | `robot-l4t-onboard` + `zed-l4t` | `onboard` |
-| `voxl` | VOXL2 | `robot-voxl-onboard` | `onboard` (always) |
-| `offboard` | Ground station | `robot-offboard` ×N + `gcs-real` | `offboard` |
+| `desktop` | Dev desktop | `robot-desktop` + `gcs` | `full_default` |
+| `desktop_split` | Dev desktop | `robot-desktop-onboard` + `robot-offboard` + `gcs` | `lite_default` + `lite_offload_global:offboard` |
+| `l4t` | Jetson | `robot-l4t` + `zed-l4t` | `full_default` |
+| `l4t_lite` | Jetson | `robot-l4t-onboard` + `zed-l4t` | `lite_default` |
+| `voxl` | VOXL2 | `robot-voxl-onboard` | `lite_default` (compute-constrained) |
+| `offboard` | Ground station | `robot-offboard` ×N + `gcs-real` | `lite_offload_global:offboard` |
+
+The hardware-profile defaults are redefinable per deployment (env /
+`--env-file` / `--stack`).
**Simulator profiles (mutually exclusive, `desktop`/`desktop_split` only):**
@@ -46,7 +53,7 @@ Combine with a simulator profile.
```
Dev desktop
├── simulator (isaac-sim / ms-airsim / simple)
-├── robot-desktop × N [role: full]
+├── robot-desktop × N [stack: full_default]
└── gcs
```
@@ -76,8 +83,8 @@ Use this to debug the split configuration and domain bridge without needing phys
```
Dev desktop
├── simulator (isaac-sim / ms-airsim / simple)
-├── robot-desktop-onboard × N [role: onboard, ROS_DOMAIN_ID = 1..N]
-├── robot-offboard × N [role: offboard, ROS_DOMAIN_ID = 0]
+├── robot-desktop-onboard × N [stack: lite_default, ROS_DOMAIN_ID = 1..N]
+├── robot-offboard × N [stack: lite_offload_global:offboard, ROS_DOMAIN_ID = 0]
└── gcs [domain 0]
```
@@ -91,8 +98,11 @@ airstack --profile desktop_split --profile isaac-sim up
!!! note "Domain isolation"
Onboard containers run on `ROS_DOMAIN_ID` 1, 2, 3… (one per robot).
All offboard containers and the GCS share `ROS_DOMAIN_ID=0`.
- A `domain_bridge` node inside each `robot-offboard` container bridges only the
- necessary topics across the domain boundary to avoid flooding the radio link.
+ The DDS router bridges only the topics listed in the split stack's
+ `bridge.yaml` across the domain boundary to avoid flooding the radio
+ link — generate its config first:
+ `python3 tools/gen_dds_router.py stacks/lite_offload_global/bridge.yaml`
+ (or `airstack fleet generate `).
---
@@ -142,18 +152,23 @@ NUM_ROBOTS=3 airstack --profile offboard up
If `AUTOLAUNCH=false`, containers start idle. Launch manually inside the container:
```bash
-# Full role (desktop or l4t):
-ros2 launch autonomy_bringup robot.launch.xml role:=full sim:=false
+# Full stack (desktop or l4t) — also the default with no stack args:
+ros2 launch autonomy_bringup robot.launch.xml sim:=false \
+ stack_dir:=/root/AirStack/stacks/full_default
-# Onboard role (VOXL, l4t_lite, desktop_split onboard):
-ros2 launch autonomy_bringup robot.launch.xml role:=onboard sim:=false
+# Lite stack (VOXL, l4t_lite, desktop_split onboard):
+ros2 launch autonomy_bringup robot.launch.xml sim:=false \
+ stack_dir:=/root/AirStack/stacks/lite_default
-# Offboard role (GCS):
-ros2 launch autonomy_bringup robot.launch.xml role:=offboard sim:=false
+# Offboard half of the split stack (GCS):
+ros2 launch autonomy_bringup robot.launch.xml sim:=false \
+ stack_dir:=/root/AirStack/stacks/lite_offload_global stack_entry:=offboard
```
-`desktop_bringup` wraps the above and adds RViz (only when `sim:=true`):
+`desktop_bringup` wraps the above and adds RViz (only when `sim:=true`);
+the stack selection flows through the `AIRSTACK_STACK_DIR` /
+`AIRSTACK_STACK_ENTRY` env vars:
```bash
-ros2 launch desktop_bringup robot.launch.xml role:=full sim:=true
+ros2 launch desktop_bringup robot.launch.xml sim:=true
```
diff --git a/docs/robot/configuration/index.md b/docs/robot/configuration/index.md
index d6d527b73..db9b5a988 100644
--- a/docs/robot/configuration/index.md
+++ b/docs/robot/configuration/index.md
@@ -25,7 +25,8 @@ ROS_DOMAIN_ID=0
# Launch Configuration
AUTOLAUNCH=true
-AUTONOMY_MODE=onboard_all
+# Stack selection (stacks are the only dispatch; unset = full_default)
+AIRSTACK_STACK_DIR=/root/AirStack/stacks/full_default
# Sensor Configuration
ENABLE_CAMERA=true
diff --git a/docs/robot/index.md b/docs/robot/index.md
index 2351d77e2..7d833b7d7 100644
--- a/docs/robot/index.md
+++ b/docs/robot/index.md
@@ -58,20 +58,24 @@ The robot autonomy stack is launched via Docker Compose. The configuration is in
### Launch Command Hierarchy
-The Docker `command:` attribute launches the top-level ROS 2 launch file, which cascades through autonomy layers:
+The Docker `command:` attribute launches the top-level ROS 2 launch file,
+which runs a shared preamble and then the selected **stack** entry file
+(stacks are the only dispatch — the legacy AUTONOMY_ROLE layer cascade was
+removed; see [Stacks](../development/stacks.md)):
```
-robot.launch.xml # Entry point (robot_bringup)
- └── autonomy.launch.xml # Autonomy orchestration (autonomy_bringup)
- ├── interface.launch.xml # Hardware interface
- ├── sensors.launch.xml # Sensor drivers
- ├── perception.launch.xml # State estimation
- ├── local.launch.xml # Local planning & control
- ├── global.launch.xml # Global planning & mapping
- └── behavior.launch.xml # Mission execution
+robot.launch.xml # Entry point (autonomy_bringup):
+ ├── (preamble: namespace, use_sim_time, robot_state_publisher, world→map TF)
+ └── stacks//launch/.launch.xml # The stack entry file —
+ ├── interface.launch.py # a flat list of module
+ ├── lidar_point_cloud_filter.launch.xml # includes; every connection
+ ├── stereo_image_proc.launch.xml # is written down here
+ ├── ... (local, global, behavior modules)
+ └── interpolate_dds_router / gossip
```
-Each `*_bringup` package contains launch files that orchestrate modules in that layer.
+No stack selected = `stacks/full_default`. Each module package ships its own
+canonical launch file; the stack entry file is the single wiring locus.
### Quick Reference
diff --git a/docs/tutorials/index.md b/docs/tutorials/index.md
index 08dbbdaee..005a3f67a 100644
--- a/docs/tutorials/index.md
+++ b/docs/tutorials/index.md
@@ -7,5 +7,5 @@ Step-by-step guides for common AirStack workflows. If you are new, start with **
| [Getting Started](../getting_started.md) | Install AirStack, pull Docker images, launch a simulated robot, and fly it for the first time. |
| [AirStack on OSMO (Mac/Windows OK)](airstack_on_osmo.md) | Develop on AirStack from a Mac, Windows, or no-GPU Linux laptop using NVIDIA OSMO + VS Code/Cursor Remote-SSH. No local Docker or local `airstack install`; use a local repo clone for the `airstack osmo:*` wrappers and workflow YAML. |
| [Multi-Robot Simulation](multi_robot_simulation.md) | Spin up multiple simulated robots in Isaac Sim and verify independent ROS 2 namespaces. |
-| [Autonomy Modes](autonomy_modes.md) | Understand `onboard_all`, `onboard_local`, and `offboard_global` modes and the commands to run each. |
+| [Autonomy Modes](../robot/autonomy_modes.md) | Understand the `full_default`, `lite_default`, and `lite_offload_global` stack topologies and the commands to run each. |
| [Deploying to Hardware](deploying_to_hardware.md) | Flash a Jetson or VOXL device, configure the robot hostname, and run the autonomy stack on a real drone. |
diff --git a/mkdocs.yml b/mkdocs.yml
index b0f4d9ad8..c2e1a9f41 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -10,6 +10,18 @@ exclude_docs: |
**/ros_ws/install
**/kit-app-template/**
**/isaac_sim_data/**
+ # Fetched module checkouts (RFC #379 §9): the docs deploy workflows clone the
+ # registry index + each registered module repo into the gitignored modules/
+ # dir. Their content is linked from docs/modules/ (generated by
+ # tools/gen_docs_catalog.py), never embedded as site pages this phase — so
+ # exclude the checkouts and their overlay symlinks entirely.
+ modules/**
+ robot/ros_ws/src/modules/**
+ simulation/isaac-sim/launch_scripts/modules/**
+ # Dot-prefixed dirs (stacks/.external, .airstack) are already skipped by
+ # MkDocs' default dotfile rule; listed here to keep that intent explicit.
+ .airstack/**
+ stacks/.external/**
extra:
version:
provider: mike
@@ -51,6 +63,7 @@ nav:
- Home: docs/index.md
- Getting Started:
- docs/getting_started/index.md
+ - Modular AirStack Walkthrough: docs/getting_started/modular_airstack.md
- docs/tutorials/airstack_on_osmo.md
- docs/getting_started/tutorials_reference.md
- Development:
@@ -165,6 +178,17 @@ nav:
- ROS Bags: docs/robot/logging/rosbags.md
- Data Offloading: docs/robot/logging/data_offloading.md
- Bag Recorder: common/ros_packages/bag_recorder_pid/README.md
+ - Modules:
+ - Catalog: docs/modules/index.md
+ - dfm2_disturbances: docs/modules/dfm2_disturbances.md
+ - macvo: docs/modules/macvo.md
+ - optitrack: docs/modules/optitrack.md
+ - Reference Stacks:
+ - full_default: stacks/full_default/README.md
+ - full_droan_cpu: stacks/full_droan_cpu/README.md
+ - full_macvo: stacks/full_macvo/README.md
+ - lite_default: stacks/lite_default/README.md
+ - lite_offload_global: stacks/lite_offload_global/README.md
- Ground Control Station:
- docs/gcs/index.md
- Docker:
diff --git a/overrides/l4t-px4-realrobot.env b/overrides/l4t-px4-realrobot.env
index c2ebecbc6..3ab0aca06 100644
--- a/overrides/l4t-px4-realrobot.env
+++ b/overrides/l4t-px4-realrobot.env
@@ -6,12 +6,12 @@
# Run:
# airstack up --env-file overrides/l4t-px4-realrobot.env robot-l4t
#
-# This file selects HARDWARE (Jetson profile, serial FCU, bag storage) — not
-# topology. Topology selection is orthogonal and layers on the same command:
-# --stack full_default instead of AUTONOMY_ROLE (docs/development/stacks.md)
+# This file selects HARDWARE (Jetson profile, serial FCU, bag storage) plus a
+# default stack. Topology selection layers on the same command:
+# --stack override the stack below (docs/development/stacks.md)
# --fleet fleet-file identity/placement resolution; replaces
# the hostname→robot_name mapping and, per robot,
-# AUTONOMY_ROLE/URDF_FILE (docs/development/fleets.md)
+# AIRSTACK_STACK_DIR/URDF_FILE (docs/development/fleets.md)
# Only bring up the Jetson stack (robot-l4t + zed-l4t).
COMPOSE_PROFILES="l4t"
@@ -25,8 +25,10 @@ NUM_ROBOTS="1"
# Run the following: ``hostnamectl set-hostname robot-1``
# resulting in robot_1 on domain 1.
-# Launches entire robot autonomy stack
-AUTONOMY_ROLE="full"
+# Launches the entire robot autonomy stack (stacks are the only dispatch —
+# the legacy AUTONOMY_ROLE was removed). Container path: stacks/ is
+# bind-mounted at /root/AirStack/stacks.
+AIRSTACK_STACK_DIR="/root/AirStack/stacks/full_default"
# --- Flight controller (MAVROS) ----------------------------------------------
# Default is the Jetson UART (ttyTHS4).
diff --git a/robot/docker/docker-compose.yaml b/robot/docker/docker-compose.yaml
index 5168de0c4..a5110fb68 100644
--- a/robot/docker/docker-compose.yaml
+++ b/robot/docker/docker-compose.yaml
@@ -30,10 +30,12 @@ services:
- AUTOLAUNCH=${AUTOLAUNCH:-true}
- NVIDIA_DRIVER_CAPABILITIES=all
- LAUNCH_PACKAGE=desktop_bringup # desktop_bringup adds RViz; real robots use autonomy_bringup
- - AUTONOMY_ROLE=full
- SIM_IP=${SIM_IP:-172.31.0.200}
# FCU_URL and TGT_SYSTEM not set, dynamically calculated in interface.launch.py
- # 'command' uses variables so that it can be shared across robot-desktop and robot-l4t, with different launch packages and roles.
+ # 'command' uses variables so that it can be shared across robot-desktop and robot-l4t, with
+ # different launch packages. The stack to launch comes from AIRSTACK_STACK_DIR /
+ # AIRSTACK_STACK_ENTRY (robot-base defaults: stacks/full_default) — stacks are the only
+ # dispatch (the legacy AUTONOMY_ROLE role arg was removed).
command: >
bash -c "
if [ -z \"$$DISPLAY\" ] && command -v Xvfb >/dev/null 2>&1; then
@@ -44,7 +46,7 @@ services:
service ssh restart;
tmux new -d -s bringup;
if [ $$AUTOLAUNCH == 'true' ]; then
- tmux send-keys -t bringup:0.0 'bws && sws && ros2 launch $$LAUNCH_PACKAGE robot.launch.xml role:=$$AUTONOMY_ROLE' ENTER;
+ tmux send-keys -t bringup:0.0 'bws && sws && ros2 launch $$LAUNCH_PACKAGE robot.launch.xml' ENTER;
fi;
sleep infinity"
# assumes you're connected to work internet, so creates a network to isolate from other developers on your work internet
@@ -67,8 +69,9 @@ services:
# ===================================================================================================================
# desktop_split: simulates onboard computer on desktop for debugging the split configuration.
- # Same image as robot-desktop; role=onboard means only lite autonomy modules launch.
- # robot-offboard (below) runs concurrently on the same machine with role=offboard.
+ # Same image as robot-desktop; the lite_default stack launches only the lite autonomy modules
+ # (interface, sensors, perception, local, behavior — no global/logging).
+ # robot-offboard (below) runs concurrently on the same machine with the split stack's offboard half.
robot-desktop-onboard:
profiles: !override
- desktop_split
@@ -76,7 +79,7 @@ services:
file: ./docker-compose.yaml
service: robot-desktop
environment:
- - AUTONOMY_ROLE=onboard
+ - AIRSTACK_STACK_DIR=/root/AirStack/stacks/lite_default
- LAUNCH_PACKAGE=desktop_bringup # for no RViz, change to autonomy_bringup to treat as simulated onboard computer
# ===================================================================================================================
@@ -104,9 +107,16 @@ services:
ports: !reset {} # offboard containers don't need ssh ports
environment:
- ROBOT_NAME_SOURCE=container_name # robot_name_map resolves replica index → robot_N
- - ROS_DOMAIN_ID=0 # all offboard containers share domain 0; domain_bridge connects to per-robot onboard domains
+ - ROS_DOMAIN_ID=0 # all offboard containers share domain 0; the split stack's DDS router connects to per-robot onboard domains
- LAUNCH_PACKAGE=autonomy_bringup # no RViz per-robot; visualization lives in the GCS container
- - AUTONOMY_ROLE=offboard # runs global planner only
+ # Ground half of the split stack: global planning only (RFC #380 §2).
+ # NOTE: the paired vehicle half (lite_offload_global:onboard) loads the
+ # DDS-router config GENERATED from the stack's bridge.yaml — run
+ # `airstack fleet generate ` or
+ # `python3 tools/gen_dds_router.py stacks/lite_offload_global/bridge.yaml`
+ # before bringing this profile up.
+ - AIRSTACK_STACK_DIR=/root/AirStack/stacks/lite_offload_global
+ - AIRSTACK_STACK_ENTRY=offboard
# inherit deploy.replicas from robot-desktop (NUM_ROBOTS)
# inherit bridge network from robot-desktop
@@ -132,16 +142,18 @@ services:
cache_from:
- *voxl_image
- *voxl_cache
- environment:
+ environment:
- ROBOT_NAME_SOURCE=hostname # see .bashrc
- AUTOLAUNCH=${AUTOLAUNCH:-true}
- LAUNCH_PACKAGE=autonomy_bringup
- - AUTONOMY_ROLE=onboard # VOXL is always lite-only; never runs global planning
+ # VOXL is compute-constrained: default to the lite stack (no global
+ # planning/logging). Hardware default — redefine at will via env/--env-file.
+ - AIRSTACK_STACK_DIR=${AIRSTACK_STACK_DIR:-/root/AirStack/stacks/lite_default}
command: >
bash -c "
tmux new -d -s bringup;
if [ $$AUTOLAUNCH == 'true' ]; then
- tmux send-keys -t bringup 'bws && sws && ros2 launch $$LAUNCH_PACKAGE robot.launch.xml role:=$$AUTONOMY_ROLE sim:=false' ENTER;
+ tmux send-keys -t bringup 'bws && sws && ros2 launch $$LAUNCH_PACKAGE robot.launch.xml sim:=false' ENTER;
fi;
sleep infinity"
network_mode: host
@@ -200,7 +212,7 @@ services:
service ssh restart;
tmux new -d -s bringup;
if [ $$AUTOLAUNCH == 'true' ]; then
- tmux send-keys -t bringup 'bws && sws && ros2 launch $$LAUNCH_PACKAGE robot.launch.xml role:=$$AUTONOMY_ROLE sim:=false' ENTER;
+ tmux send-keys -t bringup 'bws && sws && ros2 launch $$LAUNCH_PACKAGE robot.launch.xml sim:=false' ENTER;
fi;
sleep infinity"
deploy:
@@ -215,7 +227,9 @@ services:
- ROBOT_NAME_SOURCE=hostname
- AUTOLAUNCH=${AUTOLAUNCH:-true}
- LAUNCH_PACKAGE=autonomy_bringup
- - AUTONOMY_ROLE=${AUTONOMY_ROLE:-full}
+ # Autonomous Jetson runs the full stack by default. Hardware default —
+ # redefine at will via env/--env-file (e.g. a lite stack for weak Jetsons).
+ - AIRSTACK_STACK_DIR=${AIRSTACK_STACK_DIR:-/root/AirStack/stacks/full_default}
# mavros mavlink settings
- FCU_URL=${FCU_URL:-/dev/ttyTHS4:115200}
- TGT_SYSTEM=1
@@ -234,7 +248,9 @@ services:
file: ./docker-compose.yaml
service: robot-l4t
environment:
- - AUTONOMY_ROLE=onboard
+ # Hardware default — redefine at will (e.g. lite_offload_global:onboard
+ # when pairing with a ground host running the offboard half).
+ - AIRSTACK_STACK_DIR=${AIRSTACK_STACK_DIR:-/root/AirStack/stacks/lite_default}
# -----------------------
# for running the zed camera driver on an NVIDIA jetson (linux for tegra) device. This ONLY runs the zed driver
diff --git a/robot/docker/robot-base-docker-compose.yaml b/robot/docker/robot-base-docker-compose.yaml
index 92e95d2eb..dad848a49 100644
--- a/robot/docker/robot-base-docker-compose.yaml
+++ b/robot/docker/robot-base-docker-compose.yaml
@@ -14,17 +14,18 @@ services:
- RECORD_BAGS=${RECORD_BAGS}
- LOG_CONFIG=${LOG_CONFIG:-log.yaml}
# docker compose interpolation to env variables
- - AUTONOMY_ROLE=${AUTONOMY_ROLE:-full}
- - URDF_FILE=${URDF_FILE}
+ - URDF_FILE=${URDF_FILE}
# MAVROS
- OFFBOARD_BASE_PORT=${OFFBOARD_BASE_PORT}
- ONBOARD_BASE_PORT=${ONBOARD_BASE_PORT}
- ROBOT_NAME_MAP_CONFIG_FILE=${ROBOT_NAME_MAP_CONFIG_FILE:-default_robot_name_map.yaml}
- DEBUG_RVIZ=${DEBUG_RVIZ:-false}
- # Stack dispatch (RFC #379): set by `airstack up --stack ` to the
- # CONTAINER path of the stack folder (/root/AirStack/stacks/).
- # Empty = legacy AUTONOMY_ROLE dispatch in robot.launch.xml.
- - AIRSTACK_STACK_DIR=${AIRSTACK_STACK_DIR:-}
+ # Stack dispatch (RFC #379) — the ONLY dispatch (the legacy
+ # AUTONOMY_ROLE role dispatch was removed). Set by `airstack up
+ # --stack ` to the CONTAINER path of the stack folder
+ # (/root/AirStack/stacks/); unset = the trunk reference stack
+ # full_default.
+ - AIRSTACK_STACK_DIR=${AIRSTACK_STACK_DIR:-/root/AirStack/stacks/full_default}
- AIRSTACK_STACK_ENTRY=${AIRSTACK_STACK_ENTRY:-stack}
# Fleet dispatch (RFC #380): set by `airstack up --fleet ` to the
# CONTAINER path of the fleet file (/root/AirStack/config/fleets/...).
diff --git a/robot/ros_ws/src/autonomy_bringup/CMakeLists.txt b/robot/ros_ws/src/autonomy_bringup/CMakeLists.txt
index b8335e4a4..c5e46605b 100644
--- a/robot/ros_ws/src/autonomy_bringup/CMakeLists.txt
+++ b/robot/ros_ws/src/autonomy_bringup/CMakeLists.txt
@@ -14,9 +14,10 @@ if(BUILD_TESTING)
ament_lint_auto_find_test_dependencies()
endif()
+# config/ carries the shared DDS-router/domain-bridge YAMLs (moved up from
+# the removed AUTONOMY_ROLE role folders onboard_all/ and
+# onboard_local_offboard_global/ — stacks select them directly).
install(DIRECTORY config DESTINATION share/${PROJECT_NAME})
install(DIRECTORY launch DESTINATION share/${PROJECT_NAME})
-install(DIRECTORY onboard_all DESTINATION share/${PROJECT_NAME})
-install(DIRECTORY onboard_local_offboard_global DESTINATION share/${PROJECT_NAME})
ament_package()
diff --git a/robot/ros_ws/src/autonomy_bringup/onboard_all/config/dds_router.yaml b/robot/ros_ws/src/autonomy_bringup/config/dds_router.yaml
similarity index 85%
rename from robot/ros_ws/src/autonomy_bringup/onboard_all/config/dds_router.yaml
rename to robot/ros_ws/src/autonomy_bringup/config/dds_router.yaml
index 8a4e1a379..b2219d533 100644
--- a/robot/ros_ws/src/autonomy_bringup/onboard_all/config/dds_router.yaml
+++ b/robot/ros_ws/src/autonomy_bringup/config/dds_router.yaml
@@ -11,7 +11,13 @@
# all topics are bidirectional by default. See https://eprosima-dds-router.readthedocs.io/ for more details
-# onboard_all DDS Router
+# Shared robot <-> GCS DDS Router allowlist (autonomy_bringup/config/).
+# History: lived at onboard_all/config/ under the removed AUTONOMY_ROLE
+# dispatch; stacks (RFC #379) now select it directly — the full_* stacks and
+# lite_default point their interpolate_dds_router include here. (The legacy
+# onboard_local_offboard_global extension of this file merged to an identical
+# allowlist, so lite_default uses this base unchanged; the lite_offload_global
+# split stack instead GENERATES its router config from its bridge.yaml.)
participants:
- name: "robot"
kind: "local"
diff --git a/robot/ros_ws/src/autonomy_bringup/onboard_all/config/dds_router_echo.yaml b/robot/ros_ws/src/autonomy_bringup/config/dds_router_echo.yaml
similarity index 100%
rename from robot/ros_ws/src/autonomy_bringup/onboard_all/config/dds_router_echo.yaml
rename to robot/ros_ws/src/autonomy_bringup/config/dds_router_echo.yaml
diff --git a/robot/ros_ws/src/autonomy_bringup/onboard_all/config/dds_router_vanilla.yaml b/robot/ros_ws/src/autonomy_bringup/config/dds_router_vanilla.yaml
similarity index 100%
rename from robot/ros_ws/src/autonomy_bringup/onboard_all/config/dds_router_vanilla.yaml
rename to robot/ros_ws/src/autonomy_bringup/config/dds_router_vanilla.yaml
diff --git a/robot/ros_ws/src/autonomy_bringup/onboard_all/config/domain_bridge.yaml b/robot/ros_ws/src/autonomy_bringup/config/domain_bridge.yaml
similarity index 82%
rename from robot/ros_ws/src/autonomy_bringup/onboard_all/config/domain_bridge.yaml
rename to robot/ros_ws/src/autonomy_bringup/config/domain_bridge.yaml
index 34e7e1d26..72775238b 100644
--- a/robot/ros_ws/src/autonomy_bringup/onboard_all/config/domain_bridge.yaml
+++ b/robot/ros_ws/src/autonomy_bringup/config/domain_bridge.yaml
@@ -1,3 +1,7 @@
+# Shared robot <-> GCS domain_bridge topic map (autonomy_bringup/config/).
+# History: lived at onboard_all/config/ under the removed AUTONOMY_ROLE
+# dispatch. Currently has no launch caller (interpolate_domain_bridge.launch.py
+# is the loader); kept as the reference topic map for domain_bridge users.
name: onboard_all_bridge
topics:
# ============= Outgoing from Robot ================
diff --git a/robot/ros_ws/src/autonomy_bringup/launch/robot.launch.xml b/robot/ros_ws/src/autonomy_bringup/launch/robot.launch.xml
index 77cba4f66..fc250b5a7 100644
--- a/robot/ros_ws/src/autonomy_bringup/launch/robot.launch.xml
+++ b/robot/ros_ws/src/autonomy_bringup/launch/robot.launch.xml
@@ -1,29 +1,40 @@
-
-
-
-
-
-
-
+
+
@@ -43,59 +54,7 @@ lite, desktop_split sim)
name="world_to_map_broadcaster"
args="0 0 0 0 0 0 world map" />
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
diff --git a/robot/ros_ws/src/autonomy_bringup/onboard_all/launch/onboard_autonomy_all.launch.xml b/robot/ros_ws/src/autonomy_bringup/onboard_all/launch/onboard_autonomy_all.launch.xml
deleted file mode 100644
index f9e9d414d..000000000
--- a/robot/ros_ws/src/autonomy_bringup/onboard_all/launch/onboard_autonomy_all.launch.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/robot/ros_ws/src/autonomy_bringup/onboard_all/launch/static_transforms.launch.xml b/robot/ros_ws/src/autonomy_bringup/onboard_all/launch/static_transforms.launch.xml
deleted file mode 100644
index e28cda2ce..000000000
--- a/robot/ros_ws/src/autonomy_bringup/onboard_all/launch/static_transforms.launch.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/robot/ros_ws/src/autonomy_bringup/onboard_local_offboard_global/config/dds_router.yaml b/robot/ros_ws/src/autonomy_bringup/onboard_local_offboard_global/config/dds_router.yaml
deleted file mode 100644
index 221722a85..000000000
--- a/robot/ros_ws/src/autonomy_bringup/onboard_local_offboard_global/config/dds_router.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-# onboard_all DDS Router
-extends: "$(find-pkg-share autonomy_bringup)/onboard_all/config/dds_router.yaml"
-
-allowlist:
- # ============= Outgoing Robot --> GCS ================
-
- - name: "rt/$(env ROBOT_NAME)/sensors/front_stereo/left/image_rect"
- - name: "rt/$(env ROBOT_NAME)/sensors/front_stereo/left/camera_info"
- - name: "rt/$(env ROBOT_NAME)/sensors/front_stereo/right/image_rect"
- - name: "rt/$(env ROBOT_NAME)/sensors/front_stereo/right/camera_info"
-
- # ============= Incoming GCS --> Robot ================
- # control commands
-
- - name: "rt/$(env ROBOT_NAME)/global_plan"
\ No newline at end of file
diff --git a/robot/ros_ws/src/autonomy_bringup/onboard_local_offboard_global/config/domain_bridge.yaml b/robot/ros_ws/src/autonomy_bringup/onboard_local_offboard_global/config/domain_bridge.yaml
deleted file mode 100644
index 7bf7c15fe..000000000
--- a/robot/ros_ws/src/autonomy_bringup/onboard_local_offboard_global/config/domain_bridge.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: onboard_offboard_bridge
-extends: "$(find-pkg-share autonomy_bringup)/onboard_all/config/domain_bridge.yaml"
-topics:
- # ============= Additional Outgoing from Robot ================
-
- # ============= Additional Incoming to Robot ================
-
- # to offboard modules
- global_plan:
- type: nav_msgs/msg/Path
- from_domain: $(var gcs_domain)
- to_domain: $(env ROS_DOMAIN_ID)
\ No newline at end of file
diff --git a/robot/ros_ws/src/autonomy_bringup/onboard_local_offboard_global/launch/offboard_autonomy_global.launch.xml b/robot/ros_ws/src/autonomy_bringup/onboard_local_offboard_global/launch/offboard_autonomy_global.launch.xml
deleted file mode 100644
index 8354b5907..000000000
--- a/robot/ros_ws/src/autonomy_bringup/onboard_local_offboard_global/launch/offboard_autonomy_global.launch.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/robot/ros_ws/src/autonomy_bringup/onboard_local_offboard_global/launch/onboard_autonomy_local.launch.xml b/robot/ros_ws/src/autonomy_bringup/onboard_local_offboard_global/launch/onboard_autonomy_local.launch.xml
deleted file mode 100644
index 6fd49bd6c..000000000
--- a/robot/ros_ws/src/autonomy_bringup/onboard_local_offboard_global/launch/onboard_autonomy_local.launch.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/robot/ros_ws/src/behavior/behavior_bringup/CMakeLists.txt b/robot/ros_ws/src/behavior/behavior_bringup/CMakeLists.txt
deleted file mode 100644
index b62906b63..000000000
--- a/robot/ros_ws/src/behavior/behavior_bringup/CMakeLists.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-cmake_minimum_required(VERSION 3.8)
-project(behavior_bringup)
-
-if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
- add_compile_options(-Wall -Wextra -Wpedantic)
-endif()
-
-# find dependencies
-find_package(ament_cmake REQUIRED)
-# uncomment the following section in order to fill in
-# further dependencies manually.
-# find_package( REQUIRED)
-
-if(BUILD_TESTING)
- find_package(ament_lint_auto REQUIRED)
- # the following line skips the linter which checks for copyrights
- # comment the line when a copyright and license is added to all source files
- set(ament_cmake_copyright_FOUND TRUE)
- # the following line skips cpplint (only works in a git repo)
- # comment the line when this package is in a git repo and when
- # a copyright and license is added to all source files
- set(ament_cmake_cpplint_FOUND TRUE)
- ament_lint_auto_find_test_dependencies()
-endif()
-
-# Install files.
-install(DIRECTORY launch DESTINATION share/${PROJECT_NAME})
-# install(DIRECTORY rviz DESTINATION share/${PROJECT_NAME})
-# install(DIRECTORY config DESTINATION share/${PROJECT_NAME})
-# install(DIRECTORY params DESTINATION share/${PROJECT_NAME})
-
-ament_package()
diff --git a/robot/ros_ws/src/behavior/behavior_bringup/LICENSE b/robot/ros_ws/src/behavior/behavior_bringup/LICENSE
deleted file mode 100644
index d64569567..000000000
--- a/robot/ros_ws/src/behavior/behavior_bringup/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/robot/ros_ws/src/behavior/behavior_bringup/launch/behavior.launch.xml b/robot/ros_ws/src/behavior/behavior_bringup/launch/behavior.launch.xml
deleted file mode 100644
index cef093702..000000000
--- a/robot/ros_ws/src/behavior/behavior_bringup/launch/behavior.launch.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/robot/ros_ws/src/behavior/behavior_bringup/package.xml b/robot/ros_ws/src/behavior/behavior_bringup/package.xml
deleted file mode 100644
index fbdc2cd6a..000000000
--- a/robot/ros_ws/src/behavior/behavior_bringup/package.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
- behavior_bringup
- 0.0.0
- TODO: Package description
- andrew
- Apache-2.0
-
- ament_cmake
-
- ament_lint_auto
- ament_lint_common
-
-
- ament_cmake
-
-
diff --git a/robot/ros_ws/src/global/global_bringup/CMakeLists.txt b/robot/ros_ws/src/global/global_bringup/CMakeLists.txt
index 77d079414..1c0214f8d 100644
--- a/robot/ros_ws/src/global/global_bringup/CMakeLists.txt
+++ b/robot/ros_ws/src/global/global_bringup/CMakeLists.txt
@@ -23,10 +23,9 @@ if(BUILD_TESTING)
ament_lint_auto_find_test_dependencies()
endif()
-# Install files.
-install(DIRECTORY launch DESTINATION share/${PROJECT_NAME})
-# install(DIRECTORY rviz DESTINATION share/${PROJECT_NAME})
+# Install files. No launch/ anymore: the legacy global.launch.xml went with
+# the removed AUTONOMY_ROLE dispatch — stacks include vdb_mapping_ros2.py and
+# random_walk_planner.launch.xml directly, selecting the config/ files here.
install(DIRECTORY config DESTINATION share/${PROJECT_NAME})
-# install(DIRECTORY params DESTINATION share/${PROJECT_NAME})
ament_package()
diff --git a/robot/ros_ws/src/global/global_bringup/launch/global.launch.xml b/robot/ros_ws/src/global/global_bringup/launch/global.launch.xml
deleted file mode 100644
index c0e7dcf40..000000000
--- a/robot/ros_ws/src/global/global_bringup/launch/global.launch.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/robot/ros_ws/src/global/global_bringup/launch/global_planner.launch.xml b/robot/ros_ws/src/global/global_bringup/launch/global_planner.launch.xml
deleted file mode 100644
index 939903f83..000000000
--- a/robot/ros_ws/src/global/global_bringup/launch/global_planner.launch.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/robot/ros_ws/src/global/planners/ensemble_planner/CMakeLists.txt b/robot/ros_ws/src/global/planners/ensemble_planner/CMakeLists.txt
deleted file mode 100644
index 9de81c1bd..000000000
--- a/robot/ros_ws/src/global/planners/ensemble_planner/CMakeLists.txt
+++ /dev/null
@@ -1,75 +0,0 @@
-cmake_minimum_required(VERSION 3.5)
-project(ensemble_global_planner)
-
-# Default to C++14
-if(NOT CMAKE_CXX_STANDARD)
- set(CMAKE_CXX_STANDARD 17)
-endif()
-
-# Find dependencies
-find_package(ament_cmake REQUIRED)
-find_package(rclcpp REQUIRED)
-find_package(rclcpp_action REQUIRED)
-find_package(std_msgs REQUIRED)
-find_package(std_srvs REQUIRED)
-find_package(geometry_msgs REQUIRED)
-find_package(nav_msgs REQUIRED)
-find_package(visualization_msgs REQUIRED)
-find_package(tf2 REQUIRED)
-find_package(tf2_ros REQUIRED)
-
-###################################
-## ament specific configuration ##
-###################################
-
-ament_package()
-
-###########
-## Build ##
-###########
-
-# Specify additional locations of header files
-
-# Declare a C++ executable
-add_executable(ensemble_global_planner src/ensemble_global_planner_node.cpp)
-
-# Link libraries
-ament_target_dependencies(ensemble_global_planner
- rclcpp
- rclcpp_action
- std_msgs
- std_srvs
- geometry_msgs
- nav_msgs
- visualization_msgs
- tf2
- tf2_ros
-)
-
-# Install executable
-install(TARGETS
- ensemble_global_planner
- DESTINATION lib/${PROJECT_NAME}
-)
-# install(DIRECTORY
-# launch
-# DESTINATION share/${PROJECT_NAME})
-
-install(DIRECTORY
- config
- DESTINATION share/${PROJECT_NAME})
-
-install(DIRECTORY
- src
- DESTINATION share/${PROJECT_NAME})
-
-
-#############
-## Testing ##
-#############
-
-# Add gtest based cpp test target and link libraries
-# ament_add_gtest(${PROJECT_NAME}-test test/test_ensemble_global_planner.cpp)
-# if(TARGET ${PROJECT_NAME}-test)
-# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}_node)
-# endif()
\ No newline at end of file
diff --git a/robot/ros_ws/src/global/planners/ensemble_planner/config/ensemble_global_planner_config.yaml b/robot/ros_ws/src/global/planners/ensemble_planner/config/ensemble_global_planner_config.yaml
deleted file mode 100644
index efc46f87f..000000000
--- a/robot/ros_ws/src/global/planners/ensemble_planner/config/ensemble_global_planner_config.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-/**:
- ros__parameters:
- srv_global_plan_toggle_topic: "~/global_plan_toggle"
- way_point_planners:
- - name: "random_walk"
- config:
- frequency: 1.0 #hz
-
diff --git a/robot/ros_ws/src/global/planners/ensemble_planner/include/ensemble_global_planner_node.hpp b/robot/ros_ws/src/global/planners/ensemble_planner/include/ensemble_global_planner_node.hpp
deleted file mode 100644
index f989e86cf..000000000
--- a/robot/ros_ws/src/global/planners/ensemble_planner/include/ensemble_global_planner_node.hpp
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (c) 2024 Carnegie Mellon University
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-
-#pragma once
-
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "rclcpp/rclcpp.hpp"
-
-class EnsembleGlobalPlannerNode : public rclcpp::Node {
- private:
- // String constants
- std::string srv_global_plan_toggle_topic_;
-
- void globalPlannnerToggleCallback(const std_srvs::srv::Trigger::Request::SharedPtr request,
- std_srvs::srv::Trigger::Response::SharedPtr response);
-
- // Other functions
- void readParameters();
-
- bool enable_global_planner = false;
-
- public:
- // explicit RandomWalkNode(const rclcpp::NodeOptions & options = rclcpp::NodeOptions());
- EnsembleGlobalPlannerNode();
- ~EnsembleGlobalPlannerNode() = default;
-
- // ROS subscribers
- rclcpp::Subscription::SharedPtr sub_map;
- rclcpp::Subscription::SharedPtr sub_robot_tf;
-
- // ROS publishers
- // rclcpp::Publisher::SharedPtr pub_global_path;
- rclcpp::Publisher::SharedPtr pub_goal_point;
- rclcpp::Publisher::SharedPtr pub_trajectory_lines;
-
- // ROS services
- rclcpp::Service::SharedPtr srv_global_planner_toggle;
-
- // ROS timers
- rclcpp::TimerBase::SharedPtr timer;
-};
diff --git a/robot/ros_ws/src/global/planners/ensemble_planner/package.xml b/robot/ros_ws/src/global/planners/ensemble_planner/package.xml
deleted file mode 100644
index fafdacbce..000000000
--- a/robot/ros_ws/src/global/planners/ensemble_planner/package.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
- ensemble_global_planner
- 0.0.0
- Ensemble planner to coordinate different groups of planners
-
-
-
-
- todo
-
-
-
-
-
- TODO
-
-
-
-
-
-
-
-
-
-
-
-
-
- ament_cmake
- rclcpp
- rclcpp_action
- std_msgs
- std_srvs
- base
- geometry_msgs
- nav_msgs
- visualization_msgs
- message_generation
- tf2
- tf2_ros
-
-
- ament_cmake
-
-
diff --git a/robot/ros_ws/src/global/planners/ensemble_planner/src/ensemble_global_planner_node.cpp b/robot/ros_ws/src/global/planners/ensemble_planner/src/ensemble_global_planner_node.cpp
deleted file mode 100644
index 254bf2498..000000000
--- a/robot/ros_ws/src/global/planners/ensemble_planner/src/ensemble_global_planner_node.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (c) 2024 Carnegie Mellon University
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-
-#include "../include/ensemble_global_planner_node.hpp"
-
-void EnsembleGlobalPlannerNode::readParameters() {
- this->declare_parameter("srv_global_plan_toggle_topic");
- if (!this->get_parameter("srv_global_plan_toggle_topic", this->srv_global_plan_toggle_topic_)) {
- RCLCPP_ERROR(this->get_logger(), "Cannot read parameter: srv_global_plan_toggle_topic");
- }
-}
-
-EnsembleGlobalPlannerNode::EnsembleGlobalPlannerNode() : Node("ensemble_global_planner_node") {
- // Initialize the Global Planner planner
- EnsembleGlobalPlannerNode::readParameters();
- this->srv_global_planner_toggle = this->create_service(
- srv_global_plan_toggle_topic_,
- std::bind(&EnsembleGlobalPlannerNode::globalPlannnerToggleCallback, this, std::placeholders::_1,
- std::placeholders::_2));
-}
-
-void EnsembleGlobalPlannerNode::globalPlannnerToggleCallback(
- const std_srvs::srv::Trigger::Request::SharedPtr request,
- std_srvs::srv::Trigger::Response::SharedPtr response) {
- if (this->enable_global_planner == false) {
- this->enable_global_planner = true;
- response->success = true;
- response->message = "Global Planner enabled";
- RCLCPP_INFO(this->get_logger(), "Global Planner enabled");
- } else {
- this->enable_global_planner = false;
- response->success = true;
- response->message = "Global Planer disabled";
- RCLCPP_INFO(this->get_logger(), "Global Planner disabled");
- }
-}
-
-int main(int argc, char *argv[]) {
- rclcpp::init(argc, argv);
- rclcpp::spin(std::make_shared());
- rclcpp::shutdown();
- return 0;
-}
diff --git a/robot/ros_ws/src/global/planners/exploration/README.md b/robot/ros_ws/src/global/planners/exploration/README.md
index 402b6d62e..2a18367e1 100644
--- a/robot/ros_ws/src/global/planners/exploration/README.md
+++ b/robot/ros_ws/src/global/planners/exploration/README.md
@@ -4,7 +4,10 @@ The Exploration Planner is an optional global planner for autonomous flight. It
## Functionality
-You can comment out the world model and random walk planning module in `global.launch.xml` and add the following line:
+In your stack's entry launch file (e.g. a copy of
+`stacks/full_default/launch/stack.launch.xml`), replace the
+`random_walk_planner.launch.xml` include (and optionally the vdb_mapping
+include) with:
``:
diff --git a/robot/ros_ws/src/global/planners/exploration/launch/exploration_launch.xml b/robot/ros_ws/src/global/planners/exploration/launch/exploration_launch.xml
index 90fd427ad..a6be878ea 100644
--- a/robot/ros_ws/src/global/planners/exploration/launch/exploration_launch.xml
+++ b/robot/ros_ws/src/global/planners/exploration/launch/exploration_launch.xml
@@ -1,3 +1,7 @@
+
diff --git a/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo.xml b/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo.xml
deleted file mode 100644
index eb8eb3ea1..000000000
--- a/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gazebo_vis.rviz b/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gazebo_vis.rviz
deleted file mode 100644
index d4e39c4b2..000000000
--- a/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gazebo_vis.rviz
+++ /dev/null
@@ -1,696 +0,0 @@
-Panels:
- - Class: rviz_common/Displays
- Help Height: 78
- Name: Displays
- Property Tree Widget:
- Expanded:
- - /Global Options1
- - /TF1/Frames1
- - /Sensors1
- - /Perception1
- - /Perception1/MACVO PointCloud1
- - /Local1
- - /Local1/DROAN1/Trimmed Global Plan for DROAN1/Topic1
- - /Global1
- - /Global1/VDB Mapping Marker1
- Splitter Ratio: 0.590062141418457
- Tree Height: 677
- - Class: rviz_common/Selection
- Name: Selection
- - Class: rviz_common/Tool Properties
- Expanded:
- - /2D Goal Pose1
- - /Publish Point1
- Name: Tool Properties
- Splitter Ratio: 0.5886790156364441
- - Class: rviz_common/Views
- Expanded:
- - /Current View1
- Name: Views
- Splitter Ratio: 0.5
- - Class: rviz_common/Time
- Experimental: false
- Name: Time
- SyncMode: 0
- SyncSource: ""
-Visualization Manager:
- Class: ""
- Displays:
- - Alpha: 0.5
- Cell Size: 1
- Class: rviz_default_plugins/Grid
- Color: 160; 160; 164
- Enabled: true
- Line Style:
- Line Width: 0.029999999329447746
- Value: Lines
- Name: Grid
- Normal Cell Count: 0
- Offset:
- X: 0
- Y: 0
- Z: 0
- Plane: XY
- Plane Cell Count: 100
- Reference Frame:
- Value: true
- - Class: rviz_default_plugins/TF
- Enabled: true
- Frame Timeout: 15
- Frames:
- All Enabled: false
- base_link:
- Value: true
- base_link_stabilized:
- Value: false
- look_ahead_point:
- Value: true
- look_ahead_point_stabilized:
- Value: false
- map:
- Value: true
- ouster:
- Value: false
- rmf_owl:
- Value: false
- rmf_owl/base_link:
- Value: false
- rmf_owl/base_link/air_pressure:
- Value: false
- rmf_owl/base_link/base_link_inertia_collision:
- Value: false
- rmf_owl/base_link/base_link_inertia_visual:
- Value: false
- rmf_owl/base_link/camera_front:
- Value: false
- rmf_owl/base_link/imu_sensor:
- Value: false
- rmf_owl/base_link/magnetometer:
- Value: false
- rmf_owl/camera_link:
- Value: false
- rmf_owl/camera_link/camera_collision:
- Value: false
- rmf_owl/camera_link/camera_visual:
- Value: false
- rmf_owl/camera_link/depth_camera_front:
- Value: false
- rmf_owl/camera_link/segmentation_camera:
- Value: false
- rmf_owl/laser_link:
- Value: false
- rmf_owl/laser_link/gpu_lidar:
- Value: false
- rmf_owl/laser_link/laser_collision:
- Value: false
- rmf_owl/laser_link/laser_visual:
- Value: false
- rmf_owl/rotor_0:
- Value: false
- rmf_owl/rotor_0/rotor_0_collision:
- Value: false
- rmf_owl/rotor_0/rotor_0_visual:
- Value: false
- rmf_owl/rotor_1:
- Value: false
- rmf_owl/rotor_1/rotor_1_collision:
- Value: false
- rmf_owl/rotor_1/rotor_1_visual:
- Value: false
- rmf_owl/rotor_2:
- Value: false
- rmf_owl/rotor_2/rotor_2_collision:
- Value: false
- rmf_owl/rotor_2/rotor_2_visual:
- Value: false
- rmf_owl/rotor_3:
- Value: false
- rmf_owl/rotor_3/rotor_3_collision:
- Value: false
- rmf_owl/rotor_3/rotor_3_visual:
- Value: false
- tracking_point:
- Value: true
- tracking_point_stabilized:
- Value: false
- world:
- Value: false
- Marker Scale: 2
- Name: TF
- Show Arrows: true
- Show Axes: true
- Show Names: true
- Tree:
- world:
- map:
- base_link_stabilized:
- {}
- look_ahead_point:
- {}
- look_ahead_point_stabilized:
- {}
- tracking_point:
- {}
- tracking_point_stabilized:
- {}
- rmf_owl:
- rmf_owl/base_link:
- base_link:
- {}
- rmf_owl/base_link/air_pressure:
- {}
- rmf_owl/base_link/base_link_inertia_collision:
- {}
- rmf_owl/base_link/base_link_inertia_visual:
- {}
- rmf_owl/base_link/camera_front:
- {}
- rmf_owl/base_link/imu_sensor:
- {}
- rmf_owl/base_link/magnetometer:
- {}
- rmf_owl/camera_link:
- rmf_owl/camera_link/camera_collision:
- {}
- rmf_owl/camera_link/camera_visual:
- {}
- rmf_owl/camera_link/depth_camera_front:
- {}
- rmf_owl/camera_link/segmentation_camera:
- {}
- rmf_owl/laser_link:
- rmf_owl/laser_link/gpu_lidar:
- ouster:
- {}
- rmf_owl/laser_link/laser_collision:
- {}
- rmf_owl/laser_link/laser_visual:
- {}
- rmf_owl/rotor_0:
- rmf_owl/rotor_0/rotor_0_collision:
- {}
- rmf_owl/rotor_0/rotor_0_visual:
- {}
- rmf_owl/rotor_1:
- rmf_owl/rotor_1/rotor_1_collision:
- {}
- rmf_owl/rotor_1/rotor_1_visual:
- {}
- rmf_owl/rotor_2:
- rmf_owl/rotor_2/rotor_2_collision:
- {}
- rmf_owl/rotor_2/rotor_2_visual:
- {}
- rmf_owl/rotor_3:
- rmf_owl/rotor_3/rotor_3_collision:
- {}
- rmf_owl/rotor_3/rotor_3_visual:
- {}
- Update Interval: 0
- Value: true
- - Class: rviz_common/Group
- Displays:
- - Class: rviz_default_plugins/Image
- Enabled: false
- Max Value: 1
- Median window: 5
- Min Value: 0
- Name: Front Left RGB
- Normalize Range: true
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: sensors/front_stereo/left/image_rect
- Value: false
- - Class: rviz_default_plugins/Image
- Enabled: false
- Max Value: 100
- Median window: 5
- Min Value: 0
- Name: Front Left Depth
- Normalize Range: false
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: sensors/front_stereo/left/depth
- Value: false
- - Alpha: 1
- Autocompute Intensity Bounds: true
- Autocompute Value Bounds:
- Max Value: 6.571824073791504
- Min Value: -0.5682187080383301
- Value: true
- Axis: Z
- Channel Name: intensity
- Class: rviz_default_plugins/PointCloud2
- Color: 170; 170; 255
- Color Transformer: FlatColor
- Decay Time: 0
- Enabled: false
- Invert Rainbow: false
- Max Color: 255; 255; 255
- Max Intensity: 4096
- Min Color: 0; 0; 0
- Min Intensity: 0
- Name: Lidar
- Position Transformer: XYZ
- Selectable: true
- Size (Pixels): 1
- Size (m): 0.009999999776482582
- Style: Points
- Topic:
- Depth: 5
- Durability Policy: Volatile
- Filter size: 10
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: sensors/ouster/point_cloud
- Use Fixed Frame: true
- Use rainbow: true
- Value: false
- - Angle Tolerance: 0
- Class: rviz_default_plugins/Odometry
- Covariance:
- Orientation:
- Alpha: 0.5
- Color: 255; 255; 127
- Color Style: Unique
- Frame: Local
- Offset: 1
- Scale: 1
- Value: true
- Position:
- Alpha: 0.30000001192092896
- Color: 204; 51; 204
- Scale: 1
- Value: true
- Value: true
- Enabled: false
- Keep: 1
- Name: Odometry
- Position Tolerance: 0
- Shape:
- Alpha: 1
- Axes Length: 1
- Axes Radius: 0.10000000149011612
- Color: 255; 25; 0
- Head Length: 0.30000001192092896
- Head Radius: 0.10000000149011612
- Shaft Length: 1
- Shaft Radius: 0.05000000074505806
- Value: Axes
- Topic:
- Depth: 5
- Durability Policy: Volatile
- Filter size: 10
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: odometry_conversion/odometry
- Value: false
- Enabled: true
- Name: Sensors
- - Class: rviz_common/Group
- Displays:
- - Class: rviz_default_plugins/Image
- Enabled: false
- Max Value: 1
- Median window: 5
- Min Value: 0
- Name: MACVO Disparity
- Normalize Range: true
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: /robot_1/macvo/disparity
- Value: false
- - Alpha: 1
- Autocompute Intensity Bounds: true
- Autocompute Value Bounds:
- Max Value: 10
- Min Value: -10
- Value: true
- Axis: Z
- Channel Name: intensity
- Class: rviz_default_plugins/PointCloud
- Color: 255; 255; 255
- Color Transformer: RGBF32
- Decay Time: 5
- Enabled: true
- Invert Rainbow: false
- Max Color: 255; 255; 255
- Max Intensity: 4096
- Min Color: 0; 0; 0
- Min Intensity: 0
- Name: MACVO PointCloud
- Position Transformer: XYZ
- Selectable: true
- Size (Pixels): 3
- Size (m): 0.009999999776482582
- Style: Flat Squares
- Topic:
- Depth: 5
- Durability Policy: Volatile
- Filter size: 10
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: /robot_1/macvo/point_cloud
- Use Fixed Frame: true
- Use rainbow: true
- Value: true
- - Angle Tolerance: 0.10000000149011612
- Class: rviz_default_plugins/Odometry
- Covariance:
- Orientation:
- Alpha: 0.5
- Color: 255; 255; 127
- Color Style: Unique
- Frame: Local
- Offset: 1
- Scale: 1
- Value: true
- Position:
- Alpha: 0.30000001192092896
- Color: 204; 51; 204
- Scale: 1
- Value: true
- Value: true
- Enabled: true
- Keep: 100
- Name: MACVO Odometry
- Position Tolerance: 0.10000000149011612
- Shape:
- Alpha: 1
- Axes Length: 1
- Axes Radius: 0.10000000149011612
- Color: 255; 25; 0
- Head Length: 0.30000001192092896
- Head Radius: 0.10000000149011612
- Shaft Length: 1
- Shaft Radius: 0.05000000074505806
- Value: Arrow
- Topic:
- Depth: 5
- Durability Policy: Volatile
- Filter size: 10
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: /robot_1/macvo/odometry
- Value: true
- Enabled: true
- Name: Perception
- - Class: rviz_common/Group
- Displays:
- - Class: rviz_common/Group
- Displays:
- - Class: rviz_default_plugins/Marker
- Enabled: false
- Name: Disparity Frustum
- Namespaces:
- {}
- Topic:
- Depth: 5
- Durability Policy: Volatile
- Filter size: 10
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: /robot_1/droan/frustum
- Value: false
- - Class: rviz_default_plugins/MarkerArray
- Enabled: false
- Name: Disparity Map Collision Checking
- Namespaces:
- {}
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: /robot_1/droan/disparity_map_debug
- Value: false
- - Class: rviz_default_plugins/MarkerArray
- Enabled: false
- Name: Disparity Graph Poses
- Namespaces:
- {}
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: /robot_1/droan/disparity_graph
- Value: false
- - Class: rviz_default_plugins/MarkerArray
- Enabled: true
- Name: Trimmed Global Plan for DROAN
- Namespaces:
- {}
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: droan/local_planner_global_plan_vis
- Value: true
- - Class: rviz_default_plugins/MarkerArray
- Enabled: false
- Name: ExpansionPoly
- Namespaces:
- {}
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: droan/expansion_poly
- Value: false
- - Alpha: 1
- Autocompute Intensity Bounds: true
- Autocompute Value Bounds:
- Max Value: 10
- Min Value: -10
- Value: true
- Axis: Z
- Channel Name: intensity
- Class: rviz_default_plugins/PointCloud2
- Color: 255; 255; 255
- Color Transformer: Intensity
- Decay Time: 0
- Enabled: true
- Invert Rainbow: false
- Max Color: 255; 255; 255
- Max Intensity: 220
- Min Color: 0; 0; 0
- Min Intensity: 120
- Name: Expansion Cloud
- Position Transformer: XYZ
- Selectable: true
- Size (Pixels): 3
- Size (m): 0.009999999776482582
- Style: Flat Squares
- Topic:
- Depth: 5
- Durability Policy: Volatile
- Filter size: 10
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: droan/expansion_cloud
- Use Fixed Frame: true
- Use rainbow: true
- Value: true
- - Class: rviz_default_plugins/MarkerArray
- Enabled: true
- Name: Traj Library
- Namespaces:
- {}
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: droan/trajectory_library_vis
- Value: true
- - Class: rviz_default_plugins/MarkerArray
- Enabled: true
- Name: Virtual Obstacles
- Namespaces:
- {}
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: droan/virtual_obstacles
- Value: true
- Enabled: true
- Name: DROAN
- - Class: rviz_common/Group
- Displays:
- - Class: rviz_default_plugins/MarkerArray
- Enabled: true
- Name: Traj Vis
- Namespaces:
- traj_controller: true
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: trajectory_controller/trajectory_vis
- Value: true
- - Class: rviz_default_plugins/MarkerArray
- Enabled: false
- Name: Traj Debug
- Namespaces:
- {}
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: trajectory_controller/trajectory_controller_debug_markers
- Value: false
- Enabled: true
- Name: Trajectory Controller
- Enabled: true
- Name: Local
- - Class: rviz_common/Group
- Displays:
- - Class: rviz_default_plugins/Marker
- Enabled: true
- Name: VDB Mapping Marker
- Namespaces:
- vdb_grid: true
- Topic:
- Depth: 5
- Durability Policy: Volatile
- Filter size: 10
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: /robot_1/exploration_planner/vdb_viz
- Value: true
- - Alpha: 1
- Buffer Length: 1
- Class: rviz_default_plugins/Path
- Color: 0; 255; 255
- Enabled: true
- Head Diameter: 0.30000001192092896
- Head Length: 0.20000000298023224
- Length: 0.30000001192092896
- Line Style: Billboards
- Line Width: 0.10000000149011612
- Name: Global Plan
- Offset:
- X: 0
- Y: 0
- Z: 0
- Pose Color: 255; 85; 255
- Pose Style: None
- Radius: 0.029999999329447746
- Shaft Diameter: 0.10000000149011612
- Shaft Length: 0.10000000149011612
- Topic:
- Depth: 5
- Durability Policy: Volatile
- Filter size: 10
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: /robot_1/global_plan
- Value: true
- Enabled: true
- Name: Global
- Enabled: true
- Global Options:
- Background Color: 48; 48; 48
- Fixed Frame: map
- Frame Rate: 30
- Name: root
- Tools:
- - Class: rviz_default_plugins/Interact
- Hide Inactive Objects: true
- - Class: rviz_default_plugins/MoveCamera
- - Class: rviz_default_plugins/Select
- - Class: rviz_default_plugins/FocusCamera
- - Class: rviz_default_plugins/Measure
- Line color: 128; 128; 0
- - Class: rviz_default_plugins/SetInitialPose
- Covariance x: 0.25
- Covariance y: 0.25
- Covariance yaw: 0.06853891909122467
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: /initialpose
- - Class: rviz_default_plugins/SetGoal
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: /goal_pose
- - Class: rviz_default_plugins/PublishPoint
- Single click: true
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: /clicked_point
- Transformation:
- Current:
- Class: rviz_default_plugins/TF
- Value: true
- Views:
- Current:
- Class: rviz_default_plugins/Orbit
- Distance: 15.553143501281738
- Enable Stereo Rendering:
- Stereo Eye Separation: 0.05999999865889549
- Stereo Focal Distance: 1
- Swap Stereo Eyes: false
- Value: false
- Focal Point:
- X: -0.2159808725118637
- Y: -0.7331764101982117
- Z: -1.5793094635009766
- Focal Shape Fixed Size: false
- Focal Shape Size: 0.05000000074505806
- Invert Z Axis: false
- Name: Current View
- Near Clip Distance: 0.009999999776482582
- Pitch: 0.5253989100456238
- Target Frame: base_link
- Value: Orbit (rviz)
- Yaw: 2.190396785736084
- Saved: ~
-Window Geometry:
- Displays:
- collapsed: false
- Front Left Depth:
- collapsed: false
- Front Left RGB:
- collapsed: false
- Height: 1016
- Hide Left Dock: false
- Hide Right Dock: false
- MACVO Disparity:
- collapsed: false
- QMainWindow State: 000000ff00000000fd0000000400000000000001e50000032efc020000000afb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003b0000032e000000c700fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d00610067006500000002eb000000c90000000000000000fb00000028004d004100430056004f00200049006d00610067006500200046006500610074007500720065007300000002ba000000ca000000000000000000000001000001f60000032efc0200000008fb00000016004c006500660074002000430061006d006500720061010000003b000001880000000000000000fb00000014004c006500660074002000440065007000740068010000003b0000016a0000000000000000fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000001c00460072006f006e00740020004c0065006600740020005200470042000000003b000001060000002800fffffffb0000002000460072006f006e00740020004c006500660074002000440065007000740068000000003b000001250000002800fffffffb0000001e004d004100430056004f0020004400690073007000610072006900740079000000003b0000032e0000002800fffffffb0000000a00560069006500770073000000025900000114000000a000fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000007380000006efc0100000002fb0000000800540069006d00650100000000000007380000025300fffffffb0000000800540069006d006501000000000000045000000000000000000000054d0000032e00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
- Selection:
- collapsed: false
- Time:
- collapsed: false
- Tool Properties:
- collapsed: false
- Views:
- collapsed: false
- Width: 1848
- X: 1272
- Y: 621
diff --git a/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_autonomy_launch.xml b/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_autonomy_launch.xml
deleted file mode 100644
index e9a133f38..000000000
--- a/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_autonomy_launch.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_behavior_launch.xml b/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_behavior_launch.xml
deleted file mode 100644
index cef093702..000000000
--- a/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_behavior_launch.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_domain_bridge.yaml b/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_domain_bridge.yaml
deleted file mode 100644
index 13ffbb91d..000000000
--- a/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_domain_bridge.yaml
+++ /dev/null
@@ -1,77 +0,0 @@
-name: my_domain_bridge
-
-topics:
- # Bridge "/foo/chatter" topic from doman ID 2 to domain ID 3
- # Automatically detect QoS settings and default to 'keep_last' history with depth 10
-
- /tf:
- type: tf2_msgs/msg/TFMessage
- from_domain: 0
- to_domain: 1
- qos:
- subscription: # on domain 0 side: match gz_parameter_bridge (RELIABLE)
- reliability: reliable
- durability: volatile
- history: keep_last
- depth: 100
- publisher: # on domain 1 side: match RViz TF listener (RELIABLE)
- reliability: reliable
- durability: volatile
- history: keep_last
- depth: 100
-
- /tf_static:
- type: tf2_msgs/msg/TFMessage
- from_domain: 0
- to_domain: 1
- qos:
- reliability: reliable
- durability: transient_local
- history: keep_last
- depth: 1
-
- /clock:
- type: rosgraph_msgs/msg/Clock
- from_domain: 0
- to_domain: 1
- qos:
- reliability: best_effort
- durability: volatile
- history: keep_last
- depth: 1
-
- /odom:
- from_domain: 0
- to_domain: 1
- type: nav_msgs/msg/Odometry
-
- /robot_1/sensors/ouster/point_cloud:
- from_domain: 0
- to_domain: 1
- type: sensor_msgs/msg/PointCloud2
-
- /robot_1/interface/cmd_velocity:
- from_domain: 1
- to_domain: 0
- type: geometry_msgs/msg/TwistStamped
-
- # /robot_1/odometry_conversion/odometry:
- # from_domain: 1
- # to_domain: 0
- # type: nav_msgs/msg/Odometry
-
- # /robot_1/behavior/behavior_tree_graphviz:
- # type: std_msgs/msg/String
- # from_domain: 1
- # to_domain: 0
-
-# GPS related topics -----------
- # /robot_1/interface/mavros/global_position/raw/fix:
- # type: sensor_msgs/msg/NavSatFix
- # from_domain: 1
- # to_domain: 0
-
- # /robot_1/interface/mavros/global_position/global:
- # type: sensor_msgs/msg/NavSatFix
- # from_domain: 1
- # to_domain: 0
diff --git a/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_global_launch.xml b/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_global_launch.xml
deleted file mode 100644
index ae5fed486..000000000
--- a/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_global_launch.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_interface_launch.xml b/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_interface_launch.xml
deleted file mode 100644
index f42a5d1fd..000000000
--- a/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_interface_launch.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_local_launch.xml b/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_local_launch.xml
deleted file mode 100644
index 93217a883..000000000
--- a/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_local_launch.xml
+++ /dev/null
@@ -1,100 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_static_transforms.launch.xml b/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_static_transforms.launch.xml
deleted file mode 100644
index b1946584a..000000000
--- a/robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_static_transforms.launch.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/robot/ros_ws/src/interface/interface_bringup/launch/interface.launch.py b/robot/ros_ws/src/interface/interface_bringup/launch/interface.launch.py
index 1b1636424..da0f28416 100644
--- a/robot/ros_ws/src/interface/interface_bringup/launch/interface.launch.py
+++ b/robot/ros_ws/src/interface/interface_bringup/launch/interface.launch.py
@@ -1,6 +1,11 @@
#!/usr/bin/env python3
"""ROS2 Python launch file for interface bringup.
+Launches MAVROS (via mavros_px4.launch.xml), the robot_interface node with the
+MAVROS plugin, the position setpoint publisher, and odometry conversion.
+Included by: stacks/*/launch entry files — the canonical interface bringup
+(wrapped by design until the platform-module refactor, RFC #380 Part 2).
+
Dynamically computes FCU URL and TGT_SYSTEM from environment variables:
OFFBOARD_PORT = OFFBOARD_BASE_PORT + ROS_DOMAIN_ID
ONBOARD_PORT = ONBOARD_BASE_PORT + ROS_DOMAIN_ID
@@ -122,7 +127,8 @@ def launch_setup(context, *args, **kwargs):
)
actions.append(odometry_conversion_node)
- # NOTE: drone_safety_monitor is now launched from behavior_bringup
+ # NOTE: drone_safety_monitor is launched by the stack entry files
+ # (drone_safety_monitor.launch.xml), not here
return actions
diff --git a/robot/ros_ws/src/interface/interface_bringup/launch/mavros_px4.launch.xml b/robot/ros_ws/src/interface/interface_bringup/launch/mavros_px4.launch.xml
index decb1ad11..4a2c64e90 100644
--- a/robot/ros_ws/src/interface/interface_bringup/launch/mavros_px4.launch.xml
+++ b/robot/ros_ws/src/interface/interface_bringup/launch/mavros_px4.launch.xml
@@ -1,23 +1,37 @@
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/robot/ros_ws/src/interface/mavros_interface/CMakeLists.txt b/robot/ros_ws/src/interface/mavros_interface/CMakeLists.txt
index c4605217d..f1a33872f 100644
--- a/robot/ros_ws/src/interface/mavros_interface/CMakeLists.txt
+++ b/robot/ros_ws/src/interface/mavros_interface/CMakeLists.txt
@@ -84,6 +84,4 @@ ament_export_targets(
export_${PROJECT_NAME}
)
-install(DIRECTORY launch DESTINATION share/${PROJECT_NAME})
-
ament_package()
diff --git a/robot/ros_ws/src/interface/mavros_interface/launch/README.md b/robot/ros_ws/src/interface/mavros_interface/launch/README.md
deleted file mode 100644
index 94ed10a02..000000000
--- a/robot/ros_ws/src/interface/mavros_interface/launch/README.md
+++ /dev/null
@@ -1,105 +0,0 @@
-# MAVROS Connection Polling Launch File
-
-This directory contains a Python launch file that continuously polls for a mavlink connection and launches `px4.launch` once the connection is established.
-
-## File Overview
-
-### `mavros_connection_poll.launch.py` (OpaqueFunction Approach)
-
-This implementation uses ROS2's `OpaqueFunction` to handle connection polling and dynamic launch actions.
-
-**Features:**
-- Comprehensive URL parsing (TCP, UDP, Serial)
-- Configurable polling intervals and timeouts
-- Built-in connection checking for different protocols
-- Thread-based polling to avoid blocking the launch process
-- Automatic MAVROS launch when connection is established
-
-**Usage:**
-```bash
-export MAVROS_FCU_URL="tcpin:localhost:4560"
-ros2 launch mavros_interface mavros_connection_poll.launch.py
-
-# With custom max wait time
-ros2 launch mavros_interface mavros_connection_poll.launch.py max_wait_time:=120.0
-
-# With multiple custom arguments
-ros2 launch mavros_interface mavros_connection_poll.launch.py \
- max_wait_time:=90.0 \
- polling_interval:=0.5
-```
-
-## Supported FCU URL Formats
-
-All implementations support the following FCU URL formats:
-
-### TCP Connections
-- `tcpin:localhost:4560` - TCP input connection
-- `tcp://localhost:4560` - Standard TCP URL format
-
-### UDP Connections
-- `udp://localhost:14540` - Simple UDP connection
-- `udp://:14540@172.31.0.200:14580` - UDP with local and remote ports
-
-### Serial Connections
-- `/dev/ttyTHS4:115200` - Serial device connection
-- `/dev/ttyUSB0:57600` - USB serial connection
-
-## Environment Variables
-
-### Optional
-- `ROS_DOMAIN_ID` - Used for robot identification
-- `ROBOT_NAME` - Used for namespacing
-
-## Launch Arguments
-
-- `polling_interval` (default: 1.0s) - How often to check connection
-- `max_wait_time` (default: 60s) - Maximum time in seconds to wait for mavlink connection
-- `MAVROS_FCU_URL` - The FCU connection URL (see formats above)
-
-## Integration with AirStack
-
-This launch file is designed to work with the AirStack interface system:
-
-1. **Set the FCU URL**: Configure `MAVROS_FCU_URL` in your robot's `.bashrc`
-2. **Launch Interface**: Use this launch file instead of directly launching MAVROS
-3. **Connection Handling**: The system will wait for the mavlink endpoint to be available
-4. **Automatic Launch**: MAVROS will be launched automatically when connection is established
-
-## Example Usage Scenarios
-
-### Scenario 1: Pegasus Isaac Sim Integration
-```bash
-# Robot 0
-export ROS_DOMAIN_ID=0
-ros2 launch mavros_interface mavros_connection_poll.launch.py fcu_url:="tcpin:localhost:4560"
-
-# Robot 1
-export ROS_DOMAIN_ID=1
-export MAVROS_FCU_URL="tcpin:localhost:4561"
-ros2 launch mavros_interface mavros_connection_poll.launch.py
-```
-
-### Scenario 2: Real Hardware
-```bash
-ros2 launch mavros_interface mavros_connection_poll.launch.py connection_timeout:=30.0 fcu_url:="/dev/ttyTHS4:115200"
-```
-
-### Scenario 3: SITL Development
-```bash
-ros2 launch mavros_interface mavros_connection_poll.launch.py fcu_url:="$MAVROS_FCU_URL"
-```
-
-## Troubleshooting
-
-### Connection Issues
-1. Verify `MAVROS_FCU_URL` is set correctly
-2. Check that the target endpoint is reachable
-3. For serial connections, verify device permissions
-4. Increase `connection_timeout` for slow-starting systems
-
-### Launch Issues
-1. Ensure MAVROS package is installed
-2. Check ROS2 workspace is sourced
-3. Verify launch file permissions
-4. Review ROS2 logs for detailed error messages
diff --git a/robot/ros_ws/src/interface/mavros_interface/launch/mavros_connection_poll.launch.py b/robot/ros_ws/src/interface/mavros_interface/launch/mavros_connection_poll.launch.py
deleted file mode 100644
index a912efb00..000000000
--- a/robot/ros_ws/src/interface/mavros_interface/launch/mavros_connection_poll.launch.py
+++ /dev/null
@@ -1,145 +0,0 @@
-#!/usr/bin/env python3
-
-"""
-ROS2 Python Launch file for MAVROS interface with connection polling.
-This launch file continuously polls for a mavlink connection using pymavlink
-to check for heartbeat messages and launches px4.launch once the
-connection is established.
-"""
-
-import time
-from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, OpaqueFunction
-from launch.substitutions import LaunchConfiguration
-from launch_ros.actions import PushRosNamespace
-from launch_ros.substitutions import FindPackageShare
-from launch.launch_description_sources import AnyLaunchDescriptionSource
-
-from pymavlink import mavutil
-
-
-def wait_for_connection_and_launch(context):
- """Wait for mavlink connection and then launch px4.launch."""
-
- # Get the MAVROS FCU URL from launch configuration
- fcu_url = LaunchConfiguration('fcu_url').perform(context)
-
- if not fcu_url:
- print("ERROR: fcu_url launch argument is not set!")
- return []
-
- # Get launch configurations from context
- heartbeat_timeout = float(LaunchConfiguration('heartbeat_timeout').perform(context))
-
- mavlink_url = fcu_url.split("@")[0].replace("udp://", "udpin:") + fcu_url.split("@")[1] if "@" in fcu_url else ""
-
- print(f"Starting mavlink heartbeat polling for: {mavlink_url}")
- if heartbeat_timeout < 0:
- print(f"Heartbeat timeout: infinite (will wait indefinitely)")
- else:
- print(f"Heartbeat timeout: {heartbeat_timeout} seconds")
-
- # Poll for heartbeat with single timeout loop
- start_time = time.time()
- print(f"Polling for mavlink heartbeat from: {mavlink_url}")
-
- connection = None
- tgt_system, tgt_component = None, None
-
-
- while heartbeat_timeout < 0 or (time.time() - start_time) < heartbeat_timeout:
- try:
- # Create connection once
- if connection is None:
- connection = mavutil.mavlink_connection(mavlink_url)
-
- msg = connection.recv_match(type='HEARTBEAT', blocking=False, timeout=1)
-
- if msg:
- # Check the message type to identify a heartbeat
- if msg.get_type() == 'HEARTBEAT':
- print("Heartbeat message received!")
- print(f"MAV_TYPE: {msg.type}, MAV_AUTOPILOT: {msg.autopilot}")
-
- print("Requesting autopilot version...")
- connection.mav.command_long_send(
- connection.target_system, # Target system
- connection.target_component, # Target component
- mavutil.mavlink.MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES, # Command ID
- 0, # Confirmation
- 0, # Parameter 1
- 0, # Parameter 2
- 0, # Parameter 3
- 0, # Parameter 4
- 0, # Parameter 5
- 0, # Parameter 6
- 0 # Parameter 7
- )
-
- # wait for a response and print it
- msg = connection.recv_match(type='AUTOPILOT_VERSION', blocking=True, timeout=10)
- if msg:
- print("Autopilot version message received!")
- print(msg)
- else:
- print("No autopilot version message received within timeout.")
- exit(1)
-
- tgt_system = connection.target_system
- tgt_component = connection.target_component
- print(f"Target System: {tgt_system}, Target Component: {tgt_component}")
-
- connection.close()
- break
- print(f"Waiting for mavlink heartbeat from: {mavlink_url}")
- except Exception as e:
- print(f"Waiting for connection {time.time() - start_time:.1f}s")
- time.sleep(1)
-
- print(f"Heartbeat received! Launching MAVROS px4.launch on {fcu_url}")
-
- # Create the MAVROS launch action
- mavros_launch = [
- PushRosNamespace('interface'),
- IncludeLaunchDescription(
- AnyLaunchDescriptionSource([
- FindPackageShare('mavros'), '/launch/px4.launch'
- ]),
- launch_arguments={
- 'fcu_url': fcu_url,
- # "tgt_system": str(tgt_system),
- # "tgt_component": str(tgt_component)
- }.items()
- )
- ]
-
- return mavros_launch
-
-
-def generate_launch_description():
- """Generate the launch description."""
-
- # Declare launch arguments
- fcu_url_arg = DeclareLaunchArgument(
- 'fcu_url',
- default_value='',
- description='FCU connection URL (e.g., udp://localhost:14540, tcp://localhost:5760, /dev/ttyUSB0:57600)'
- )
-
- heartbeat_timeout_arg = DeclareLaunchArgument(
- 'heartbeat_timeout',
- default_value='-1.0',
- description='Timeout in seconds to wait for mavlink heartbeat (use negative value for infinite waiting)'
- )
-
- # Use OpaqueFunction to handle the connection polling and launch
- connection_and_launch = OpaqueFunction(function=wait_for_connection_and_launch)
-
- return LaunchDescription([
- # Launch arguments
- fcu_url_arg,
- heartbeat_timeout_arg,
-
- # Connection polling and MAVROS launch
- connection_and_launch,
- ])
diff --git a/robot/ros_ws/src/interface/px4_interface/CMakeLists.txt b/robot/ros_ws/src/interface/px4_interface/CMakeLists.txt
index 4567d39a7..b153f0d4b 100644
--- a/robot/ros_ws/src/interface/px4_interface/CMakeLists.txt
+++ b/robot/ros_ws/src/interface/px4_interface/CMakeLists.txt
@@ -50,8 +50,6 @@ install(
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin)
-install(DIRECTORY launch DESTINATION share/${PROJECT_NAME})
-
# ── Export ───────────────────────────────────────────────────────────────────
ament_export_include_directories(include)
ament_export_libraries(px4_interface)
diff --git a/robot/ros_ws/src/interface/px4_interface/launch/px4_interface.launch.xml b/robot/ros_ws/src/interface/px4_interface/launch/px4_interface.launch.xml
deleted file mode 100644
index a61891088..000000000
--- a/robot/ros_ws/src/interface/px4_interface/launch/px4_interface.launch.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/robot/ros_ws/src/interface/robot_interface/CMakeLists.txt b/robot/ros_ws/src/interface/robot_interface/CMakeLists.txt
index 5920ac965..d2c14d4b6 100644
--- a/robot/ros_ws/src/interface/robot_interface/CMakeLists.txt
+++ b/robot/ros_ws/src/interface/robot_interface/CMakeLists.txt
@@ -129,8 +129,6 @@ if(BUILD_TESTING)
endif()
# Install files.
-install(DIRECTORY launch DESTINATION share/${PROJECT_NAME})
-
# install(DIRECTORY rviz DESTINATION share/${PROJECT_NAME})
# install(DIRECTORY config DESTINATION share/${PROJECT_NAME})
# install(DIRECTORY params DESTINATION share/${PROJECT_NAME})
diff --git a/robot/ros_ws/src/interface/robot_interface/launch/odometry_conversion.xml b/robot/ros_ws/src/interface/robot_interface/launch/odometry_conversion.xml
deleted file mode 100644
index 384160a47..000000000
--- a/robot/ros_ws/src/interface/robot_interface/launch/odometry_conversion.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/robot/ros_ws/src/local/controls/pid_controller/launch/pid_controller.launch.xml b/robot/ros_ws/src/local/controls/pid_controller/launch/pid_controller.launch.xml
index e971ca3aa..f826b6566 100644
--- a/robot/ros_ws/src/local/controls/pid_controller/launch/pid_controller.launch.xml
+++ b/robot/ros_ws/src/local/controls/pid_controller/launch/pid_controller.launch.xml
@@ -18,7 +18,7 @@
description="Input robot odometry (nav_msgs/Odometry)" />
+ description="Input trajectory-controller tracking point setpoint (airstack_msgs/Odometry)" />
diff --git a/robot/ros_ws/src/local/local_bringup/CMakeLists.txt b/robot/ros_ws/src/local/local_bringup/CMakeLists.txt
deleted file mode 100644
index 19c86fbba..000000000
--- a/robot/ros_ws/src/local/local_bringup/CMakeLists.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-cmake_minimum_required(VERSION 3.8)
-project(local_bringup)
-
-if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
- add_compile_options(-Wall -Wextra -Wpedantic)
-endif()
-
-# find dependencies
-find_package(ament_cmake REQUIRED)
-# uncomment the following section in order to fill in
-# further dependencies manually.
-# find_package( REQUIRED)
-
-if(BUILD_TESTING)
- find_package(ament_lint_auto REQUIRED)
- # the following line skips the linter which checks for copyrights
- # comment the line when a copyright and license is added to all source files
- set(ament_cmake_copyright_FOUND TRUE)
- # the following line skips cpplint (only works in a git repo)
- # comment the line when this package is in a git repo and when
- # a copyright and license is added to all source files
- set(ament_cmake_cpplint_FOUND TRUE)
- ament_lint_auto_find_test_dependencies()
-endif()
-
-# Install files.
-install(DIRECTORY launch DESTINATION share/${PROJECT_NAME})
-install(DIRECTORY rviz DESTINATION share/${PROJECT_NAME})
-# install(DIRECTORY config DESTINATION share/${PROJECT_NAME})
-# install(DIRECTORY params DESTINATION share/${PROJECT_NAME})
-
-ament_package()
diff --git a/robot/ros_ws/src/local/local_bringup/LICENSE b/robot/ros_ws/src/local/local_bringup/LICENSE
deleted file mode 100644
index d64569567..000000000
--- a/robot/ros_ws/src/local/local_bringup/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/robot/ros_ws/src/local/local_bringup/launch/local.launch.xml b/robot/ros_ws/src/local/local_bringup/launch/local.launch.xml
deleted file mode 100644
index fd46b9282..000000000
--- a/robot/ros_ws/src/local/local_bringup/launch/local.launch.xml
+++ /dev/null
@@ -1,216 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ?>
-
-
-
-
-
-
-
-
-
-
-
-
- ?>
-
-
-
\ No newline at end of file
diff --git a/robot/ros_ws/src/local/local_bringup/package.xml b/robot/ros_ws/src/local/local_bringup/package.xml
deleted file mode 100644
index 4075599fa..000000000
--- a/robot/ros_ws/src/local/local_bringup/package.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
- local_bringup
- 0.0.0
- TODO: Package description
- andrew
- Apache-2.0
-
- ament_cmake
-
- ament_lint_auto
- ament_lint_common
-
-
- ament_cmake
-
-
diff --git a/robot/ros_ws/src/local/local_bringup/rviz/droan.rviz b/robot/ros_ws/src/local/local_bringup/rviz/droan.rviz
deleted file mode 100644
index e8754fbc0..000000000
--- a/robot/ros_ws/src/local/local_bringup/rviz/droan.rviz
+++ /dev/null
@@ -1,675 +0,0 @@
-Panels:
- - Class: rviz_common/Displays
- Help Height: 78
- Name: Displays
- Property Tree Widget:
- Expanded:
- - /TF1/Frames1
- - /Sensors1
- - /Local1
- - /Local1/DROAN1
- - /Local1/Trajectory Controller1
- - /Global1
- Splitter Ratio: 0.590062141418457
- Tree Height: 1085
- - Class: rviz_common/Selection
- Name: Selection
- - Class: rviz_common/Tool Properties
- Expanded:
- - /2D Goal Pose1
- - /Publish Point1
- Name: Tool Properties
- Splitter Ratio: 0.5886790156364441
- - Class: rviz_common/Views
- Expanded:
- - /Current View1
- Name: Views
- Splitter Ratio: 0.5
- - Class: rviz_common/Time
- Experimental: false
- Name: Time
- SyncMode: 0
- SyncSource: Expansion Cloud
-Visualization Manager:
- Class: ""
- Displays:
- - Alpha: 0.5
- Cell Size: 1
- Class: rviz_default_plugins/Grid
- Color: 160; 160; 164
- Enabled: true
- Line Style:
- Line Width: 0.029999999329447746
- Value: Lines
- Name: Grid
- Normal Cell Count: 0
- Offset:
- X: 0
- Y: 0
- Z: 0
- Plane: XY
- Plane Cell Count: 100
- Reference Frame:
- Value: true
- - Class: rviz_default_plugins/TF
- Enabled: true
- Frame Timeout: 15
- Frames:
- All Enabled: false
- American_Beech:
- Value: true
- Plane:
- Value: true
- base_link:
- Value: true
- base_link_frd:
- Value: false
- base_link_stabilized:
- Value: false
- front_stereo:
- Value: false
- left_camera:
- Value: true
- look_ahead_point:
- Value: false
- look_ahead_point_stabilized:
- Value: false
- map:
- Value: true
- map_FLU:
- Value: false
- map_ned:
- Value: false
- odom:
- Value: false
- odom_ned:
- Value: false
- ouster:
- Value: false
- right_camera:
- Value: true
- tracking_point:
- Value: true
- tracking_point_stabilized:
- Value: false
- world:
- Value: false
- Marker Scale: 1
- Name: TF
- Show Arrows: true
- Show Axes: true
- Show Names: true
- Tree:
- world:
- American_Beech:
- {}
- Plane:
- {}
- map_FLU:
- map:
- base_link:
- base_link_frd:
- {}
- front_stereo:
- left_camera:
- {}
- right_camera:
- {}
- ouster:
- {}
- base_link_stabilized:
- {}
- look_ahead_point:
- {}
- look_ahead_point_stabilized:
- {}
- map_ned:
- {}
- tracking_point:
- {}
- tracking_point_stabilized:
- {}
- Update Interval: 0
- Value: true
- - Class: rviz_common/Group
- Displays:
- - Class: rviz_default_plugins/Image
- Enabled: true
- Max Value: 1
- Median window: 5
- Min Value: 0
- Name: Front Left RGB
- Normalize Range: true
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: sensors/front_stereo/left/image_rect
- Value: true
- - Class: rviz_default_plugins/Image
- Enabled: true
- Max Value: 100
- Median window: 5
- Min Value: 0
- Name: Front Left Depth
- Normalize Range: false
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: sensors/front_stereo/left/depth
- Value: true
- - Alpha: 1
- Autocompute Intensity Bounds: true
- Autocompute Value Bounds:
- Max Value: 6.571824073791504
- Min Value: -0.5682187080383301
- Value: true
- Axis: Z
- Channel Name: intensity
- Class: rviz_default_plugins/PointCloud2
- Color: 170; 170; 255
- Color Transformer: FlatColor
- Decay Time: 0
- Enabled: false
- Invert Rainbow: false
- Max Color: 255; 255; 255
- Max Intensity: 4096
- Min Color: 0; 0; 0
- Min Intensity: 0
- Name: Lidar
- Position Transformer: XYZ
- Selectable: true
- Size (Pixels): 1
- Size (m): 0.009999999776482582
- Style: Points
- Topic:
- Depth: 5
- Durability Policy: Volatile
- Filter size: 10
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: sensors/ouster/point_cloud
- Use Fixed Frame: true
- Use rainbow: true
- Value: false
- - Angle Tolerance: 0
- Class: rviz_default_plugins/Odometry
- Covariance:
- Orientation:
- Alpha: 0.5
- Color: 255; 255; 127
- Color Style: Unique
- Frame: Local
- Offset: 1
- Scale: 1
- Value: true
- Position:
- Alpha: 0.30000001192092896
- Color: 204; 51; 204
- Scale: 1
- Value: true
- Value: true
- Enabled: false
- Keep: 1
- Name: Odometry
- Position Tolerance: 0
- Shape:
- Alpha: 1
- Axes Length: 1
- Axes Radius: 0.10000000149011612
- Color: 255; 25; 0
- Head Length: 0.30000001192092896
- Head Radius: 0.10000000149011612
- Shaft Length: 1
- Shaft Radius: 0.05000000074505806
- Value: Axes
- Topic:
- Depth: 5
- Durability Policy: Volatile
- Filter size: 10
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: odometry_conversion/odometry
- Value: false
- Enabled: true
- Name: Sensors
- - Class: rviz_common/Group
- Displays:
- - Class: rviz_common/Group
- Displays:
- - Class: rviz_default_plugins/Marker
- Enabled: true
- Name: Disparity Frustum
- Namespaces:
- frustum: true
- Topic:
- Depth: 5
- Durability Policy: Volatile
- Filter size: 10
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: /robot_1/droan/frustum
- Value: true
- - Class: rviz_default_plugins/MarkerArray
- Enabled: false
- Name: Disparity Map Collision Checking
- Namespaces:
- {}
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: /robot_1/droan/disparity_map_debug
- Value: false
- - Class: rviz_default_plugins/MarkerArray
- Enabled: false
- Name: Disparity Graph Poses
- Namespaces:
- {}
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: /robot_1/droan/disparity_graph
- Value: false
- - Class: rviz_default_plugins/MarkerArray
- Enabled: true
- Name: Trimmed Global Plan for DROAN
- Namespaces:
- global_plan: true
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: droan/local_planner_global_plan_vis
- Value: true
- - Class: rviz_default_plugins/MarkerArray
- Enabled: false
- Name: ExpansionPoly
- Namespaces:
- {}
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: droan/expansion_poly
- Value: false
- - Alpha: 1
- Autocompute Intensity Bounds: true
- Autocompute Value Bounds:
- Max Value: 10
- Min Value: -10
- Value: true
- Axis: Z
- Channel Name: intensity
- Class: rviz_default_plugins/PointCloud2
- Color: 255; 255; 255
- Color Transformer: Intensity
- Decay Time: 0
- Enabled: true
- Invert Rainbow: false
- Max Color: 255; 255; 255
- Max Intensity: 220
- Min Color: 0; 0; 0
- Min Intensity: 120
- Name: Expansion Cloud
- Position Transformer: XYZ
- Selectable: true
- Size (Pixels): 3
- Size (m): 0.009999999776482582
- Style: Flat Squares
- Topic:
- Depth: 5
- Durability Policy: Volatile
- Filter size: 10
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: droan/expansion_cloud
- Use Fixed Frame: true
- Use rainbow: true
- Value: true
- - Class: rviz_default_plugins/MarkerArray
- Enabled: true
- Name: Traj Library
- Namespaces:
- trajectory_0: true
- trajectory_1: true
- trajectory_10: true
- trajectory_100: true
- trajectory_101: true
- trajectory_102: true
- trajectory_103: true
- trajectory_104: true
- trajectory_105: true
- trajectory_106: true
- trajectory_107: true
- trajectory_108: true
- trajectory_109: true
- trajectory_11: true
- trajectory_110: true
- trajectory_111: true
- trajectory_112: true
- trajectory_113: true
- trajectory_114: true
- trajectory_115: true
- trajectory_116: true
- trajectory_117: true
- trajectory_118: true
- trajectory_119: true
- trajectory_12: true
- trajectory_120: true
- trajectory_121: true
- trajectory_122: true
- trajectory_123: true
- trajectory_124: true
- trajectory_125: true
- trajectory_126: true
- trajectory_127: true
- trajectory_128: true
- trajectory_129: true
- trajectory_13: true
- trajectory_130: true
- trajectory_131: true
- trajectory_132: true
- trajectory_133: true
- trajectory_134: true
- trajectory_135: true
- trajectory_136: true
- trajectory_137: true
- trajectory_138: true
- trajectory_139: true
- trajectory_14: true
- trajectory_140: true
- trajectory_141: true
- trajectory_142: true
- trajectory_143: true
- trajectory_144: true
- trajectory_145: true
- trajectory_146: true
- trajectory_147: true
- trajectory_148: true
- trajectory_149: true
- trajectory_15: true
- trajectory_150: true
- trajectory_151: true
- trajectory_152: true
- trajectory_153: true
- trajectory_154: true
- trajectory_155: true
- trajectory_156: true
- trajectory_157: true
- trajectory_158: true
- trajectory_159: true
- trajectory_16: true
- trajectory_160: true
- trajectory_161: true
- trajectory_17: true
- trajectory_18: true
- trajectory_19: true
- trajectory_2: true
- trajectory_20: true
- trajectory_21: true
- trajectory_22: true
- trajectory_23: true
- trajectory_24: true
- trajectory_25: true
- trajectory_26: true
- trajectory_27: true
- trajectory_28: true
- trajectory_29: true
- trajectory_3: true
- trajectory_30: true
- trajectory_31: true
- trajectory_32: true
- trajectory_33: true
- trajectory_34: true
- trajectory_35: true
- trajectory_36: true
- trajectory_37: true
- trajectory_38: true
- trajectory_39: true
- trajectory_4: true
- trajectory_40: true
- trajectory_41: true
- trajectory_42: true
- trajectory_43: true
- trajectory_44: true
- trajectory_45: true
- trajectory_46: true
- trajectory_47: true
- trajectory_48: true
- trajectory_49: true
- trajectory_5: true
- trajectory_50: true
- trajectory_51: true
- trajectory_52: true
- trajectory_53: true
- trajectory_54: true
- trajectory_55: true
- trajectory_56: true
- trajectory_57: true
- trajectory_58: true
- trajectory_59: true
- trajectory_6: true
- trajectory_60: true
- trajectory_61: true
- trajectory_62: true
- trajectory_63: true
- trajectory_64: true
- trajectory_65: true
- trajectory_66: true
- trajectory_67: true
- trajectory_68: true
- trajectory_69: true
- trajectory_7: true
- trajectory_70: true
- trajectory_71: true
- trajectory_72: true
- trajectory_73: true
- trajectory_74: true
- trajectory_75: true
- trajectory_76: true
- trajectory_77: true
- trajectory_78: true
- trajectory_79: true
- trajectory_8: true
- trajectory_80: true
- trajectory_81: true
- trajectory_82: true
- trajectory_83: true
- trajectory_84: true
- trajectory_85: true
- trajectory_86: true
- trajectory_87: true
- trajectory_88: true
- trajectory_89: true
- trajectory_9: true
- trajectory_90: true
- trajectory_91: true
- trajectory_92: true
- trajectory_93: true
- trajectory_94: true
- trajectory_95: true
- trajectory_96: true
- trajectory_97: true
- trajectory_98: true
- trajectory_99: true
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: droan/trajectory_library_vis
- Value: true
- Enabled: true
- Name: DROAN
- - Class: rviz_common/Group
- Displays:
- - Class: rviz_default_plugins/MarkerArray
- Enabled: true
- Name: Traj Vis
- Namespaces:
- traj_controller: true
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: trajectory_controller/trajectory_vis
- Value: true
- - Class: rviz_default_plugins/MarkerArray
- Enabled: false
- Name: Traj Debug
- Namespaces:
- {}
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: trajectory_controller/trajectory_controller_debug_markers
- Value: false
- Enabled: true
- Name: Trajectory Controller
- Enabled: true
- Name: Local
- - Class: rviz_common/Group
- Displays:
- - Class: rviz_default_plugins/Marker
- Enabled: false
- Name: VDB Mapping Marker
- Namespaces:
- {}
- Topic:
- Depth: 5
- Durability Policy: Volatile
- Filter size: 10
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: vdb_mapping/vdb_map_visualization
- Value: false
- - Alpha: 1
- Buffer Length: 1
- Class: rviz_default_plugins/Path
- Color: 0; 255; 255
- Enabled: true
- Head Diameter: 0.30000001192092896
- Head Length: 0.20000000298023224
- Length: 0.30000001192092896
- Line Style: Billboards
- Line Width: 0.10000000149011612
- Name: Global Plan
- Offset:
- X: 0
- Y: 0
- Z: 0
- Pose Color: 255; 85; 255
- Pose Style: None
- Radius: 0.029999999329447746
- Shaft Diameter: 0.10000000149011612
- Shaft Length: 0.10000000149011612
- Topic:
- Depth: 5
- Durability Policy: Volatile
- Filter size: 10
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: /robot_1/global_plan
- Value: true
- Enabled: true
- Name: Global
- Enabled: true
- Global Options:
- Background Color: 48; 48; 48
- Fixed Frame: world
- Frame Rate: 30
- Name: root
- Tools:
- - Class: rviz_default_plugins/Interact
- Hide Inactive Objects: true
- - Class: rviz_default_plugins/MoveCamera
- - Class: rviz_default_plugins/Select
- - Class: rviz_default_plugins/FocusCamera
- - Class: rviz_default_plugins/Measure
- Line color: 128; 128; 0
- - Class: rviz_default_plugins/SetInitialPose
- Covariance x: 0.25
- Covariance y: 0.25
- Covariance yaw: 0.06853891909122467
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: /initialpose
- - Class: rviz_default_plugins/SetGoal
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: /goal_pose
- - Class: rviz_default_plugins/PublishPoint
- Single click: true
- Topic:
- Depth: 5
- Durability Policy: Volatile
- History Policy: Keep Last
- Reliability Policy: Reliable
- Value: /clicked_point
- Transformation:
- Current:
- Class: rviz_default_plugins/TF
- Value: true
- Views:
- Current:
- Class: rviz_default_plugins/Orbit
- Distance: 8.18502426147461
- Enable Stereo Rendering:
- Stereo Eye Separation: 0.05999999865889549
- Stereo Focal Distance: 1
- Swap Stereo Eyes: false
- Value: false
- Focal Point:
- X: 3.3486995697021484
- Y: -0.9512473344802856
- Z: 1.4642823934555054
- Focal Shape Fixed Size: false
- Focal Shape Size: 0.05000000074505806
- Invert Z Axis: false
- Name: Current View
- Near Clip Distance: 0.009999999776482582
- Pitch: 0.560396134853363
- Target Frame:
- Value: Orbit (rviz)
- Yaw: 2.143571615219116
- Saved: ~
-Window Geometry:
- Displays:
- collapsed: false
- Front Left Depth:
- collapsed: false
- Front Left RGB:
- collapsed: false
- Height: 1376
- Hide Left Dock: false
- Hide Right Dock: false
- QMainWindow State: 000000ff00000000fd0000000400000000000001e5000004c6fc0200000009fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003b000004c6000000c700fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d00610067006500000002eb000000c9000000000000000000000001000001f6000004c6fc0200000007fb00000016004c006500660074002000430061006d006500720061010000003b000001880000000000000000fb00000014004c006500660074002000440065007000740068010000003b0000016a0000000000000000fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000001c00460072006f006e00740020004c0065006600740020005200470042010000003b0000020e0000002800fffffffb0000002000460072006f006e00740020004c006500660074002000440065007000740068010000024f000002b20000002800fffffffb0000000a0056006900650077007300000000fd000001a8000000a000fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000009ba0000003efc0100000002fb0000000800540069006d00650100000000000009ba0000025300fffffffb0000000800540069006d00650100000000000004500000000000000000000005d3000004c600000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
- Selection:
- collapsed: false
- Time:
- collapsed: false
- Tool Properties:
- collapsed: false
- Views:
- collapsed: false
- Width: 2490
- X: 1990
- Y: 27
diff --git a/robot/ros_ws/src/local/planners/droan_gl/launch/droan_gl.launch.xml b/robot/ros_ws/src/local/planners/droan_gl/launch/droan_gl.launch.xml
index 6cb7d8f01..24855ea84 100644
--- a/robot/ros_ws/src/local/planners/droan_gl/launch/droan_gl.launch.xml
+++ b/robot/ros_ws/src/local/planners/droan_gl/launch/droan_gl.launch.xml
@@ -25,10 +25,10 @@
description="Input global waypoint path (nav_msgs/Path)" />
+ description="Input trajectory-controller look-ahead point (airstack_msgs/Odometry)" />
+ description="Input trajectory-controller tracking point (airstack_msgs/Odometry)" />
diff --git a/robot/ros_ws/src/local/planners/droan_local_planner/launch/droan_local_planner.launch.xml b/robot/ros_ws/src/local/planners/droan_local_planner/launch/droan_local_planner.launch.xml
index c9f1d4633..5ba565595 100644
--- a/robot/ros_ws/src/local/planners/droan_local_planner/launch/droan_local_planner.launch.xml
+++ b/robot/ros_ws/src/local/planners/droan_local_planner/launch/droan_local_planner.launch.xml
@@ -22,10 +22,10 @@
description="Input global waypoint path (nav_msgs/Path)" />
+ description="Input trajectory-controller look-ahead point (airstack_msgs/Odometry)" />
+ description="Input trajectory-controller tracking point (airstack_msgs/Odometry)" />
diff --git a/robot/ros_ws/src/local/planners/takeoff_landing_planner/launch/takeoff_landing_planner.launch.xml b/robot/ros_ws/src/local/planners/takeoff_landing_planner/launch/takeoff_landing_planner.launch.xml
index 60d6967c4..7a76126ef 100644
--- a/robot/ros_ws/src/local/planners/takeoff_landing_planner/launch/takeoff_landing_planner.launch.xml
+++ b/robot/ros_ws/src/local/planners/takeoff_landing_planner/launch/takeoff_landing_planner.launch.xml
@@ -24,7 +24,7 @@
description="Output trajectory-controller mode command" />
+ description="Input trajectory-controller tracking point (airstack_msgs/Odometry)" />
diff --git a/robot/ros_ws/src/perception/perception_bringup/launch/perception.launch.xml b/robot/ros_ws/src/perception/perception_bringup/launch/perception.launch.xml
deleted file mode 100644
index 374206c3d..000000000
--- a/robot/ros_ws/src/perception/perception_bringup/launch/perception.launch.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/robot/ros_ws/src/sensors/camera_param_server/launch/camera_param_server.launch.xml b/robot/ros_ws/src/sensors/camera_param_server/launch/camera_param_server.launch.xml
index 1347318d9..2f61cfe62 100644
--- a/robot/ros_ws/src/sensors/camera_param_server/launch/camera_param_server.launch.xml
+++ b/robot/ros_ws/src/sensors/camera_param_server/launch/camera_param_server.launch.xml
@@ -1,3 +1,7 @@
+
diff --git a/robot/ros_ws/src/sensors/lidar_point_cloud_filter/README.md b/robot/ros_ws/src/sensors/lidar_point_cloud_filter/README.md
index 44449f50f..1521b55fa 100644
--- a/robot/ros_ws/src/sensors/lidar_point_cloud_filter/README.md
+++ b/robot/ros_ws/src/sensors/lidar_point_cloud_filter/README.md
@@ -33,7 +33,7 @@ Defaults are in `config/lidar_point_cloud_filter.yaml`. `$(env ROBOT_NAME)` is e
ros2 launch lidar_point_cloud_filter lidar_point_cloud_filter.launch.xml
```
-Included from `sensors_bringup` under the robot and `sensors` namespaces. Defaults use **`sensors/ouster/point_cloud_raw` → `sensors/ouster/point_cloud`** to match Pegasus / Isaac and `vdb_params`. For RTX-only topic names, override `input_topic` and `output_topic` (for example under `sensors/lidar/...`).
+Included from the stack entry files (stacks/*/launch) under the robot and `sensors` namespaces. Defaults use **`sensors/ouster/point_cloud_raw` → `sensors/ouster/point_cloud`** to match Pegasus / Isaac and `vdb_params`. For RTX-only topic names, override `input_topic` and `output_topic` (for example under `sensors/lidar/...`).
## System tests (`sensors` mark)
diff --git a/robot/ros_ws/src/sensors/sensors_bringup/CMakeLists.txt b/robot/ros_ws/src/sensors/sensors_bringup/CMakeLists.txt
deleted file mode 100644
index 785bb6402..000000000
--- a/robot/ros_ws/src/sensors/sensors_bringup/CMakeLists.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-cmake_minimum_required(VERSION 3.8)
-project(sensors_bringup)
-
-if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
- add_compile_options(-Wall -Wextra -Wpedantic)
-endif()
-
-# find dependencies
-find_package(ament_cmake REQUIRED)
-# uncomment the following section in order to fill in
-# further dependencies manually.
-# find_package( REQUIRED)
-
-if(BUILD_TESTING)
- find_package(ament_lint_auto REQUIRED)
- # the following line skips the linter which checks for copyrights
- # comment the line when a copyright and license is added to all source files
- set(ament_cmake_copyright_FOUND TRUE)
- # the following line skips cpplint (only works in a git repo)
- # comment the line when this package is in a git repo and when
- # a copyright and license is added to all source files
- set(ament_cmake_cpplint_FOUND TRUE)
- ament_lint_auto_find_test_dependencies()
-endif()
-
-# Install files.
-install(DIRECTORY launch DESTINATION share/${PROJECT_NAME})
-# install(DIRECTORY rviz DESTINATION share/${PROJECT_NAME})
-# install(DIRECTORY config DESTINATION share/${PROJECT_NAME})
-# install(DIRECTORY params DESTINATION share/${PROJECT_NAME})
-
-ament_package()
diff --git a/robot/ros_ws/src/sensors/sensors_bringup/LICENSE b/robot/ros_ws/src/sensors/sensors_bringup/LICENSE
deleted file mode 100644
index d64569567..000000000
--- a/robot/ros_ws/src/sensors/sensors_bringup/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/robot/ros_ws/src/sensors/sensors_bringup/launch/gst2ros.launch.xml b/robot/ros_ws/src/sensors/sensors_bringup/launch/gst2ros.launch.xml
deleted file mode 100644
index 02f8c72bd..000000000
--- a/robot/ros_ws/src/sensors/sensors_bringup/launch/gst2ros.launch.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/robot/ros_ws/src/sensors/sensors_bringup/launch/sensors.launch.xml b/robot/ros_ws/src/sensors/sensors_bringup/launch/sensors.launch.xml
deleted file mode 100644
index 95acdc865..000000000
--- a/robot/ros_ws/src/sensors/sensors_bringup/launch/sensors.launch.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/robot/ros_ws/src/sensors/sensors_bringup/package.xml b/robot/ros_ws/src/sensors/sensors_bringup/package.xml
deleted file mode 100644
index 82d396f55..000000000
--- a/robot/ros_ws/src/sensors/sensors_bringup/package.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
- sensors_bringup
- 0.0.0
- TODO: Package description
- andrew
- Apache-2.0
-
- ament_cmake
-
- lidar_point_cloud_filter
-
- ament_lint_auto
- ament_lint_common
-
-
- ament_cmake
-
-
diff --git a/simulation/isaac-sim/config/sim_to_robot_bridge.yaml b/simulation/isaac-sim/config/sim_to_robot_bridge.yaml
index 40ec50f85..0e2ba3725 100644
--- a/simulation/isaac-sim/config/sim_to_robot_bridge.yaml
+++ b/simulation/isaac-sim/config/sim_to_robot_bridge.yaml
@@ -17,7 +17,8 @@
# Robot domain topics (domain 1):
# Same topic names - robot services expect namespaced topics
#
-# TF frames remain non-namespaced to match static_transforms.launch.xml
+# TF frames remain non-namespaced to match the world->map static transform
+# published inline by autonomy_bringup robot.launch.xml
#
# Usage:
# domain_bridge /config/sim_to_robot_bridge.yaml
diff --git a/stacks/full_default/README.md b/stacks/full_default/README.md
index 11744ea0d..b6ca7723a 100644
--- a/stacks/full_default/README.md
+++ b/stacks/full_default/README.md
@@ -18,10 +18,13 @@ layer). Two blocks stay wrapped by design: `interface.launch.py` (the safety
boundary; flattens with RFC #380 Part 2's platform modules) and
`logging.launch.xml` (already a single self-contained module).
-## Equivalence claim
+## Equivalence
-`airstack up --stack full_default` produces a ROS graph identical to the legacy
-`AUTONOMY_ROLE=full` dispatch. Verify with the wiring snapshot test:
+`airstack up --stack full_default` produces a ROS graph identical to the
+removed legacy `AUTONOMY_ROLE=full` dispatch — **machine-proven** by the
+wiring-snapshot diff (this folder's `wiring.md` is the committed baseline).
+This stack is also the default: with no stack selected, `robot.launch.xml`
+launches it. Verify with the wiring snapshot test:
```bash
airstack test -m wiring --stack full_default --sim isaacsim --num-robots 1
@@ -51,6 +54,8 @@ The shared per-robot preamble (ROBOT_NAME namespace, `use_sim_time`,
## wiring.md
+This stack's observed wiring diagram is committed at [wiring.md](wiring.md).
+
Generated — the commit arrives with the first snapshot run of
`airstack test -m wiring --stack full_default` (the harness writes
`observed_full_default.md` under the run directory; validate it and copy it
diff --git a/stacks/full_default/launch/stack.launch.xml b/stacks/full_default/launch/stack.launch.xml
index fa8fd0618..f8e3e6327 100644
--- a/stacks/full_default/launch/stack.launch.xml
+++ b/stacks/full_default/launch/stack.launch.xml
@@ -27,9 +27,10 @@
robot_state_publisher, world/map static TF) runs there unconditionally, so
it is NOT repeated here.
- Equivalence claim: launching this stack produces a ROS graph identical to
- legacy AUTONOMY_ROLE=full (this folder's wiring.md is the committed
- observed baseline).
+ Equivalence: machine-proven identical to the removed AUTONOMY_ROLE=full
+ dispatch (wiring-snapshot diff); this folder's wiring.md is the committed
+ observed baseline. AUTONOMY_ROLE is gone from this branch — stacks are the
+ only dispatch.
-->
@@ -102,7 +103,7 @@
global_plan out; navigate/exploration actions under tasks/*. The node
self-names random_walk_node at the robot root (no namespace push).
Swapping the global planner = replacing this include; the alternative
- trunk planners (ensemble_planner, exploration) ship no canonical
+ trunk planners (exploration) ship no canonical
module launch yet — write one in their package first. -->
@@ -116,12 +117,12 @@
-
+
+ value="$(find-pkg-share autonomy_bringup)/config/dds_router.yaml" />
diff --git a/stacks/full_droan_cpu/README.md b/stacks/full_droan_cpu/README.md
index c5c1290e4..151b766ad 100644
--- a/stacks/full_droan_cpu/README.md
+++ b/stacks/full_droan_cpu/README.md
@@ -15,10 +15,10 @@ perception, the other local modules, global, behavior, logging, DDS router,
gossip — matches `full_default` exactly. (This stack absorbed the deleted
`local_bringup/launch/local_droan_cpu.launch.xml` variant.)
-## Equivalence claim
+## Equivalence
`airstack up --stack full_droan_cpu` produces a ROS graph identical to the
-legacy `AUTONOMY_ROLE=full` dispatch with
+removed legacy `AUTONOMY_ROLE=full` dispatch with
`local_launch_file:=local_droan_cpu.launch.xml` passed to
`onboard_autonomy_all.launch.xml` (that variant file was deleted in P5-E2;
this folder's committed `wiring.md`, captured from exactly that legacy
@@ -45,6 +45,8 @@ airstack ready
## wiring.md
+This stack's observed wiring diagram is committed at [wiring.md](wiring.md).
+
Generated — the commit arrives with the first snapshot run of
`airstack test -m wiring --stack full_droan_cpu`. Once committed, CI
drift-checks the running graph against it.
diff --git a/stacks/full_droan_cpu/launch/stack.launch.xml b/stacks/full_droan_cpu/launch/stack.launch.xml
index 63efb3875..f47b42362 100644
--- a/stacks/full_droan_cpu/launch/stack.launch.xml
+++ b/stacks/full_droan_cpu/launch/stack.launch.xml
@@ -31,10 +31,10 @@
preamble (ROBOT_NAME namespace push, use_sim_time, robot_state_publisher,
world/map static TF) runs there unconditionally, so it is NOT repeated here.
- Equivalence claim: launching this stack produces a ROS graph identical to
- legacy AUTONOMY_ROLE=full with the (deleted) CPU-DROAN local variant
- bringup selected (this folder's wiring.md is the committed observed
- baseline, captured from exactly that legacy configuration).
+ Equivalence: same graph the removed AUTONOMY_ROLE=full dispatch produced
+ with the (deleted) CPU-DROAN local variant bringup selected (this folder's
+ wiring.md is the committed observed baseline, captured from exactly that
+ legacy configuration).
-->
@@ -113,7 +113,7 @@
global_plan out; navigate/exploration actions under tasks/*. The node
self-names random_walk_node at the robot root (no namespace push).
Swapping the global planner = replacing this include; the alternative
- trunk planners (ensemble_planner, exploration) ship no canonical
+ trunk planners (exploration) ship no canonical
module launch yet — write one in their package first. -->
@@ -132,7 +132,7 @@
+ value="$(find-pkg-share autonomy_bringup)/config/dds_router.yaml" />
diff --git a/stacks/full_macvo/README.md b/stacks/full_macvo/README.md
index f7e4653b6..c36f9fa9f 100644
--- a/stacks/full_macvo/README.md
+++ b/stacks/full_macvo/README.md
@@ -71,6 +71,8 @@ airstack ready
## wiring.md
+This stack's observed wiring diagram is committed at [wiring.md](wiring.md).
+
Generated — the commit arrives with the first snapshot run of
`airstack test -m wiring --stack full_macvo`. Once committed, CI drift-checks
the running graph against it.
diff --git a/stacks/full_macvo/launch/stack.launch.xml b/stacks/full_macvo/launch/stack.launch.xml
index 904c60cfe..59c1f4d1a 100644
--- a/stacks/full_macvo/launch/stack.launch.xml
+++ b/stacks/full_macvo/launch/stack.launch.xml
@@ -122,7 +122,7 @@
global_plan out; navigate/exploration actions under tasks/*. The node
self-names random_walk_node at the robot root (no namespace push).
Swapping the global planner = replacing this include; the alternative
- trunk planners (ensemble_planner, exploration) ship no canonical
+ trunk planners (exploration) ship no canonical
module launch yet — write one in their package first. -->
@@ -141,7 +141,7 @@
+ value="$(find-pkg-share autonomy_bringup)/config/dds_router.yaml" />
diff --git a/stacks/lite_default/README.md b/stacks/lite_default/README.md
index 9c33274c6..bb807f05f 100644
--- a/stacks/lite_default/README.md
+++ b/stacks/lite_default/README.md
@@ -1,24 +1,28 @@
# `lite_default` — onboard-lite reference stack
The compute-lite topology, unsplit, as a self-contained stack folder
-(RFC #379 §3). This is the stack equivalent of the legacy
-`AUTONOMY_ROLE=onboard` dispatch: everything a small vehicle runs on its own
-compute, with the heavy global layer left out entirely.
+(RFC #379 §3). This is the stack equivalent of the removed legacy
+`AUTONOMY_ROLE=onboard` role: everything a small vehicle runs on its own
+compute, with the heavy global layer left out entirely. (Historical note:
+the desktop-profile `onboard` role was unreachable in practice —
+`robot-desktop` hardcoded `AUTONOMY_ROLE=full` — so this stack is the first
+reachable lite topology on desktop.)
## What it launches
The single entry point `launch/stack.launch.xml` composes:
-- **Interface, Sensors, Perception, Behavior** — the layer bringups, exactly
- as `onboard_autonomy_local.launch.xml` composes them (wrap form).
-- **Local layer, flat** (module launch files with canonical defaults, copied
- from `full_default`): takeoff/land task server, fixed-trajectory task
- server, GPU DROAN planner, trajectory controller, PID controller.
-- **Role-onboard extras**: the robot↔GCS DDS router (lean onboard allowlist)
- and the gossip coordination layer.
+- **Interface** (wrapped by design) plus **Sensors, Perception, Local,
+ Behavior — all flat**: module launch files with canonical defaults, the
+ same blocks as `full_default` (LiDAR filter, stereo_image_proc + topic
+ keepalive, takeoff/land task server, fixed-trajectory task server, GPU
+ DROAN planner, trajectory controller, PID controller, safety monitor).
+- **Cross-domain extras**: the robot↔GCS DDS router (the shared
+ `autonomy_bringup/config/dds_router.yaml` allowlist) and the gossip
+ coordination layer.
**Deliberately absent:** the global layer (vdb_mapping, random_walk) and the
-logging layer — the `AUTONOMY_ROLE=onboard` subtractions.
+logging layer.
## When to use it
@@ -30,10 +34,11 @@ logging layer — the `AUTONOMY_ROLE=onboard` subtractions.
`onboard.launch.xml` is this topology paired with an offboard global half
and an explicit `bridge.yaml`.
-## Equivalence claim
+## Equivalence
-`airstack up --stack lite_default` produces a ROS graph identical to the
-legacy `AUTONOMY_ROLE=onboard` dispatch. Verify with the wiring snapshot test:
+`airstack up --stack lite_default` produces the topology the removed legacy
+`AUTONOMY_ROLE=onboard` dispatch launched. Verify with the wiring snapshot
+test:
```bash
airstack test -m wiring --stack lite_default --sim isaacsim --num-robots 1
@@ -67,6 +72,8 @@ when `AIRSTACK_STACK_DIR` is set.
## wiring.md
+This stack's observed wiring diagram is committed at [wiring.md](wiring.md).
+
Generated — the commit arrives with the first snapshot run of
`airstack test -m wiring --stack lite_default` (the harness writes
`observed_lite_default.md` under the run directory; validate it and copy it
diff --git a/stacks/lite_default/launch/stack.launch.xml b/stacks/lite_default/launch/stack.launch.xml
index 18bca8f55..60d32fc37 100644
--- a/stacks/lite_default/launch/stack.launch.xml
+++ b/stacks/lite_default/launch/stack.launch.xml
@@ -2,42 +2,64 @@
lite_default : trunk reference stack (RFC #379 S3) — the onboard-lite
topology, unsplit.
- This is the AUTONOMY_ROLE=onboard equivalent as a self-contained stack:
- interface, sensors, perception, the flat Local layer, and behavior run on
- this machine; there is NO global layer and NO logging layer (mirroring
- onboard_autonomy_local.launch.xml's subtractions — global_package and
- logging_package empty). Use it for compute-constrained vehicles (VOXL,
- Jetson lite) or as the onboard half's reference when authoring a split
- stack; lite_offload_global/launch/onboard.launch.xml is this file paired
- with an offboard global half.
-
- LOCAL LAYER FLATTENED (P5-E2 style, copied from full_default): each block
- below reads "this module, these connections". Every module launch file
- declares its topic endpoints as args with canonical defaults (see
+ This is the lite topology as a self-contained stack (historically the
+ AUTONOMY_ROLE=onboard role, removed on this branch): interface, sensors,
+ perception, the flat Local layer, and behavior run on this machine; there
+ is NO global layer and NO logging layer. Use it for compute-constrained
+ vehicles (VOXL, Jetson lite) or as the onboard half's reference when
+ authoring a split stack; lite_offload_global/launch/onboard.launch.xml is
+ this file paired with an offboard global half.
+
+ ALL LAYERS FLATTENED (same layout as full_default): each block below reads
+ "this module, these connections". Every module launch file declares its
+ topic endpoints as args with canonical defaults (see
docs/robot/autonomy/interface_conventions.md), so a bare include means
"wired canonically"; only deviations from canonical appear as include args.
+ Two blocks stay WRAPPED BY DESIGN (not a migration leftover):
+ - interface.launch.py — MAVROS wiring is dense, FCU_URL is env-computed
+ in python, and the interface layer is the safety boundary.
+ - interpolate_dds_router / gossip — cross-domain bridging helpers, not
+ robot-graph modules; their wiring lives in DDS-router YAML configs.
+
Launched by autonomy_bringup/launch/robot.launch.xml when AIRSTACK_STACK_DIR
points at this folder (`airstack up ...stack lite_default`). The shared
preamble (ROBOT_NAME namespace push, use_sim_time, robot_state_publisher,
world/map static TF) runs there unconditionally, so it is NOT repeated here.
- Equivalence claim: launching this stack produces a ROS graph identical to
- legacy AUTONOMY_ROLE=onboard (wiring.md, once committed from the first
- validated snapshot run, is the observed baseline).
+ Equivalence: same topology the removed AUTONOMY_ROLE=onboard dispatch
+ launched (wiring.md, once committed from the first validated snapshot run,
+ is the observed baseline). Note the legacy desktop profile hardcoded
+ AUTONOMY_ROLE=full, so the desktop-profile "onboard" role was unreachable
+ in practice — this stack is the first reachable lite topology on desktop.
-->
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
@@ -67,30 +89,33 @@
interface (cmd_roll_pitch_yawrate_thrust). ONBOARD-ONLY per RFC #380 S2. -->
-
+
-
-
+
+
-
+
-
+
-
+
-
+ value="$(find-pkg-share autonomy_bringup)/config/dds_router.yaml" />
+
-
+
diff --git a/stacks/lite_offload_global/README.md b/stacks/lite_offload_global/README.md
index a800e9520..12d23beb7 100644
--- a/stacks/lite_offload_global/README.md
+++ b/stacks/lite_offload_global/README.md
@@ -3,14 +3,19 @@
The first **split stack** (RFC #380 §2): a lite vehicle half plus an offboard
global-planning half, with an explicit [`bridge.yaml`](bridge.yaml) listing
everything that crosses the machine boundary. This is the stack-shaped
-successor of the legacy `AUTONOMY_ROLE=onboard` / `offboard` pair.
+successor of the legacy `AUTONOMY_ROLE=onboard` / `offboard` pair (removed —
+stacks are the only dispatch). The router config generated from `bridge.yaml`
+replaced the legacy split's committed
+`onboard_local_offboard_global/config/dds_router.yaml`, deliberately minus
+the `set_trajectory_mode` crossing that config carried (doctor hard gate #2:
+command authority stays onboard).
## Anatomy
| File | Runs where | What |
|------|-----------|------|
| `launch/onboard.launch.xml` | the vehicle | [`lite_default`](../lite_default/README.md)'s topology (interface, sensors, perception, flat Local layer, behavior — no global, no logging) + the DDS router configured **from `bridge.yaml`** + gossip |
-| `launch/offboard.launch.xml` | the ground host (conventionally the GCS machine, domain 0) | the global layer only: `vdb_mapping` + `random_walk` (the `global_bringup` include, as in `full_default`) |
+| `launch/offboard.launch.xml` | the ground host (conventionally the GCS machine, domain 0) | the global layer only: `vdb_mapping` + `random_walk` (the same flat includes as `full_default`'s global layer) |
| `bridge.yaml` | — | **THE boundary document**: every topic/service/action crossing between the halves — name, type, direction, QoS. Feeds DDS-router config generation; readable in source. |
A split is a stack *shape*, not special machinery: same four-file anatomy,
diff --git a/stacks/lite_offload_global/bridge.yaml b/stacks/lite_offload_global/bridge.yaml
index 77470afbd..7684722f8 100644
--- a/stacks/lite_offload_global/bridge.yaml
+++ b/stacks/lite_offload_global/bridge.yaml
@@ -30,8 +30,11 @@
# `airstack doctor`) exits 1 naming any violation.
#
# Seeded from the legacy onboard_local_offboard_global dds_router.yaml
-# allowlist (its extension + the onboard_all base it extends). Deliberately
-# NOT carried over from that allowlist:
+# allowlist (its extension + the onboard_all base it extended). Both legacy
+# role folders are REMOVED — the base allowlist now lives at
+# autonomy_bringup/config/dds_router.yaml, and THIS file (via
+# tools/gen_dds_router.py) fully replaces the legacy split's router config.
+# Deliberately NOT carried over from that allowlist:
# - trajectory_controller/set_trajectory_mode (service) — trajectory group:
# the hard gate above. Mode changes are owned by the onboard task servers.
# - trajectory_controller/trajectory_vis — the trajectory_controller/* group
@@ -40,7 +43,7 @@
# split; its viz is already on the ground host, nothing onboard consumes it.
# - bag_record/* — lite stacks run no logging layer.
# - behavior/global_plan_toggle (service) — no node serves it in the current
-# graph (vestigial remap in global.launch.xml).
+# graph (vestigial remap in the deleted legacy global.launch.xml).
# - sensors/front_stereo/right/depth_ground_truth — sim-only ground truth.
version: 1
diff --git a/stacks/lite_offload_global/launch/offboard.launch.xml b/stacks/lite_offload_global/launch/offboard.launch.xml
index b9ab5b42f..e4da5fe87 100644
--- a/stacks/lite_offload_global/launch/offboard.launch.xml
+++ b/stacks/lite_offload_global/launch/offboard.launch.xml
@@ -5,8 +5,9 @@
Topology = the global layer ONLY — vdb_mapping (global world model) +
random_walk (global planner), exactly the includes full_default's entry
- composes for its global layer — mirroring the legacy AUTONOMY_ROLE=offboard
- dispatch (offboard_autonomy_global.launch.xml: every other layer disabled).
+ composes for its global layer. (Historically the AUTONOMY_ROLE=offboard
+ role — every other layer disabled; that dispatch is removed, stacks are
+ the only dispatch.)
Inputs arrive across the bridge (see ../bridge.yaml): the filtered lidar
cloud and odometry from the vehicle. Outputs cross back: global_plan and
@@ -23,10 +24,21 @@
-->
-
-
+
+
+
+
+
+
+
+
+
diff --git a/stacks/lite_offload_global/launch/onboard.launch.xml b/stacks/lite_offload_global/launch/onboard.launch.xml
index 50b4f392c..fea71e35d 100644
--- a/stacks/lite_offload_global/launch/onboard.launch.xml
+++ b/stacks/lite_offload_global/launch/onboard.launch.xml
@@ -33,16 +33,31 @@
default="$(env AIRSTACK_STACK_DIR)/../../.airstack/generated/dds_router.lite_offload_global.yaml"
description="DDS-router config for the onboard/offboard bridge; generated from this stack's bridge.yaml by tools/gen_dds_router.py — regenerate after editing bridge.yaml, never hand-edit the output" />
-
+
-
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -72,11 +87,12 @@
interface (cmd_roll_pitch_yawrate_thrust). ONBOARD-ONLY. -->
-
+
-
-
+
+
diff --git a/tests/conftest.py b/tests/conftest.py
index 7aea0279d..d26e2bbd6 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -28,12 +28,11 @@ def pytest_addoption(parser):
parser.addoption("--num-robots", default="1,3",
help="Comma-separated robot counts, e.g. 1,3")
parser.addoption("--stack", default=None,
- help="Stack folder under stacks/ to launch instead of "
- "the legacy AUTONOMY_ROLE dispatch (sets "
+ help="Stack folder under stacks/ to launch (sets "
"AIRSTACK_STACK_DIR for airstack up). Default: "
- "None (legacy role dispatch). The wiring test "
- "drift-checks against stacks//wiring.md "
- "when set.")
+ "None = the default dispatch, stacks/full_default "
+ "(stacks are the only dispatch). The wiring test "
+ "drift-checks against stacks//wiring.md.")
parser.addoption("--fleet", default=None,
help="Fleet preset under config/fleets/ (RFC #380 §2), "
"e.g. sim_three_mixed. Sets FLEET_CONFIG_FILE for "
@@ -246,8 +245,8 @@ def airstack_env(request):
env_overrides.update(cfg.get("extra_env", {}))
# Stack dispatch (RFC #379 §3): route robot.launch.xml to the stack's
- # entry launch file instead of the legacy AUTONOMY_ROLE role groups.
- # Container path — stacks/ is bind-mounted at /root/AirStack/stacks.
+ # entry launch file (unset = the full_default default). Container path —
+ # stacks/ is bind-mounted at /root/AirStack/stacks.
stack = request.config.getoption("--stack")
if stack:
env_overrides["AIRSTACK_STACK_DIR"] = f"/root/AirStack/stacks/{stack}"
@@ -304,7 +303,8 @@ def airstack_env(request):
"robot_pattern": "robot.*desktop",
"up_started_at": t0,
"cfg": cfg,
- # None = legacy AUTONOMY_ROLE dispatch; else the stacks/ launched.
+ # None = the default dispatch (stacks/full_default); else the
+ # stacks/ explicitly launched.
"stack": stack,
# None = legacy NUM_ROBOTS behavior; else the config/fleets/ flown.
"fleet": fleet,
diff --git a/tests/goldens/wiring/README.md b/tests/goldens/wiring/README.md
deleted file mode 100644
index eb093f0ce..000000000
--- a/tests/goldens/wiring/README.md
+++ /dev/null
@@ -1,58 +0,0 @@
-# Wiring snapshot goldens
-
-Committed observed-wiring baselines for the drift check in
-[`tests/system/test_wiring_snapshot.py`](../../system/test_wiring_snapshot.py)
-(RFC #379 §4.4: the wiring picture is snapshotted from the **running** ROS
-graph — observed, never generated from configuration — so it cannot lie or
-rot).
-
-## Files
-
-One golden per `(sim, num_robots)` configuration, named
-
-```text
-full_default..robot.md
-```
-
-e.g. `full_default.isaacsim.1robot.md`, `full_default.msairsim.3robot.md`.
-Each file is a full `wiring.md`: provenance lines, a mermaid dataflow diagram
-(nodes grouped by namespace, edges labeled topic/type), and a
-machine-readable JSON trailer (``) that the drift
-check actually compares. **Never hand-edit these files** — they are only ever
-copied from a validated run.
-
-## Bootstrap: committing the first golden
-
-Goldens are committed from a validated local run:
-
-1. Run the wiring mark against the target configuration, e.g.
-
- ```bash
- airstack test -m wiring --sim isaacsim --num-robots 1 -v
- ```
-
-2. With no golden present, the test **passes** and writes the observed
- snapshot to `tests/results//wiring/observed_full_default.md`,
- logging an `INSTRUCTION:` line with the exact destination path.
-
-3. Validate the observed snapshot (skim the mermaid diagram: expected nodes
- present, no junk endpoints), then copy it to the golden name and commit:
-
- ```bash
- cp tests/results//wiring/observed_full_default.md \
- tests/goldens/wiring/full_default.isaacsim.1robot.md
- ```
-
-## Drift check semantics
-
-Once a golden exists, the test snapshots the running graph and diffs the two
-normalized graphs (`tests/wiring_snapshot.py diff`): missing/extra nodes,
-missing/extra pub/sub edges, per-edge QoS mismatches, and per-topic type
-mismatches all fail the test with a JSON verdict. Infra noise
-(`/parameter_events`, `/rosout`, pid-suffixed `launch_ros_*` /
-`transform_listener_impl_*` / `_ros2cli*` nodes) is normalized out on both
-sides; `/tf` and `/tf_static` are kept — frame plumbing is wiring.
-
-A PR that intentionally changes wiring must regenerate the affected goldens
-(same flow as bootstrap) and commit them, so the review diff shows the
-topology change.
diff --git a/tests/goldens/wiring/full_default.isaacsim.1robot.md b/tests/goldens/wiring/full_default.isaacsim.1robot.md
deleted file mode 100644
index 999ee2989..000000000
--- a/tests/goldens/wiring/full_default.isaacsim.1robot.md
+++ /dev/null
@@ -1,7567 +0,0 @@
-# Wiring snapshot: full_default
-
-- **generated-by**: tests/system/test_wiring_snapshot.py
-- **date**: 2026-08-21 00:22:30
-- **sim**: isaacsim
-- **num_robots**: 1
-- **source-sha**: 19ec8184f64d
-
-```mermaid
-graph LR
- subgraph g0["behavior"]
- n2["/robot_1/behavior/drone_safety_monitor/drone_safety_monitor"]
- end
- subgraph g1["control"]
- n3["/robot_1/control/pid_controller"]
- end
- subgraph g2["droan"]
- n4["/robot_1/droan/disparity_expander_node"]
- end
- subgraph g3["interface"]
- n6["/robot_1/interface/mavros/actuator_control"]
- n7["/robot_1/interface/mavros/adsb"]
- n8["/robot_1/interface/mavros/altitude"]
- n9["/robot_1/interface/mavros/cam_imu_sync"]
- n10["/robot_1/interface/mavros/camera"]
- n11["/robot_1/interface/mavros/cellular_status"]
- n12["/robot_1/interface/mavros/cmd"]
- n13["/robot_1/interface/mavros/companion_process"]
- n14["/robot_1/interface/mavros/debug_value"]
- n15["/robot_1/interface/mavros/esc_status"]
- n16["/robot_1/interface/mavros/esc_telemetry"]
- n17["/robot_1/interface/mavros/fake_gps"]
- n18["/robot_1/interface/mavros/ftp"]
- n19["/robot_1/interface/mavros/geofence"]
- n20["/robot_1/interface/mavros/gimbal_control"]
- n21["/robot_1/interface/mavros/global_position"]
- n22["/robot_1/interface/mavros/gps_input"]
- n23["/robot_1/interface/mavros/gps_rtk"]
- n24["/robot_1/interface/mavros/gpsstatus"]
- n25["/robot_1/interface/mavros/guided_target"]
- n26["/robot_1/interface/mavros/hil"]
- n27["/robot_1/interface/mavros/home_position"]
- n28["/robot_1/interface/mavros/imu"]
- n29["/robot_1/interface/mavros/landing_target"]
- n30["/robot_1/interface/mavros/local_position"]
- n31["/robot_1/interface/mavros/log_transfer"]
- n32["/robot_1/interface/mavros/mag_calibration"]
- n33["/robot_1/interface/mavros/manual_control"]
- n34["/robot_1/interface/mavros/mavros"]
- n35["/robot_1/interface/mavros/mavros_node"]
- n36["/robot_1/interface/mavros/mavros_router"]
- n37["/robot_1/interface/mavros/mission"]
- n38["/robot_1/interface/mavros/mocap"]
- n39["/robot_1/interface/mavros/mount_control"]
- n40["/robot_1/interface/mavros/nav_controller_output"]
- n41["/robot_1/interface/mavros/obstacle"]
- n42["/robot_1/interface/mavros/obstacle_distance_3d"]
- n43["/robot_1/interface/mavros/odometry"]
- n44["/robot_1/interface/mavros/onboard_computer"]
- n45["/robot_1/interface/mavros/open_drone_id"]
- n46["/robot_1/interface/mavros/optical_flow"]
- n47["/robot_1/interface/mavros/param"]
- n48["/robot_1/interface/mavros/play_tune"]
- n49["/robot_1/interface/mavros/px4flow"]
- n50["/robot_1/interface/mavros/rallypoint"]
- n51["/robot_1/interface/mavros/rc"]
- n52["/robot_1/interface/mavros/setpoint_accel"]
- n53["/robot_1/interface/mavros/setpoint_attitude"]
- n54["/robot_1/interface/mavros/setpoint_position"]
- n55["/robot_1/interface/mavros/setpoint_raw"]
- n56["/robot_1/interface/mavros/setpoint_trajectory"]
- n57["/robot_1/interface/mavros/setpoint_velocity"]
- n58["/robot_1/interface/mavros/sim_state"]
- n59["/robot_1/interface/mavros/sys"]
- n60["/robot_1/interface/mavros/tdr_radio"]
- n61["/robot_1/interface/mavros/terrain"]
- n62["/robot_1/interface/mavros/time"]
- n63["/robot_1/interface/mavros/trajectory"]
- n64["/robot_1/interface/mavros/tunnel"]
- n65["/robot_1/interface/mavros/vfr_hud"]
- n66["/robot_1/interface/mavros/vision_pose"]
- n67["/robot_1/interface/mavros/vision_speed"]
- n68["/robot_1/interface/mavros/wind"]
- n69["/robot_1/interface/odom_modifier"]
- n70["/robot_1/interface/robot_interface"]
- end
- subgraph g4["odometry_conversion"]
- n71["/robot_1/odometry_conversion/odometry_conversion"]
- end
- subgraph g5["perception"]
- n72["/robot_1/perception/stereo_image_proc/disparity_node"]
- n73["/robot_1/perception/stereo_pointcloud"]
- end
- subgraph g6["robot_1"]
- n1["/robot_1/Container"]
- n5["/robot_1/gossip_node"]
- n74["/robot_1/random_walk_node"]
- n75["/robot_1/robot_state_publisher"]
- n78["/robot_1/topic_keepalive"]
- n81["/robot_1/vdb_mapping"]
- n82["/robot_1/world_to_map_broadcaster"]
- end
- subgraph g7["root"]
- n0["/action_relay_client"]
- end
- subgraph g8["sensors"]
- n76["/robot_1/sensors/lidar_point_cloud_filter"]
- end
- subgraph g9["takeoff_landing_planner"]
- n77["/robot_1/takeoff_landing_planner/takeoff_landing_task"]
- end
- subgraph g10["trajectory_controller"]
- n79["/robot_1/trajectory_controller/fixed_trajectory_task"]
- n80["/robot_1/trajectory_controller/trajectory_control_node"]
- end
- d0(["/clock (no publishers)"]) -->|"/clock
Clock"| n1
- d0 -->|"/clock
Clock"| n2
- d0 -->|"/clock
Clock"| n3
- d0 -->|"/clock
Clock"| n4
- d0 -->|"/clock
Clock"| n5
- d0 -->|"/clock
Clock"| n6
- d0 -->|"/clock
Clock"| n7
- d0 -->|"/clock
Clock"| n8
- d0 -->|"/clock
Clock"| n9
- d0 -->|"/clock
Clock"| n10
- d0 -->|"/clock
Clock"| n11
- d0 -->|"/clock
Clock"| n12
- d0 -->|"/clock
Clock"| n13
- d0 -->|"/clock
Clock"| n14
- d0 -->|"/clock
Clock"| n15
- d0 -->|"/clock
Clock"| n16
- d0 -->|"/clock
Clock"| n17
- d0 -->|"/clock
Clock"| n18
- d0 -->|"/clock
Clock"| n19
- d0 -->|"/clock
Clock"| n20
- d0 -->|"/clock
Clock"| n21
- d0 -->|"/clock
Clock"| n22
- d0 -->|"/clock
Clock"| n23
- d0 -->|"/clock
Clock"| n24
- d0 -->|"/clock
Clock"| n25
- d0 -->|"/clock
Clock"| n26
- d0 -->|"/clock
Clock"| n27
- d0 -->|"/clock
Clock"| n28
- d0 -->|"/clock
Clock"| n29
- d0 -->|"/clock
Clock"| n30
- d0 -->|"/clock
Clock"| n31
- d0 -->|"/clock
Clock"| n32
- d0 -->|"/clock
Clock"| n33
- d0 -->|"/clock
Clock"| n34
- d0 -->|"/clock
Clock"| n35
- d0 -->|"/clock
Clock"| n36
- d0 -->|"/clock
Clock"| n37
- d0 -->|"/clock
Clock"| n38
- d0 -->|"/clock
Clock"| n39
- d0 -->|"/clock
Clock"| n40
- d0 -->|"/clock
Clock"| n41
- d0 -->|"/clock
Clock"| n42
- d0 -->|"/clock
Clock"| n43
- d0 -->|"/clock
Clock"| n44
- d0 -->|"/clock
Clock"| n45
- d0 -->|"/clock
Clock"| n46
- d0 -->|"/clock
Clock"| n47
- d0 -->|"/clock
Clock"| n48
- d0 -->|"/clock
Clock"| n49
- d0 -->|"/clock
Clock"| n50
- d0 -->|"/clock
Clock"| n51
- d0 -->|"/clock
Clock"| n52
- d0 -->|"/clock
Clock"| n53
- d0 -->|"/clock
Clock"| n54
- d0 -->|"/clock
Clock"| n55
- d0 -->|"/clock
Clock"| n56
- d0 -->|"/clock
Clock"| n57
- d0 -->|"/clock
Clock"| n58
- d0 -->|"/clock
Clock"| n59
- d0 -->|"/clock
Clock"| n60
- d0 -->|"/clock
Clock"| n61
- d0 -->|"/clock
Clock"| n62
- d0 -->|"/clock
Clock"| n63
- d0 -->|"/clock
Clock"| n64
- d0 -->|"/clock
Clock"| n65
- d0 -->|"/clock
Clock"| n66
- d0 -->|"/clock
Clock"| n67
- d0 -->|"/clock
Clock"| n68
- d0 -->|"/clock
Clock"| n69
- d0 -->|"/clock
Clock"| n70
- d0 -->|"/clock
Clock"| n71
- d0 -->|"/clock
Clock"| n72
- d0 -->|"/clock
Clock"| n73
- d0 -->|"/clock
Clock"| n74
- d0 -->|"/clock
Clock"| n75
- d0 -->|"/clock
Clock"| n76
- d0 -->|"/clock
Clock"| n77
- d0 -->|"/clock
Clock"| n78
- d0 -->|"/clock
Clock"| n79
- d0 -->|"/clock
Clock"| n80
- d0 -->|"/clock
Clock"| n81
- d0 -->|"/clock
Clock"| n82
- n34 -->|"/diagnostics
DiagnosticArray"| d1(["/diagnostics (no subscribers)"])
- n36 -->|"/diagnostics
DiagnosticArray"| d1
- n5 -->|"/gossip/peers
PeerProfile"| n5
- n25 -->|"/move_base_simple/goal
PoseStamped"| d2(["/move_base_simple/goal (no subscribers)"])
- d3(["/robot_1/behavior/drone_safety_monitor/command (no publishers)"]) -->|"/robot_1/behavior/drone_safety_monitor/command
String"| n2
- n2 -->|"/robot_1/behavior/drone_safety_monitor/state_estimate_timed_out
Bool"| n77
- n70 -->|"/robot_1/control/reset_integrators
Empty"| n3
- n3 -->|"/robot_1/control/vx_pid_info
PIDInfo"| d4(["/robot_1/control/vx_pid_info (no subscribers)"])
- n3 -->|"/robot_1/control/vy_pid_info
PIDInfo"| d5(["/robot_1/control/vy_pid_info (no subscribers)"])
- n3 -->|"/robot_1/control/vz_pid_info
PIDInfo"| d6(["/robot_1/control/vz_pid_info (no subscribers)"])
- n3 -->|"/robot_1/control/x_pid_info
PIDInfo"| d7(["/robot_1/control/x_pid_info (no subscribers)"])
- n3 -->|"/robot_1/control/y_pid_info
PIDInfo"| d8(["/robot_1/control/y_pid_info (no subscribers)"])
- n3 -->|"/robot_1/control/z_pid_info
PIDInfo"| d9(["/robot_1/control/z_pid_info (no subscribers)"])
- n5 -->|"/robot_1/coordination/peer_registry
PeerProfile"| d10(["/robot_1/coordination/peer_registry (no subscribers)"])
- n69 -->|"/robot_1/cross_track_error
PoseStamped"| d11(["/robot_1/cross_track_error (no subscribers)"])
- n4 -->|"/robot_1/droan/background_expanded
Image"| d12(["/robot_1/droan/background_expanded (no subscribers)"])
- d13(["/robot_1/droan/clear_map (no publishers)"]) -->|"/robot_1/droan/clear_map
Empty"| n4
- d14(["/robot_1/droan/disparity_graph (no publishers)"]) -->|"/robot_1/droan/disparity_graph
MarkerArray"| n78
- d15(["/robot_1/droan/disparity_map_debug (no publishers)"]) -->|"/robot_1/droan/disparity_map_debug
MarkerArray"| n78
- d16(["/robot_1/droan/expansion_cloud (no publishers)"]) -->|"/robot_1/droan/expansion_cloud
PointCloud2"| n78
- d17(["/robot_1/droan/expansion_poly (no publishers)"]) -->|"/robot_1/droan/expansion_poly
MarkerArray"| n78
- n4 -->|"/robot_1/droan/fg_bg_cloud
PointCloud2"| n78
- n4 -->|"/robot_1/droan/foreground_expanded
Image"| d18(["/robot_1/droan/foreground_expanded (no subscribers)"])
- d19(["/robot_1/droan/frustum (no publishers)"]) -->|"/robot_1/droan/frustum
Marker"| n78
- n4 -->|"/robot_1/droan/graph_vis
MarkerArray"| n78
- n4 -->|"/robot_1/droan/local_planner_global_plan_vis
MarkerArray"| n78
- d20(["/robot_1/droan/reset_stuck (no publishers)"]) -->|"/robot_1/droan/reset_stuck
Empty"| n4
- n4 -->|"/robot_1/droan/rewind_info
MarkerArray"| n78
- n4 -->|"/robot_1/droan/stuck
Bool"| d21(["/robot_1/droan/stuck (no subscribers)"])
- n4 -->|"/robot_1/droan/traj_debug
MarkerArray"| n78
- d22(["/robot_1/droan/trajectory_library_vis (no publishers)"]) -->|"/robot_1/droan/trajectory_library_vis
MarkerArray"| n78
- d23(["/robot_1/droan/virtual_obstacles (no publishers)"]) -->|"/robot_1/droan/virtual_obstacles
MarkerArray"| n78
- n69 -->|"/robot_1/global_plan
Path"| n4
- n69 -->|"/robot_1/global_plan
Path"| n5
- n69 -->|"/robot_1/global_plan
Path"| n78
- n74 -->|"/robot_1/global_plan
Path"| n4
- n74 -->|"/robot_1/global_plan
Path"| n5
- n74 -->|"/robot_1/global_plan
Path"| n78
- d24(["/robot_1/interface/attitude_thrust_command (no publishers)"]) -->|"/robot_1/interface/attitude_thrust_command
AttitudeThrust"| n70
- d25(["/robot_1/interface/cmd_attitude_thrust (no publishers)"]) -->|"/robot_1/interface/cmd_attitude_thrust
AttitudeThrust"| n70
- n69 -->|"/robot_1/interface/cmd_pose
PoseStamped"| n70
- d26(["/robot_1/interface/cmd_rate_thrust (no publishers)"]) -->|"/robot_1/interface/cmd_rate_thrust
RateThrust"| n70
- n3 -->|"/robot_1/interface/cmd_roll_pitch_yawrate_thrust
RollPitchYawrateThrust"| n70
- d27(["/robot_1/interface/cmd_torque_thrust (no publishers)"]) -->|"/robot_1/interface/cmd_torque_thrust
TorqueThrust"| n70
- n69 -->|"/robot_1/interface/cmd_velocity
TwistStamped"| n70
- n70 -->|"/robot_1/interface/has_control
Bool"| n77
- n70 -->|"/robot_1/interface/is_armed
Bool"| n77
- d28(["/robot_1/interface/mavros/actuator_control (no publishers)"]) -->|"/robot_1/interface/mavros/actuator_control
ActuatorControl"| n6
- d29(["/robot_1/interface/mavros/adsb/send (no publishers)"]) -->|"/robot_1/interface/mavros/adsb/send
ADSBVehicle"| n7
- n7 -->|"/robot_1/interface/mavros/adsb/vehicle
ADSBVehicle"| d30(["/robot_1/interface/mavros/adsb/vehicle (no subscribers)"])
- n8 -->|"/robot_1/interface/mavros/altitude
Altitude"| d31(["/robot_1/interface/mavros/altitude (no subscribers)"])
- n59 -->|"/robot_1/interface/mavros/battery
BatteryState"| d32(["/robot_1/interface/mavros/battery (no subscribers)"])
- n9 -->|"/robot_1/interface/mavros/cam_imu_sync/cam_imu_stamp
CamIMUStamp"| d33(["/robot_1/interface/mavros/cam_imu_sync/cam_imu_stamp (no subscribers)"])
- n10 -->|"/robot_1/interface/mavros/camera/image_captured
CameraImageCaptured"| d34(["/robot_1/interface/mavros/camera/image_captured (no subscribers)"])
- d35(["/robot_1/interface/mavros/cellular_status/status (no publishers)"]) -->|"/robot_1/interface/mavros/cellular_status/status
CellularStatus"| n11
- d36(["/robot_1/interface/mavros/companion_process/status (no publishers)"]) -->|"/robot_1/interface/mavros/companion_process/status
CompanionProcessStatus"| n13
- n14 -->|"/robot_1/interface/mavros/debug_value/debug
DebugValue"| d37(["/robot_1/interface/mavros/debug_value/debug (no subscribers)"])
- n14 -->|"/robot_1/interface/mavros/debug_value/debug_float_array
DebugValue"| d38(["/robot_1/interface/mavros/debug_value/debug_float_array (no subscribers)"])
- n14 -->|"/robot_1/interface/mavros/debug_value/debug_vector
DebugValue"| d39(["/robot_1/interface/mavros/debug_value/debug_vector (no subscribers)"])
- n14 -->|"/robot_1/interface/mavros/debug_value/named_value_float
DebugValue"| d40(["/robot_1/interface/mavros/debug_value/named_value_float (no subscribers)"])
- n14 -->|"/robot_1/interface/mavros/debug_value/named_value_int
DebugValue"| d41(["/robot_1/interface/mavros/debug_value/named_value_int (no subscribers)"])
- d42(["/robot_1/interface/mavros/debug_value/send (no publishers)"]) -->|"/robot_1/interface/mavros/debug_value/send
DebugValue"| n14
- n15 -->|"/robot_1/interface/mavros/esc_status/info
ESCInfo"| d43(["/robot_1/interface/mavros/esc_status/info (no subscribers)"])
- n15 -->|"/robot_1/interface/mavros/esc_status/status
ESCStatus"| d44(["/robot_1/interface/mavros/esc_status/status (no subscribers)"])
- n16 -->|"/robot_1/interface/mavros/esc_telemetry/telemetry
ESCTelemetry"| d45(["/robot_1/interface/mavros/esc_telemetry/telemetry (no subscribers)"])
- n59 -->|"/robot_1/interface/mavros/estimator_status
EstimatorStatus"| d46(["/robot_1/interface/mavros/estimator_status (no subscribers)"])
- n59 -->|"/robot_1/interface/mavros/extended_state
ExtendedState"| n77
- d47(["/robot_1/interface/mavros/fake_gps/mocap/tf (no publishers)"]) -->|"/robot_1/interface/mavros/fake_gps/mocap/tf
TransformStamped"| n17
- n19 -->|"/robot_1/interface/mavros/geofence/fences
WaypointList"| d48(["/robot_1/interface/mavros/geofence/fences (no subscribers)"])
- n20 -->|"/robot_1/interface/mavros/gimbal_control/device/attitude_status
GimbalDeviceAttitudeStatus"| d49(["/robot_1/interface/mavros/gimbal_control/device/attitude_status (no subscribers)"])
- n20 -->|"/robot_1/interface/mavros/gimbal_control/device/info
GimbalDeviceInformation"| d50(["/robot_1/interface/mavros/gimbal_control/device/info (no subscribers)"])
- d51(["/robot_1/interface/mavros/gimbal_control/device/set_attitude (no publishers)"]) -->|"/robot_1/interface/mavros/gimbal_control/device/set_attitude
GimbalDeviceSetAttitude"| n20
- n20 -->|"/robot_1/interface/mavros/gimbal_control/manager/info
GimbalManagerInformation"| d52(["/robot_1/interface/mavros/gimbal_control/manager/info (no subscribers)"])
- d53(["/robot_1/interface/mavros/gimbal_control/manager/set_attitude (no publishers)"]) -->|"/robot_1/interface/mavros/gimbal_control/manager/set_attitude
GimbalManagerSetAttitude"| n20
- d54(["/robot_1/interface/mavros/gimbal_control/manager/set_manual_control (no publishers)"]) -->|"/robot_1/interface/mavros/gimbal_control/manager/set_manual_control
GimbalManagerSetPitchyaw"| n20
- d55(["/robot_1/interface/mavros/gimbal_control/manager/set_pitchyaw (no publishers)"]) -->|"/robot_1/interface/mavros/gimbal_control/manager/set_pitchyaw
GimbalManagerSetPitchyaw"| n20
- n20 -->|"/robot_1/interface/mavros/gimbal_control/manager/status
GimbalManagerStatus"| d56(["/robot_1/interface/mavros/gimbal_control/manager/status (no subscribers)"])
- n21 -->|"/robot_1/interface/mavros/global_position/compass_hdg
Float64"| n5
- n21 -->|"/robot_1/interface/mavros/global_position/global
NavSatFix"| n54
- n21 -->|"/robot_1/interface/mavros/global_position/global
NavSatFix"| n70
- n21 -->|"/robot_1/interface/mavros/global_position/gp_lp_offset
PoseStamped"| d57(["/robot_1/interface/mavros/global_position/gp_lp_offset (no subscribers)"])
- n21 -->|"/robot_1/interface/mavros/global_position/gp_origin
GeoPointStamped"| n25
- n21 -->|"/robot_1/interface/mavros/global_position/local
Odometry"| n70
- n21 -->|"/robot_1/interface/mavros/global_position/raw/fix
NavSatFix"| n5
- n21 -->|"/robot_1/interface/mavros/global_position/raw/gps_vel
TwistStamped"| d58(["/robot_1/interface/mavros/global_position/raw/gps_vel (no subscribers)"])
- n21 -->|"/robot_1/interface/mavros/global_position/raw/satellites
UInt32"| d59(["/robot_1/interface/mavros/global_position/raw/satellites (no subscribers)"])
- n21 -->|"/robot_1/interface/mavros/global_position/rel_alt
Float64"| d60(["/robot_1/interface/mavros/global_position/rel_alt (no subscribers)"])
- d61(["/robot_1/interface/mavros/global_position/set_gp_origin (no publishers)"]) -->|"/robot_1/interface/mavros/global_position/set_gp_origin
GeoPointStamped"| n21
- d62(["/robot_1/interface/mavros/gps_input/gps_input (no publishers)"]) -->|"/robot_1/interface/mavros/gps_input/gps_input
GPSINPUT"| n22
- n23 -->|"/robot_1/interface/mavros/gps_rtk/rtk_baseline
RTKBaseline"| d63(["/robot_1/interface/mavros/gps_rtk/rtk_baseline (no subscribers)"])
- d64(["/robot_1/interface/mavros/gps_rtk/send_rtcm (no publishers)"]) -->|"/robot_1/interface/mavros/gps_rtk/send_rtcm
RTCM"| n23
- n24 -->|"/robot_1/interface/mavros/gpsstatus/gps1/raw
GPSRAW"| d65(["/robot_1/interface/mavros/gpsstatus/gps1/raw (no subscribers)"])
- n24 -->|"/robot_1/interface/mavros/gpsstatus/gps1/rtk
GPSRTK"| d66(["/robot_1/interface/mavros/gpsstatus/gps1/rtk (no subscribers)"])
- n24 -->|"/robot_1/interface/mavros/gpsstatus/gps2/raw
GPSRAW"| d67(["/robot_1/interface/mavros/gpsstatus/gps2/raw (no subscribers)"])
- n24 -->|"/robot_1/interface/mavros/gpsstatus/gps2/rtk
GPSRTK"| d68(["/robot_1/interface/mavros/gpsstatus/gps2/rtk (no subscribers)"])
- n26 -->|"/robot_1/interface/mavros/hil/actuator_controls
HilActuatorControls"| d69(["/robot_1/interface/mavros/hil/actuator_controls (no subscribers)"])
- n26 -->|"/robot_1/interface/mavros/hil/controls
HilControls"| d70(["/robot_1/interface/mavros/hil/controls (no subscribers)"])
- d71(["/robot_1/interface/mavros/hil/gps (no publishers)"]) -->|"/robot_1/interface/mavros/hil/gps
HilGPS"| n26
- d72(["/robot_1/interface/mavros/hil/imu_ned (no publishers)"]) -->|"/robot_1/interface/mavros/hil/imu_ned
HilSensor"| n26
- d73(["/robot_1/interface/mavros/hil/optical_flow (no publishers)"]) -->|"/robot_1/interface/mavros/hil/optical_flow
OpticalFlowRad"| n26
- d74(["/robot_1/interface/mavros/hil/rc_inputs (no publishers)"]) -->|"/robot_1/interface/mavros/hil/rc_inputs
RCIn"| n26
- d75(["/robot_1/interface/mavros/hil/state (no publishers)"]) -->|"/robot_1/interface/mavros/hil/state
HilStateQuaternion"| n26
- n27 -->|"/robot_1/interface/mavros/home_position/home
HomePosition"| n21
- n27 -->|"/robot_1/interface/mavros/home_position/home
HomePosition"| n70
- n70 -->|"/robot_1/interface/mavros/home_position/set
HomePosition"| n27
- n28 -->|"/robot_1/interface/mavros/imu/data
Imu"| d76(["/robot_1/interface/mavros/imu/data (no subscribers)"])
- n28 -->|"/robot_1/interface/mavros/imu/data_raw
Imu"| d77(["/robot_1/interface/mavros/imu/data_raw (no subscribers)"])
- n28 -->|"/robot_1/interface/mavros/imu/diff_pressure
FluidPressure"| d78(["/robot_1/interface/mavros/imu/diff_pressure (no subscribers)"])
- n28 -->|"/robot_1/interface/mavros/imu/mag
MagneticField"| d79(["/robot_1/interface/mavros/imu/mag (no subscribers)"])
- n28 -->|"/robot_1/interface/mavros/imu/static_pressure
FluidPressure"| d80(["/robot_1/interface/mavros/imu/static_pressure (no subscribers)"])
- n28 -->|"/robot_1/interface/mavros/imu/temperature_baro
Temperature"| d81(["/robot_1/interface/mavros/imu/temperature_baro (no subscribers)"])
- n28 -->|"/robot_1/interface/mavros/imu/temperature_imu
Temperature"| d82(["/robot_1/interface/mavros/imu/temperature_imu (no subscribers)"])
- n29 -->|"/robot_1/interface/mavros/landing_target/lt_marker
Vector3Stamped"| d83(["/robot_1/interface/mavros/landing_target/lt_marker (no subscribers)"])
- d84(["/robot_1/interface/mavros/landing_target/pose (no publishers)"]) -->|"/robot_1/interface/mavros/landing_target/pose
PoseStamped"| n29
- n29 -->|"/robot_1/interface/mavros/landing_target/pose_in
PoseStamped"| d85(["/robot_1/interface/mavros/landing_target/pose_in (no subscribers)"])
- n30 -->|"/robot_1/interface/mavros/local_position/accel
AccelWithCovarianceStamped"| d86(["/robot_1/interface/mavros/local_position/accel (no subscribers)"])
- n30 -->|"/robot_1/interface/mavros/local_position/odom
Odometry"| n71
- n30 -->|"/robot_1/interface/mavros/local_position/pose
PoseStamped"| n54
- n30 -->|"/robot_1/interface/mavros/local_position/pose_cov
PoseWithCovarianceStamped"| d87(["/robot_1/interface/mavros/local_position/pose_cov (no subscribers)"])
- n30 -->|"/robot_1/interface/mavros/local_position/velocity_body
TwistStamped"| d88(["/robot_1/interface/mavros/local_position/velocity_body (no subscribers)"])
- n30 -->|"/robot_1/interface/mavros/local_position/velocity_body_cov
TwistWithCovarianceStamped"| d89(["/robot_1/interface/mavros/local_position/velocity_body_cov (no subscribers)"])
- n30 -->|"/robot_1/interface/mavros/local_position/velocity_local
TwistStamped"| d90(["/robot_1/interface/mavros/local_position/velocity_local (no subscribers)"])
- n31 -->|"/robot_1/interface/mavros/log_transfer/raw/log_data
LogData"| d91(["/robot_1/interface/mavros/log_transfer/raw/log_data (no subscribers)"])
- n31 -->|"/robot_1/interface/mavros/log_transfer/raw/log_entry
LogEntry"| d92(["/robot_1/interface/mavros/log_transfer/raw/log_entry (no subscribers)"])
- n32 -->|"/robot_1/interface/mavros/mag_calibration/report
MagnetometerReporter"| d93(["/robot_1/interface/mavros/mag_calibration/report (no subscribers)"])
- n32 -->|"/robot_1/interface/mavros/mag_calibration/status
UInt8"| d94(["/robot_1/interface/mavros/mag_calibration/status (no subscribers)"])
- n33 -->|"/robot_1/interface/mavros/manual_control/control
ManualControl"| d95(["/robot_1/interface/mavros/manual_control/control (no subscribers)"])
- d96(["/robot_1/interface/mavros/manual_control/send (no publishers)"]) -->|"/robot_1/interface/mavros/manual_control/send
ManualControl"| n33
- n37 -->|"/robot_1/interface/mavros/mission/reached
WaypointReached"| d97(["/robot_1/interface/mavros/mission/reached (no subscribers)"])
- n37 -->|"/robot_1/interface/mavros/mission/waypoints
WaypointList"| d98(["/robot_1/interface/mavros/mission/waypoints (no subscribers)"])
- d99(["/robot_1/interface/mavros/mocap/pose (no publishers)"]) -->|"/robot_1/interface/mavros/mocap/pose
PoseStamped"| n38
- d100(["/robot_1/interface/mavros/mocap/tf (no publishers)"]) -->|"/robot_1/interface/mavros/mocap/tf
TransformStamped"| n38
- d101(["/robot_1/interface/mavros/mount_control/command (no publishers)"]) -->|"/robot_1/interface/mavros/mount_control/command
MountControl"| n39
- n39 -->|"/robot_1/interface/mavros/mount_control/orientation
Quaternion"| d102(["/robot_1/interface/mavros/mount_control/orientation (no subscribers)"])
- n39 -->|"/robot_1/interface/mavros/mount_control/status
Vector3Stamped"| d103(["/robot_1/interface/mavros/mount_control/status (no subscribers)"])
- n40 -->|"/robot_1/interface/mavros/nav_controller_output/output
NavControllerOutput"| d104(["/robot_1/interface/mavros/nav_controller_output/output (no subscribers)"])
- d105(["/robot_1/interface/mavros/obstacle/send (no publishers)"]) -->|"/robot_1/interface/mavros/obstacle/send
LaserScan"| n41
- d106(["/robot_1/interface/mavros/obstacle_distance_3d/send (no publishers)"]) -->|"/robot_1/interface/mavros/obstacle_distance_3d/send
ObstacleDistance3D"| n42
- n43 -->|"/robot_1/interface/mavros/odometry/in
Odometry"| d107(["/robot_1/interface/mavros/odometry/in (no subscribers)"])
- d108(["/robot_1/interface/mavros/odometry/out (no publishers)"]) -->|"/robot_1/interface/mavros/odometry/out
Odometry"| n43
- d109(["/robot_1/interface/mavros/onboard_computer/status (no publishers)"]) -->|"/robot_1/interface/mavros/onboard_computer/status
OnboardComputerStatus"| n44
- d110(["/robot_1/interface/mavros/open_drone_id/basic_id (no publishers)"]) -->|"/robot_1/interface/mavros/open_drone_id/basic_id
OpenDroneIDBasicID"| n45
- d111(["/robot_1/interface/mavros/open_drone_id/operator_id (no publishers)"]) -->|"/robot_1/interface/mavros/open_drone_id/operator_id
OpenDroneIDOperatorID"| n45
- d112(["/robot_1/interface/mavros/open_drone_id/self_id (no publishers)"]) -->|"/robot_1/interface/mavros/open_drone_id/self_id
OpenDroneIDSelfID"| n45
- d113(["/robot_1/interface/mavros/open_drone_id/system (no publishers)"]) -->|"/robot_1/interface/mavros/open_drone_id/system
OpenDroneIDSystem"| n45
- d114(["/robot_1/interface/mavros/open_drone_id/system_update (no publishers)"]) -->|"/robot_1/interface/mavros/open_drone_id/system_update
OpenDroneIDSystemUpdate"| n45
- n46 -->|"/robot_1/interface/mavros/optical_flow/ground_distance
Range"| d115(["/robot_1/interface/mavros/optical_flow/ground_distance (no subscribers)"])
- n46 -->|"/robot_1/interface/mavros/optical_flow/raw/optical_flow
OpticalFlow"| d116(["/robot_1/interface/mavros/optical_flow/raw/optical_flow (no subscribers)"])
- d117(["/robot_1/interface/mavros/optical_flow/raw/send (no publishers)"]) -->|"/robot_1/interface/mavros/optical_flow/raw/send
OpticalFlow"| n46
- n47 -->|"/robot_1/interface/mavros/param/event
ParamEvent"| d118(["/robot_1/interface/mavros/param/event (no subscribers)"])
- d119(["/robot_1/interface/mavros/play_tune (no publishers)"]) -->|"/robot_1/interface/mavros/play_tune
PlayTuneV2"| n48
- n49 -->|"/robot_1/interface/mavros/px4flow/ground_distance
Range"| d120(["/robot_1/interface/mavros/px4flow/ground_distance (no subscribers)"])
- n49 -->|"/robot_1/interface/mavros/px4flow/raw/optical_flow_rad
OpticalFlowRad"| d121(["/robot_1/interface/mavros/px4flow/raw/optical_flow_rad (no subscribers)"])
- d122(["/robot_1/interface/mavros/px4flow/raw/send (no publishers)"]) -->|"/robot_1/interface/mavros/px4flow/raw/send
OpticalFlowRad"| n49
- n49 -->|"/robot_1/interface/mavros/px4flow/temperature
Temperature"| d123(["/robot_1/interface/mavros/px4flow/temperature (no subscribers)"])
- n60 -->|"/robot_1/interface/mavros/radio_status
RadioStatus"| d124(["/robot_1/interface/mavros/radio_status (no subscribers)"])
- n50 -->|"/robot_1/interface/mavros/rallypoint/rallypoints
WaypointList"| d125(["/robot_1/interface/mavros/rallypoint/rallypoints (no subscribers)"])
- n51 -->|"/robot_1/interface/mavros/rc/in
RCIn"| d126(["/robot_1/interface/mavros/rc/in (no subscribers)"])
- n51 -->|"/robot_1/interface/mavros/rc/out
RCOut"| d127(["/robot_1/interface/mavros/rc/out (no subscribers)"])
- d128(["/robot_1/interface/mavros/rc/override (no publishers)"]) -->|"/robot_1/interface/mavros/rc/override
OverrideRCIn"| n51
- d129(["/robot_1/interface/mavros/setpoint_accel/accel (no publishers)"]) -->|"/robot_1/interface/mavros/setpoint_accel/accel
Vector3Stamped"| n52
- d130(["/robot_1/interface/mavros/setpoint_attitude/cmd_vel (no publishers)"]) -->|"/robot_1/interface/mavros/setpoint_attitude/cmd_vel
TwistStamped"| n53
- d131(["/robot_1/interface/mavros/setpoint_attitude/thrust (no publishers)"]) -->|"/robot_1/interface/mavros/setpoint_attitude/thrust
Thrust"| n53
- n70 -->|"/robot_1/interface/mavros/setpoint_position/global
GeoPoseStamped"| n54
- d132(["/robot_1/interface/mavros/setpoint_position/global_to_local (no publishers)"]) -->|"/robot_1/interface/mavros/setpoint_position/global_to_local
GeoPoseStamped"| n54
- n70 -->|"/robot_1/interface/mavros/setpoint_position/local
PoseStamped"| n54
- n70 -->|"/robot_1/interface/mavros/setpoint_raw/attitude
AttitudeTarget"| n55
- d133(["/robot_1/interface/mavros/setpoint_raw/global (no publishers)"]) -->|"/robot_1/interface/mavros/setpoint_raw/global
GlobalPositionTarget"| n55
- n70 -->|"/robot_1/interface/mavros/setpoint_raw/local
PositionTarget"| n55
- n55 -->|"/robot_1/interface/mavros/setpoint_raw/target_attitude
AttitudeTarget"| d134(["/robot_1/interface/mavros/setpoint_raw/target_attitude (no subscribers)"])
- n55 -->|"/robot_1/interface/mavros/setpoint_raw/target_global
GlobalPositionTarget"| d135(["/robot_1/interface/mavros/setpoint_raw/target_global (no subscribers)"])
- n55 -->|"/robot_1/interface/mavros/setpoint_raw/target_local
PositionTarget"| d136(["/robot_1/interface/mavros/setpoint_raw/target_local (no subscribers)"])
- n56 -->|"/robot_1/interface/mavros/setpoint_trajectory/desired
Path"| d137(["/robot_1/interface/mavros/setpoint_trajectory/desired (no subscribers)"])
- d138(["/robot_1/interface/mavros/setpoint_trajectory/local (no publishers)"]) -->|"/robot_1/interface/mavros/setpoint_trajectory/local
MultiDOFJointTrajectory"| n56
- d139(["/robot_1/interface/mavros/setpoint_velocity/cmd_vel (no publishers)"]) -->|"/robot_1/interface/mavros/setpoint_velocity/cmd_vel
TwistStamped"| n57
- d140(["/robot_1/interface/mavros/setpoint_velocity/cmd_vel_unstamped (no publishers)"]) -->|"/robot_1/interface/mavros/setpoint_velocity/cmd_vel_unstamped
Twist"| n57
- n58 -->|"/robot_1/interface/mavros/sim_state/acceleration
Vector3Stamped"| d141(["/robot_1/interface/mavros/sim_state/acceleration (no subscribers)"])
- n58 -->|"/robot_1/interface/mavros/sim_state/attitude
Imu"| d142(["/robot_1/interface/mavros/sim_state/attitude (no subscribers)"])
- n58 -->|"/robot_1/interface/mavros/sim_state/global_position
NavSatFix"| d143(["/robot_1/interface/mavros/sim_state/global_position (no subscribers)"])
- n58 -->|"/robot_1/interface/mavros/sim_state/velocity_body
TwistStamped"| d144(["/robot_1/interface/mavros/sim_state/velocity_body (no subscribers)"])
- n58 -->|"/robot_1/interface/mavros/sim_state/velocity_local
TwistStamped"| d145(["/robot_1/interface/mavros/sim_state/velocity_local (no subscribers)"])
- n59 -->|"/robot_1/interface/mavros/state
State"| n70
- n59 -->|"/robot_1/interface/mavros/status_event
StatusEvent"| d146(["/robot_1/interface/mavros/status_event (no subscribers)"])
- n59 -->|"/robot_1/interface/mavros/statustext/recv
StatusText"| d147(["/robot_1/interface/mavros/statustext/recv (no subscribers)"])
- d148(["/robot_1/interface/mavros/statustext/send (no publishers)"]) -->|"/robot_1/interface/mavros/statustext/send
StatusText"| n59
- n59 -->|"/robot_1/interface/mavros/sys_status
SysStatus"| d149(["/robot_1/interface/mavros/sys_status (no subscribers)"])
- n6 -->|"/robot_1/interface/mavros/target_actuator_control
ActuatorControl"| d150(["/robot_1/interface/mavros/target_actuator_control (no subscribers)"])
- n61 -->|"/robot_1/interface/mavros/terrain/report
TerrainReport"| d151(["/robot_1/interface/mavros/terrain/report (no subscribers)"])
- n62 -->|"/robot_1/interface/mavros/time_reference
TimeReference"| d152(["/robot_1/interface/mavros/time_reference (no subscribers)"])
- n62 -->|"/robot_1/interface/mavros/timesync_status
TimesyncStatus"| d153(["/robot_1/interface/mavros/timesync_status (no subscribers)"])
- n63 -->|"/robot_1/interface/mavros/trajectory/desired
Trajectory"| d154(["/robot_1/interface/mavros/trajectory/desired (no subscribers)"])
- d155(["/robot_1/interface/mavros/trajectory/generated (no publishers)"]) -->|"/robot_1/interface/mavros/trajectory/generated
Trajectory"| n63
- d156(["/robot_1/interface/mavros/trajectory/path (no publishers)"]) -->|"/robot_1/interface/mavros/trajectory/path
Path"| n63
- d157(["/robot_1/interface/mavros/tunnel/in (no publishers)"]) -->|"/robot_1/interface/mavros/tunnel/in
Tunnel"| n64
- n64 -->|"/robot_1/interface/mavros/tunnel/out
Tunnel"| d158(["/robot_1/interface/mavros/tunnel/out (no subscribers)"])
- n65 -->|"/robot_1/interface/mavros/vfr_hud
VfrHud"| d159(["/robot_1/interface/mavros/vfr_hud (no subscribers)"])
- d160(["/robot_1/interface/mavros/vision_pose/pose (no publishers)"]) -->|"/robot_1/interface/mavros/vision_pose/pose
PoseStamped"| n66
- d161(["/robot_1/interface/mavros/vision_pose/pose_cov (no publishers)"]) -->|"/robot_1/interface/mavros/vision_pose/pose_cov
PoseWithCovarianceStamped"| n66
- d162(["/robot_1/interface/mavros/vision_speed/speed_twist (no publishers)"]) -->|"/robot_1/interface/mavros/vision_speed/speed_twist
TwistStamped"| n67
- d163(["/robot_1/interface/mavros/vision_speed/speed_twist_cov (no publishers)"]) -->|"/robot_1/interface/mavros/vision_speed/speed_twist_cov
TwistWithCovarianceStamped"| n67
- d164(["/robot_1/interface/mavros/vision_speed/speed_vector (no publishers)"]) -->|"/robot_1/interface/mavros/vision_speed/speed_vector
Vector3Stamped"| n67
- n68 -->|"/robot_1/interface/mavros/wind_estimation
TwistWithCovarianceStamped"| d165(["/robot_1/interface/mavros/wind_estimation (no subscribers)"])
- d166(["/robot_1/interface/pose_command (no publishers)"]) -->|"/robot_1/interface/pose_command
PoseStamped"| n70
- d167(["/robot_1/interface/rate_thrust_command (no publishers)"]) -->|"/robot_1/interface/rate_thrust_command
RateThrust"| n70
- d168(["/robot_1/interface/roll_pitch_yawrate_thrust_command (no publishers)"]) -->|"/robot_1/interface/roll_pitch_yawrate_thrust_command
RollPitchYawrateThrust"| n70
- d169(["/robot_1/interface/torque_thrust_command (no publishers)"]) -->|"/robot_1/interface/torque_thrust_command
TorqueThrust"| n70
- d170(["/robot_1/interface/velocity_command (no publishers)"]) -->|"/robot_1/interface/velocity_command
TwistStamped"| n70
- d171(["/robot_1/joint_states (no publishers)"]) -->|"/robot_1/joint_states
JointState"| n75
- n71 -->|"/robot_1/odometry_conversion/odometry
Odometry"| n2
- n71 -->|"/robot_1/odometry_conversion/odometry
Odometry"| n3
- n71 -->|"/robot_1/odometry_conversion/odometry
Odometry"| n69
- n71 -->|"/robot_1/odometry_conversion/odometry
Odometry"| n74
- n71 -->|"/robot_1/odometry_conversion/odometry
Odometry"| n77
- n71 -->|"/robot_1/odometry_conversion/odometry
Odometry"| n78
- n71 -->|"/robot_1/odometry_conversion/odometry
Odometry"| n79
- n71 -->|"/robot_1/odometry_conversion/odometry
Odometry"| n80
- n72 -->|"/robot_1/perception/stereo_image_proc/disparity
DisparityImage"| n4
- n72 -->|"/robot_1/perception/stereo_image_proc/disparity
DisparityImage"| n73
- n73 -->|"/robot_1/perception/stereo_image_proc/point_cloud
PointCloud2"| n78
- n74 -->|"/robot_1/random_walk_node/goal_point_viz
Marker"| d172(["/robot_1/random_walk_node/goal_point_viz (no subscribers)"])
- n74 -->|"/robot_1/random_walk_node/traj_viz
Marker"| d173(["/robot_1/random_walk_node/traj_viz (no subscribers)"])
- n75 -->|"/robot_1/robot_description
String"| d174(["/robot_1/robot_description (no subscribers)"])
- d175(["/robot_1/sensors/front_stereo/left/camera_info (no publishers)"]) -->|"/robot_1/sensors/front_stereo/left/camera_info
CameraInfo"| n72
- d175 -->|"/robot_1/sensors/front_stereo/left/camera_info
CameraInfo"| n73
- d175 -->|"/robot_1/sensors/front_stereo/left/camera_info
CameraInfo"| n78
- d176(["/robot_1/sensors/front_stereo/left/depth_ground_truth (no publishers)"]) -->|"/robot_1/sensors/front_stereo/left/depth_ground_truth
Image"| n78
- d177(["/robot_1/sensors/front_stereo/left/image_rect (no publishers)"]) -->|"/robot_1/sensors/front_stereo/left/image_rect
Image"| n72
- d177 -->|"/robot_1/sensors/front_stereo/left/image_rect
Image"| n73
- d177 -->|"/robot_1/sensors/front_stereo/left/image_rect
Image"| n78
- d178(["/robot_1/sensors/front_stereo/right/camera_info (no publishers)"]) -->|"/robot_1/sensors/front_stereo/right/camera_info
CameraInfo"| n4
- d178 -->|"/robot_1/sensors/front_stereo/right/camera_info
CameraInfo"| n72
- d178 -->|"/robot_1/sensors/front_stereo/right/camera_info
CameraInfo"| n73
- d178 -->|"/robot_1/sensors/front_stereo/right/camera_info
CameraInfo"| n78
- d179(["/robot_1/sensors/front_stereo/right/depth_ground_truth (no publishers)"]) -->|"/robot_1/sensors/front_stereo/right/depth_ground_truth
Image"| n78
- d180(["/robot_1/sensors/front_stereo/right/image_rect (no publishers)"]) -->|"/robot_1/sensors/front_stereo/right/image_rect
Image"| n72
- d180 -->|"/robot_1/sensors/front_stereo/right/image_rect
Image"| n78
- d181(["/robot_1/sensors/lidar/point_cloud (no publishers)"]) -->|"/robot_1/sensors/lidar/point_cloud
PointCloud2"| n78
- n76 -->|"/robot_1/sensors/ouster/point_cloud
PointCloud2"| n81
- d182(["/robot_1/sensors/ouster/point_cloud_raw (no publishers)"]) -->|"/robot_1/sensors/ouster/point_cloud_raw
PointCloud2"| n76
- n77 -->|"/robot_1/takeoff_landing_planner/is_airborne
Bool"| d183(["/robot_1/takeoff_landing_planner/is_airborne (no subscribers)"])
- d184(["/robot_1/takeoff_landing_planner/trajectory_completion_percentage (no publishers)"]) -->|"/robot_1/takeoff_landing_planner/trajectory_completion_percentage
Float32"| n77
- n80 -->|"/robot_1/trajectory_controller/closest_point
Odometry"| d185(["/robot_1/trajectory_controller/closest_point (no subscribers)"])
- n80 -->|"/robot_1/trajectory_controller/look_ahead
Odometry"| n4
- n80 -->|"/robot_1/trajectory_controller/projected_drone_pose
PoseStamped"| n69
- n80 -->|"/robot_1/trajectory_controller/tracking_error
Float32"| d186(["/robot_1/trajectory_controller/tracking_error (no subscribers)"])
- n80 -->|"/robot_1/trajectory_controller/tracking_point
Odometry"| n3
- n80 -->|"/robot_1/trajectory_controller/tracking_point
Odometry"| n4
- n80 -->|"/robot_1/trajectory_controller/tracking_point
Odometry"| n69
- n80 -->|"/robot_1/trajectory_controller/tracking_point
Odometry"| n77
- n80 -->|"/robot_1/trajectory_controller/tracking_point_velocity_magnitude
Float32"| d187(["/robot_1/trajectory_controller/tracking_point_velocity_magnitude (no subscribers)"])
- n80 -->|"/robot_1/trajectory_controller/traj_drone_point
Odometry"| d188(["/robot_1/trajectory_controller/traj_drone_point (no subscribers)"])
- n80 -->|"/robot_1/trajectory_controller/trajectory_completion_percentage
Float32"| n79
- n80 -->|"/robot_1/trajectory_controller/trajectory_controller_debug_markers
MarkerArray"| n78
- n77 -->|"/robot_1/trajectory_controller/trajectory_override
TrajectoryXYZVYaw"| n80
- n79 -->|"/robot_1/trajectory_controller/trajectory_override
TrajectoryXYZVYaw"| n80
- n4 -->|"/robot_1/trajectory_controller/trajectory_segment_to_add
TrajectoryXYZVYaw"| n80
- n80 -->|"/robot_1/trajectory_controller/trajectory_time
Float32"| d189(["/robot_1/trajectory_controller/trajectory_time (no subscribers)"])
- n80 -->|"/robot_1/trajectory_controller/trajectory_vis
MarkerArray"| n78
- n80 -->|"/robot_1/trajectory_controller/virtual_tracking_point
Odometry"| d190(["/robot_1/trajectory_controller/virtual_tracking_point (no subscribers)"])
- n81 -->|"/robot_1/vdb_mapping/vdb_map_overwrites
UpdateGrid"| d191(["/robot_1/vdb_mapping/vdb_map_overwrites (no subscribers)"])
- n81 -->|"/robot_1/vdb_mapping/vdb_map_pointcloud
PointCloud2"| d192(["/robot_1/vdb_mapping/vdb_map_pointcloud (no subscribers)"])
- n81 -->|"/robot_1/vdb_mapping/vdb_map_sections
UpdateGrid"| d193(["/robot_1/vdb_mapping/vdb_map_sections (no subscribers)"])
- n81 -->|"/robot_1/vdb_mapping/vdb_map_updates
UpdateGrid"| d194(["/robot_1/vdb_mapping/vdb_map_updates (no subscribers)"])
- n81 -->|"/robot_1/vdb_mapping/vdb_map_visualization
Marker"| n74
- n81 -->|"/robot_1/vdb_mapping/vdb_map_visualization
Marker"| n78
- n34 -->|"/tf
TFMessage"| n69
- n34 -->|"/tf
TFMessage"| n78
- n71 -->|"/tf
TFMessage"| n69
- n71 -->|"/tf
TFMessage"| n78
- n75 -->|"/tf
TFMessage"| n69
- n75 -->|"/tf
TFMessage"| n78
- n80 -->|"/tf
TFMessage"| n69
- n80 -->|"/tf
TFMessage"| n78
- n34 -->|"/tf_static
TFMessage"| n69
- n34 -->|"/tf_static
TFMessage"| n78
- n75 -->|"/tf_static
TFMessage"| n69
- n75 -->|"/tf_static
TFMessage"| n78
- n82 -->|"/tf_static
TFMessage"| n69
- n82 -->|"/tf_static
TFMessage"| n78
- n34 -->|"/uas2/mavlink_sink
Mavlink"| n36
- n36 -->|"/uas2/mavlink_source
Mavlink"| n34
-```
-
-
diff --git a/tests/meta/fixtures/modules_index/modules/dfm2_disturbances.yaml b/tests/meta/fixtures/modules_index/modules/dfm2_disturbances.yaml
new file mode 100644
index 000000000..fd2114cba
--- /dev/null
+++ b/tests/meta/fixtures/modules_index/modules/dfm2_disturbances.yaml
@@ -0,0 +1,18 @@
+# Registry entry for the dfm2_disturbances module (schema/module-entry.schema.json).
+# airstack_compat is the DECLARED range copied from the module's module.yaml;
+# the VERIFIED matrix lives in compat/dfm2_disturbances.yaml (CI-stamped only).
+name: dfm2_disturbances
+repo: https://github.com/castacks/asm_dfm2_disturbances
+description: Isaac Sim disturbance library (fan/vent force fields, strobe lights, lens flare)
+maintainer: maintainers@theairlab.org
+license: MIT
+type: isaac_extension
+registered_ref: af3daa783248b07b82165833d419d322ab3137fe # main HEAD, 2026-08-20
+airstack_compat: ">=0.19.0-alpha.18 <0.20.0" # DECLARED (from module.yaml)
+notes: >-
+ Pilot module of RFC #379, hand-built before the tooling existed (see the
+ repo's FRICTION_LOG.md). registered_ref is a commit SHA because no release
+ tag exists yet: v0.1.0 is pending the first green module-system-tests.yml
+ CI run. Validated end-to-end locally on 2026-08-20 (extraction campaign) —
+ declared marks liveliness + takeoff_hover_land in the module's test_stack
+ on Isaac Sim.
diff --git a/tests/meta/fixtures/modules_index/modules/macvo.yaml b/tests/meta/fixtures/modules_index/modules/macvo.yaml
new file mode 100644
index 000000000..243d2fc74
--- /dev/null
+++ b/tests/meta/fixtures/modules_index/modules/macvo.yaml
@@ -0,0 +1,23 @@
+# Registry entry for the macvo module (schema/module-entry.schema.json).
+# airstack_compat is the DECLARED range copied from the module's module.yaml;
+# the VERIFIED matrix lives in compat/macvo.yaml (CI-stamped only).
+name: macvo
+repo: https://github.com/castacks/asm_macvo
+description: >-
+ MAC-VO learned stereo visual odometry (ICRA 2025 best paper) — macvo_ros2
+ wrapper around the MAC-VO network, publishing odometry, a covariance-aware
+ point cloud, and the disparity image the local planner can consume
+maintainer: maintainers@theairlab.org # placeholder pending a named maintainer (RFC #379 §8)
+license: MIT
+type: ros_package
+registered_ref: 7d5763936122ffcbb7173a635c9d5711e25c2414 # main HEAD, 2026-08-20
+airstack_compat: ">=0.19.0-alpha.18 <0.20.0" # DECLARED (from module.yaml)
+notes: >-
+ registered_ref is a commit SHA because no release tag exists yet: v0.1.0 is
+ pending the first green module-system-tests.yml CI run. RFC #379's dogfood
+ case for Docker dependency tiers 2/3: MAC-VO's heavy deps (TensorRT, torch,
+ model weights) live in the module's Dockerfile.module, out of trunk's
+ Dockerfile.robot. Composed-image CI validation (declared marks build_docker
+ + liveliness against the tier-2 layer chain) is still pending — unlike
+ dfm2_disturbances/optitrack, this module has not yet been validated
+ end-to-end. Consumed by trunk reference stack full_macvo.
diff --git a/tests/meta/fixtures/modules_index/modules/optitrack.yaml b/tests/meta/fixtures/modules_index/modules/optitrack.yaml
new file mode 100644
index 000000000..00bf49f21
--- /dev/null
+++ b/tests/meta/fixtures/modules_index/modules/optitrack.yaml
@@ -0,0 +1,22 @@
+# Registry entry for the optitrack module (schema/module-entry.schema.json).
+# airstack_compat is the DECLARED range copied from the module's module.yaml;
+# the VERIFIED matrix lives in compat/optitrack.yaml (CI-stamped only).
+name: optitrack
+repo: https://github.com/castacks/asm_optitrack
+description: >-
+ OptiTrack NatNet mocap integration — natnet_ros2 client + PX4
+ external-vision fusion bridges on the robot, and the Motive-compatible
+ NatNet server emulator for Isaac Sim
+maintainer: maintainers@theairlab.org # placeholder pending a named maintainer (RFC #379 §8)
+license: MIT
+type: ros_package
+registered_ref: 2ae094f4b308b49127761c0605a17ad4ef4f6e31 # main HEAD, 2026-08-20
+airstack_compat: ">=0.19.0-alpha.18 <0.20.0" # DECLARED (from module.yaml)
+notes: >-
+ registered_ref is a commit SHA because no release tag exists yet: v0.1.0 is
+ pending the first green module-system-tests.yml CI run. Validated
+ end-to-end locally on 2026-08-20 (extraction campaign) — declared marks
+ integration + liveliness + optitrack (full EV-fusion flight e2e) in the
+ module's test_stack on Isaac Sim. Builds against the proprietary OptiTrack
+ NatNet SDK, fetched host-side by hooks.host_setup (never in git, never in
+ images); CI passes NATNET_ACCEPT_LICENSE=1 via hook_env.
diff --git a/tests/meta/fixtures/modules_index/stacks/full_default.yaml b/tests/meta/fixtures/modules_index/stacks/full_default.yaml
new file mode 100644
index 000000000..84e992aef
--- /dev/null
+++ b/tests/meta/fixtures/modules_index/stacks/full_default.yaml
@@ -0,0 +1,14 @@
+# Registry entry for the full_default trunk reference stack (schema/stack-entry.schema.json).
+name: full_default
+repo: https://github.com/castacks/AirStack
+path: stacks/full_default
+description: >-
+ The current full-autonomy topology as a self-contained stack folder — the
+ baseline most users start from and the stack other stacks are copied from
+airstack_compat: ">=0.19.0-alpha.18 <0.21.0" # from the stack's modules.repos airstack_compat key
+wiring: stacks/full_default/wiring.md # docs site embeds the CI-generated wiring.md from trunk
+notes: >-
+ Pulls no external modules (repositories: {} — every launched package is
+ trunk-resident). Equivalence: `airstack up --stack full_default` produces
+ a ROS graph identical to the removed legacy AUTONOMY_ROLE=full dispatch
+ (machine-proven by the wiring snapshot test, -m wiring).
diff --git a/tests/meta/fixtures/modules_index/stacks/full_droan_cpu.yaml b/tests/meta/fixtures/modules_index/stacks/full_droan_cpu.yaml
new file mode 100644
index 000000000..c12a2331f
--- /dev/null
+++ b/tests/meta/fixtures/modules_index/stacks/full_droan_cpu.yaml
@@ -0,0 +1,14 @@
+# Registry entry for the full_droan_cpu trunk reference stack (schema/stack-entry.schema.json).
+name: full_droan_cpu
+repo: https://github.com/castacks/AirStack
+path: stacks/full_droan_cpu
+description: >-
+ Full autonomy with the CPU DROAN local planner (droan_local_planner + live
+ disparity_expansion world model) instead of the GPU droan_gl node
+airstack_compat: ">=0.19.0-alpha.18 <0.21.0" # from the stack's modules.repos airstack_compat key
+wiring: stacks/full_droan_cpu/wiring.md # docs site embeds the CI-generated wiring.md from trunk
+notes: >-
+ One of the presets absorbing trunk's local_*.launch.xml variant explosion
+ into named stacks a few include lines apart (absorbed the deleted
+ local_droan_cpu.launch.xml). Identical to full_default except the DROAN
+ include lines; pulls no external modules.
diff --git a/tests/meta/fixtures/modules_index/stacks/full_macvo.yaml b/tests/meta/fixtures/modules_index/stacks/full_macvo.yaml
new file mode 100644
index 000000000..a43ea90c3
--- /dev/null
+++ b/tests/meta/fixtures/modules_index/stacks/full_macvo.yaml
@@ -0,0 +1,15 @@
+# Registry entry for the full_macvo trunk reference stack (schema/stack-entry.schema.json).
+name: full_macvo
+repo: https://github.com/castacks/AirStack
+path: stacks/full_macvo
+description: >-
+ Full autonomy with MAC-VO learned stereo visual odometry as the disparity
+ source for the local planner (droan_gl consumes
+ /$ROBOT_NAME/perception/macvo/disparity)
+airstack_compat: ">=0.19.0-alpha.18 <0.21.0" # from the stack's modules.repos airstack_compat key
+wiring: stacks/full_macvo/wiring.md # docs site embeds the CI-generated wiring.md from trunk
+notes: >-
+ Requires the macvo module (registry: modules/macvo.yaml) — its modules.repos
+ pins asm_macvo, currently at a placeholder v0.1.0 tag pending the module's
+ first release (asm_macvo TRUNK_REMOVAL.md §0). One of the presets absorbing
+ trunk's local_*.launch.xml variant explosion into named stacks.
diff --git a/tests/meta/launch_lint_allowlist.txt b/tests/meta/launch_lint_allowlist.txt
index f1125c3aa..3c6548823 100644
--- a/tests/meta/launch_lint_allowlist.txt
+++ b/tests/meta/launch_lint_allowlist.txt
@@ -2,23 +2,19 @@
# (tests/meta/test_launch_single_locus.py — RFC #379 §4 single-locus rule).
#
# This list is FROZEN at the wrap-form baseline (P5-E1): it names every launch
-# file that carried remaps when the rule landed. It only shrinks — as E2/E3
-# flatten wiring into the stack entry files, delete the corresponding line.
-# A listed file that no longer contains a remap FAILS the lint until its line
-# is removed; adding a NEW line needs the same scrutiny as an RFC change.
+# file that carried remaps when the rule landed. It only shrinks — the legacy
+# AUTONOMY_ROLE layer bringups (local/perception/sensors/global/behavior
+# *.launch.xml) were flattened into the stack entry files and deleted, so
+# their lines are gone. A listed file that no longer contains a remap FAILS
+# the lint until its line is removed; adding a NEW line needs the same
+# scrutiny as an RFC change.
+#
+# What remains: a standalone utility, a vendored driver tree, a module launch
+# awaiting its canonical rewrite, and the interface safety boundary (wrapped
+# by design until RFC #380 Part 2).
#
# Paths are relative to the repo root, one per line; '#' starts a comment.
common/ros_packages/airstack_common/launch/playback.launch.xml
robot/docker/zed/ws/src/zed_wrapper/launch/zed_camera.launch.py
-robot/ros_ws/src/behavior/behavior_bringup/launch/behavior.launch.xml
-robot/ros_ws/src/global/global_bringup/launch/global.launch.xml
robot/ros_ws/src/global/planners/exploration/launch/exploration_launch.xml
-robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_behavior_launch.xml
-robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_global_launch.xml
-robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_interface_launch.xml
-robot/ros_ws/src/global/planners/exploration/launch/robot_launch_gazebo/gz_local_launch.xml
robot/ros_ws/src/interface/interface_bringup/launch/interface.launch.py
-robot/ros_ws/src/interface/px4_interface/launch/px4_interface.launch.xml
-robot/ros_ws/src/interface/robot_interface/launch/odometry_conversion.xml
-robot/ros_ws/src/local/local_bringup/launch/local.launch.xml
-robot/ros_ws/src/perception/perception_bringup/launch/perception.launch.xml
diff --git a/tests/meta/test_bridge_contract.py b/tests/meta/test_bridge_contract.py
index 45448e150..208a693c7 100644
--- a/tests/meta/test_bridge_contract.py
+++ b/tests/meta/test_bridge_contract.py
@@ -18,8 +18,9 @@
- **Determinism** — generation is a pure function of bridge.yaml: identical
inputs produce byte-identical router configs, with no timestamps and no
absolute paths.
-- **Router format** — output parses as YAML and follows the legacy
- ``onboard_local_offboard_global/config/dds_router.yaml`` conventions:
+- **Router format** — output parses as YAML and follows the shared
+ ``autonomy_bringup/config/dds_router.yaml`` conventions (inherited from the
+ removed legacy split's ``onboard_local_offboard_global`` router config):
``$(env ROBOT_NAME)`` interpolation, rt/ topics, rq/rr service pairs, the
five action sub-endpoints, participants on ``$(env ROS_DOMAIN_ID)`` /
``$(var gcs_domain)``.
diff --git a/tests/meta/test_docs_catalog_contract.py b/tests/meta/test_docs_catalog_contract.py
new file mode 100644
index 000000000..de9eb8daf
--- /dev/null
+++ b/tests/meta/test_docs_catalog_contract.py
@@ -0,0 +1,257 @@
+# Copyright (c) 2026 Carnegie Mellon University
+# MIT License - see LICENSE in the repository root for full text.
+"""Docs-catalog contract (RFC #379 §9).
+
+The marketplace catalog under ``docs/modules/`` is GENERATED by
+``tools/gen_docs_catalog.py`` from the ``airstack-modules-index`` registry and
+committed; the docs deploy workflows regenerate it against the live registry
+at build time. This contract pins the pieces that must stay true:
+
+* the generator is deterministic (two runs => byte-identical output);
+* ``--check`` (the CI drift mode) passes against the committed pages when run
+ from the registry snapshot fixture (``tests/meta/fixtures/modules_index/``,
+ a copy of the registry entries the committed pages were generated from);
+* the catalog table lists every registered module;
+* the new-developer walkthrough page exists and is reachable from the nav,
+ along with the Modules nav section;
+* the three docs deploy workflows parse as YAML and carry the module-docs
+ fetch step with per-clone failure isolation (an unreachable module repo
+ must never fail a docs deploy).
+"""
+import subprocess
+import sys
+from pathlib import Path
+
+import pytest
+import yaml
+
+from harness.discovery import TESTS_DIR
+
+pytestmark = pytest.mark.unit
+
+REPO = TESTS_DIR.parent
+GENERATOR = REPO / "tools" / "gen_docs_catalog.py"
+FIXTURE_INDEX = TESTS_DIR / "meta" / "fixtures" / "modules_index"
+CATALOG_DIR = REPO / "docs" / "modules"
+WALKTHROUGH = REPO / "docs" / "getting_started" / "modular_airstack.md"
+MKDOCS_YML = REPO / "mkdocs.yml"
+DEPLOY_WORKFLOWS = [
+ REPO / ".github" / "workflows" / name
+ for name in (
+ "deploy_docs_from_develop.yaml",
+ "deploy_docs_from_main.yaml",
+ "deploy_docs_from_release.yaml",
+ )
+]
+
+MODULE_NAMES = sorted(p.stem for p in (FIXTURE_INDEX / "modules").glob("*.yaml"))
+
+
+def _run_generator(*args: str) -> subprocess.CompletedProcess:
+ return subprocess.run(
+ [sys.executable, str(GENERATOR), *args],
+ capture_output=True,
+ text=True,
+ cwd=REPO,
+ )
+
+
+def _generate_into(out_dir: Path, tmp_path: Path) -> "dict[str, str]":
+ empty_modules = tmp_path / "no-fetched-modules"
+ result = _run_generator(
+ "--index", str(FIXTURE_INDEX),
+ "--out", str(out_dir),
+ "--modules-dir", str(empty_modules),
+ )
+ assert result.returncode == 0, (
+ f"generator failed:\nstdout: {result.stdout}\nstderr: {result.stderr}"
+ )
+ return {p.name: p.read_text() for p in sorted(out_dir.glob("*.md"))}
+
+
+# ------------------------------------------------------------ generator
+
+
+def test_fixture_snapshot_is_populated():
+ assert MODULE_NAMES, f"no registry snapshot under {FIXTURE_INDEX}/modules"
+ assert (FIXTURE_INDEX / "stacks").is_dir()
+
+
+def test_generator_is_deterministic(tmp_path):
+ first = _generate_into(tmp_path / "run1", tmp_path)
+ second = _generate_into(tmp_path / "run2", tmp_path)
+ assert first == second, "two generator runs produced different output"
+ assert set(first) == {"index.md", *(f"{n}.md" for n in MODULE_NAMES)}
+
+
+def test_check_mode_passes_against_committed_pages(tmp_path):
+ """CI drift style: committed docs/modules/ must match regeneration."""
+ empty_modules = tmp_path / "no-fetched-modules"
+ result = _run_generator(
+ "--index", str(FIXTURE_INDEX),
+ "--out", str(CATALOG_DIR),
+ "--modules-dir", str(empty_modules),
+ "--check",
+ )
+ assert result.returncode == 0, (
+ "committed docs/modules/ pages drift from regeneration — rerun\n"
+ " python3 tools/gen_docs_catalog.py --index "
+ "--modules-dir \n"
+ f"stderr:\n{result.stderr}"
+ )
+
+
+def test_check_mode_detects_drift(tmp_path):
+ out = tmp_path / "pages"
+ _generate_into(out, tmp_path)
+ (out / "index.md").write_text("tampered\n")
+ empty_modules = tmp_path / "no-fetched-modules"
+ result = _run_generator(
+ "--index", str(FIXTURE_INDEX),
+ "--out", str(out),
+ "--modules-dir", str(empty_modules),
+ "--check",
+ )
+ assert result.returncode != 0, "--check did not flag a tampered page"
+ assert "DRIFT" in result.stderr
+
+
+def test_catalog_lists_every_registered_module():
+ index_md = (CATALOG_DIR / "index.md").read_text()
+ for name in MODULE_NAMES:
+ assert f"[{name}]({name}.md)" in index_md, (
+ f"catalog table is missing module {name}"
+ )
+ assert (CATALOG_DIR / f"{name}.md").is_file(), (
+ f"missing per-module page docs/modules/{name}.md"
+ )
+
+
+def test_module_pages_carry_the_contracted_sections():
+ for name in MODULE_NAMES:
+ entry = yaml.safe_load(
+ (FIXTURE_INDEX / "modules" / f"{name}.yaml").read_text()
+ )
+ page = (CATALOG_DIR / f"{name}.md").read_text()
+ repo = entry["repo"].rstrip("/")
+ ref = entry["registered_ref"]
+ assert f"airstack module add {repo} --version {ref}" in page, (
+ f"{name}.md: install snippet missing or unpinned"
+ )
+ assert "DECLARED" in page and "VERIFIED" in page, (
+ f"{name}.md: declared-vs-verified compat note missing"
+ )
+ assert f"{repo}/blob/{ref}/README.md" in page, (
+ f"{name}.md: README link at the registered ref missing"
+ )
+
+
+# ----------------------------------------------------------- docs + nav
+
+
+def _load_mkdocs() -> dict:
+ """Parse mkdocs.yml, tolerating the !!python/name superfences tag."""
+
+ class Loader(yaml.SafeLoader):
+ pass
+
+ Loader.add_multi_constructor(
+ "tag:yaml.org,2002:python/name:", lambda loader, suffix, node: suffix
+ )
+ return yaml.load(MKDOCS_YML.read_text(), Loader=Loader)
+
+
+def _flatten_nav(nav) -> "list[str]":
+ flat = []
+ if isinstance(nav, str):
+ flat.append(nav)
+ elif isinstance(nav, list):
+ for item in nav:
+ flat.extend(_flatten_nav(item))
+ elif isinstance(nav, dict):
+ for value in nav.values():
+ flat.extend(_flatten_nav(value))
+ return flat
+
+
+def test_walkthrough_page_exists_and_is_in_nav():
+ assert WALKTHROUGH.is_file(), "docs/getting_started/modular_airstack.md missing"
+ nav_paths = _flatten_nav(_load_mkdocs()["nav"])
+ assert "docs/getting_started/modular_airstack.md" in nav_paths
+ index_md = (REPO / "docs" / "getting_started" / "index.md").read_text()
+ assert "modular_airstack.md" in index_md, (
+ "getting_started/index.md must link the walkthrough"
+ )
+
+
+def test_modules_nav_section():
+ config = _load_mkdocs()
+ nav_paths = _flatten_nav(config["nav"])
+ assert "docs/modules/index.md" in nav_paths, "catalog missing from nav"
+ for name in MODULE_NAMES:
+ assert f"docs/modules/{name}.md" in nav_paths, f"{name} page not in nav"
+ for stack_dir in sorted((REPO / "stacks").iterdir()):
+ if stack_dir.is_dir() and not stack_dir.name.startswith("."):
+ assert f"stacks/{stack_dir.name}/README.md" in nav_paths, (
+ f"reference stack {stack_dir.name} README not in nav"
+ )
+
+
+def test_fetched_module_checkouts_are_not_site_pages():
+ exclude = _load_mkdocs().get("exclude_docs", "")
+ assert "modules/**" in exclude, (
+ "mkdocs exclude_docs must exclude the fetched modules/ checkouts"
+ )
+
+
+# ------------------------------------------------------ deploy workflows
+
+
+@pytest.mark.parametrize(
+ "workflow", DEPLOY_WORKFLOWS, ids=lambda p: p.name
+)
+def test_deploy_workflow_fetches_module_docs(workflow):
+ data = yaml.safe_load(workflow.read_text())
+ assert isinstance(data, dict), f"{workflow.name} did not parse to a mapping"
+
+ steps = data["jobs"]["deploy"]["steps"]
+ fetch = [
+ s for s in steps
+ if "registry index" in str(s.get("name", "")).lower()
+ ]
+ assert fetch, f"{workflow.name}: no registry/module-docs fetch step"
+ script = fetch[0]["run"]
+ assert "airstack-modules-index" in script
+ assert "gen_docs_catalog.py" in script, (
+ f"{workflow.name}: fetch step must regenerate docs/modules/"
+ )
+ # Failure isolation (RFC #379 §9): the registry clone, every per-module
+ # clone, and the regeneration itself are all wrapped so an unreachable
+ # repo degrades to committed pages / stub notes instead of a red deploy.
+ assert script.count("|| echo") + script.count('echo "skipped') >= 2
+ assert "skipped" in script
+ # The fetch step must run before mike deploys the site.
+ step_names = [str(s.get("name", "")) for s in steps]
+ fetch_idx = step_names.index(str(fetch[0]["name"]))
+ build_idx = next(
+ i for i, s in enumerate(steps) if "mike deploy" in str(s.get("run", ""))
+ )
+ assert fetch_idx < build_idx, f"{workflow.name}: fetch step must precede mike deploy"
+
+
+def test_develop_workflow_freshness_triggers():
+ develop = yaml.safe_load(DEPLOY_WORKFLOWS[0].read_text())
+ triggers = develop.get("on") or develop.get(True)
+ assert "workflow_dispatch" in triggers
+ assert "schedule" in triggers, "weekly freshness rebuild missing"
+ for path in ("stacks/**", "tools/gen_docs_catalog.py"):
+ assert path in triggers["push"]["paths"], (
+ f"develop docs deploy must trigger on {path}"
+ )
+
+
+def test_main_workflow_paths_extended():
+ main = yaml.safe_load(DEPLOY_WORKFLOWS[1].read_text())
+ triggers = main.get("on") or main.get(True)
+ for path in ("stacks/**", "tools/gen_docs_catalog.py"):
+ assert path in triggers["push"]["paths"]
diff --git a/tests/meta/test_launch_intent_contract.py b/tests/meta/test_launch_intent_contract.py
index afc7a4686..b9fb6f4d8 100644
--- a/tests/meta/test_launch_intent_contract.py
+++ b/tests/meta/test_launch_intent_contract.py
@@ -31,7 +31,10 @@
def run_up_dry(*flags, env=None, check=True):
"""Run `airstack up --dry-run `; return (exit_code, stdout+stderr, config_dict)."""
- full_env = {**os.environ, **(env or {})}
+ # Scrub AUTONOMY_ROLE from the invoking shell by default: it was removed
+ # (preflight hard-errors on it) and must only be set by tests that pin
+ # exactly that error.
+ full_env = {**os.environ, "AUTONOMY_ROLE": "", **(env or {})}
result = subprocess.run(
[AIRSTACK, "up", "--dry-run", *flags],
capture_output=True, text=True, cwd=str(REPO), env=full_env, timeout=120,
@@ -203,44 +206,52 @@ def test_stack_missing_entry_is_fatal():
assert "onboard.launch.xml" in out
-def test_no_stack_leaves_stack_vars_empty():
- """Legacy path: no --stack → both stack vars empty in effective config."""
+def test_no_stack_defaults_to_full_default():
+ """Stacks are the only dispatch: no --stack (and no stack env) → the
+ effective config names the trunk reference stack full_default."""
_, _, cfg = run_up_dry(
"--sim", "isaac",
# Scrub any stack vars inherited from the invoking shell.
env={"AIRSTACK_STACK_DIR": "", "AIRSTACK_STACK_ENTRY": ""},
)
- assert cfg.get("AIRSTACK_STACK_DIR", "") == ""
+ assert cfg["AIRSTACK_STACK_DIR"] == "/root/AirStack/stacks/full_default"
+ assert cfg["AIRSTACK_STACK_ENTRY"] == "stack"
-def test_autonomy_role_without_stack_warns_deprecation():
- """AUTONOMY_ROLE explicitly set (env/--env-file) + no stack → one
- deprecation warning pointing at --stack."""
- _, out, _ = run_up_dry(
+def test_autonomy_role_set_is_fatal():
+ """AUTONOMY_ROLE was removed — an explicitly set value (env / --env-file /
+ .env) hard-fails preflight with the removal message."""
+ code, out, _ = run_up_dry(
"--sim", "isaac",
env={"AUTONOMY_ROLE": "full", "AIRSTACK_STACK_DIR": ""},
+ check=False,
)
- assert "legacy dispatch" in out
- assert "--stack full_default" in out
+ assert code != 0
+ assert "AUTONOMY_ROLE was removed" in out
+ assert "--stack" in out
-def test_no_deprecation_warning_without_explicit_role():
- """The compose files' own ${AUTONOMY_ROLE:-full} default must NOT trip the
- warning — only an explicit env / --env-file / .env value does."""
- _, out, _ = run_up_dry(
+def test_empty_autonomy_role_does_not_trip_removal_error():
+ """Only an explicitly SET value trips the removal error — an empty/unset
+ AUTONOMY_ROLE must pass."""
+ code, out, _ = run_up_dry(
"--sim", "isaac",
env={"AUTONOMY_ROLE": "", "AIRSTACK_STACK_DIR": ""},
)
- assert "legacy dispatch" not in out
+ assert code == 0
+ assert "AUTONOMY_ROLE was removed" not in out
-def test_stack_and_role_both_set_warns_stack_wins():
- _, out, _ = run_up_dry(
+def test_autonomy_role_fatal_even_with_stack_selected():
+ """The removal error is unconditional: a stale AUTONOMY_ROLE next to a
+ valid --stack still fails (no silent 'stack wins' anymore)."""
+ code, out, _ = run_up_dry(
"--sim", "isaac", "--stack", "full_default",
env={"AUTONOMY_ROLE": "full"},
+ check=False,
)
- assert "stack wins" in out
- assert "legacy dispatch" not in out
+ assert code != 0
+ assert "AUTONOMY_ROLE was removed" in out
# ── override-file golden equivalence (RFC #380 P6, deliverable 8) ───────────
@@ -248,12 +259,18 @@ def test_stack_and_role_both_set_warns_stack_wins():
# `up --dry-run` unchanged as the fleet/stack machinery lands on top of them.
def test_override_ms_airsim_env_still_derives_expected_config():
- code, out, cfg = run_up_dry("--env-file", "overrides/ms-airsim.env")
+ code, out, cfg = run_up_dry(
+ "--env-file", "overrides/ms-airsim.env",
+ # Scrub stack/fleet vars a developer shell might carry.
+ env={"AIRSTACK_STACK_DIR": "", "FLEET_CONFIG_FILE": ""},
+ )
assert code == 0, out
profiles = cfg["COMPOSE_PROFILES"].split(",")
assert "ms-airsim" in profiles and "desktop" in profiles
assert "isaac-sim" not in profiles
assert cfg["URDF_FILE"].endswith("iris_stereo.ms-airsim.urdf")
+ # a sim override selects no stack of its own → the full_default default
+ assert cfg["AIRSTACK_STACK_DIR"] == "/root/AirStack/stacks/full_default"
# sim/hardware override files never opt into fleets on their own
assert "FLEET_CONFIG_FILE" not in cfg
@@ -269,5 +286,7 @@ def test_override_l4t_px4_realrobot_env_still_derives_expected_config():
assert cfg["NUM_ROBOTS"] == "1"
assert cfg["URDF_FILE"].endswith("iris_with_sensors.pegasus.robot.urdf")
assert "FLEET_CONFIG_FILE" not in cfg
- # its explicit AUTONOMY_ROLE still draws (only) the deprecation courtesy
- assert "legacy dispatch" in out
+ # the override file is stack-form now (AUTONOMY_ROLE was removed): it
+ # pins the full stack explicitly and must not draw the removal error
+ assert cfg["AIRSTACK_STACK_DIR"] == "/root/AirStack/stacks/full_default"
+ assert "AUTONOMY_ROLE was removed" not in out
diff --git a/tests/system/test_wiring_snapshot.py b/tests/system/test_wiring_snapshot.py
index 1a78391e2..efd934b75 100644
--- a/tests/system/test_wiring_snapshot.py
+++ b/tests/system/test_wiring_snapshot.py
@@ -7,19 +7,17 @@
(node/edge names keep their robot namespaces), and renders it to
``/wiring/observed_full_default.md`` via ``tests/wiring_snapshot.py``.
-Golden logic: if ``tests/goldens/wiring/full_default..robot.md`` is
-missing, the test logs a bootstrap instruction and PASSES (goldens are
-committed from a validated local run — see ``tests/goldens/wiring/README.md``);
-if present, ``diff_graphs`` must report identical or the test fails with the
-JSON drift verdict.
-
-Stack mode (RFC #379 §3/§4): with ``--stack `` the fixture launches the
-stack's entry launch file and the golden becomes ``stacks//wiring.md``
-(one observed-wiring document per stack, committed in the stack folder). The
-observed snapshot is written as ``observed_.md`` and the same
-bootstrap-pass + INSTRUCTION behavior applies when the stack has no wiring.md
-yet. Legacy runs (no ``--stack``) keep the tests/goldens/wiring path
-unchanged.
+Golden logic (RFC #379 §3/§4): every run compares against the launched
+stack's committed wiring baseline, ``stacks//wiring.md`` (one
+observed-wiring document per stack, committed in the stack folder). No
+``--stack`` means the default dispatch — stacks/full_default (stacks are the
+only dispatch; the legacy AUTONOMY_ROLE path was removed) — so the default
+golden is ``stacks/full_default/wiring.md``. If the stack has no wiring.md
+yet, the test logs a bootstrap INSTRUCTION and PASSES (baselines are
+committed from a validated run — mechanism documented in
+docs/development/stacks.md); if present, ``diff_graphs`` must report
+identical or the test fails with the JSON drift verdict. The observed
+snapshot is written as ``observed_.md`` under the run dir.
"""
import json
import os
@@ -244,11 +242,9 @@ def test_wiring_matches_golden(self, airstack_env):
m = get_metrics()
tid = current_test_id()
- # Legacy (no --stack) keeps the full_default naming and the
- # tests/goldens/wiring golden; a selected stack owns its golden at
- # stacks//wiring.md.
- stack = airstack_env.get("stack")
- stack_name = stack or "full_default"
+ # Each stack owns its golden at stacks//wiring.md. No --stack =
+ # the default dispatch, stacks/full_default.
+ stack_name = airstack_env.get("stack") or "full_default"
_wait_for_settled_node_sets(airstack_env)
t0 = time.time()
@@ -279,18 +275,12 @@ def test_wiring_matches_golden(self, airstack_env):
m.record(tid, "wiring_topic_count", len(graph["topics"]),
unit="count", direction="higher_is_better")
- if stack:
- golden_path = repo_path("stacks", stack, "wiring.md")
- else:
- golden_path = repo_path(
- "tests", "goldens", "wiring",
- f"full_default.{sim}.{num_robots}robot.md",
- )
+ golden_path = repo_path("stacks", stack_name, "wiring.md")
if not golden_path.exists():
logger.info(
"INSTRUCTION: no golden at %s — bootstrap by validating the "
"observed snapshot (%s) and copying it to that path, then "
- "commit it (see tests/goldens/wiring/README.md).",
+ "commit it (mechanism: docs/development/stacks.md).",
golden_path, observed_path,
)
return
diff --git a/tools/fleet/generate_fleet_compose.py b/tools/fleet/generate_fleet_compose.py
index c1e4e2bcd..d3c3235ce 100755
--- a/tools/fleet/generate_fleet_compose.py
+++ b/tools/fleet/generate_fleet_compose.py
@@ -73,8 +73,7 @@
"""
# The robot-desktop bring-up command, copied verbatim (docker-compose.yaml is
-# the source of truth; the AUTONOMY_ROLE arg is ignored whenever a stack dir
-# is set — every fleet service sets one).
+# the source of truth; every fleet service sets an explicit stack dir/entry).
ROBOT_COMMAND = (
"bash -c \" "
"if [ -z \\\"$$DISPLAY\\\" ] && command -v Xvfb >/dev/null 2>&1; then "
@@ -85,7 +84,7 @@
"service ssh restart; "
"tmux new -d -s bringup; "
"if [ $$AUTOLAUNCH == 'true' ]; then "
- "tmux send-keys -t bringup:0.0 'bws && sws && ros2 launch $$LAUNCH_PACKAGE robot.launch.xml role:=$$AUTONOMY_ROLE' ENTER; "
+ "tmux send-keys -t bringup:0.0 'bws && sws && ros2 launch $$LAUNCH_PACKAGE robot.launch.xml' ENTER; "
"fi; "
"sleep infinity\""
)
@@ -135,10 +134,6 @@ def _common_environment():
"AUTOLAUNCH=${AUTOLAUNCH:-true}",
"NVIDIA_DRIVER_CAPABILITIES=all",
"SIM_IP=${SIM_IP:-172.31.0.200}",
- # role is ignored whenever a stack dir is set (robot.launch.xml); kept
- # non-empty so the shared bring-up command's role:=$AUTONOMY_ROLE arg
- # stays well-formed.
- "AUTONOMY_ROLE=full",
]
diff --git a/tools/gen_dds_router.py b/tools/gen_dds_router.py
index f4a568300..a88f63954 100644
--- a/tools/gen_dds_router.py
+++ b/tools/gen_dds_router.py
@@ -6,8 +6,9 @@
A split stack carries one launch entry point per host role plus a ``bridge.yaml``
explicitly listing every topic/service/action crossing the machine boundary.
That list is authoritative and human-readable; this tool derives the eProsima
-DDS Router allowlist config from it — the same format as
-``autonomy_bringup/onboard_local_offboard_global/config/dds_router.yaml`` and
+DDS Router allowlist config from it — the same format as the shared
+``autonomy_bringup/config/dds_router.yaml`` (inherited from the removed
+legacy split's router config) and
consumed by the same ``interpolate_dds_router.launch.py`` (``$(env ROBOT_NAME)``
/ ``$(var gcs_domain)`` tokens are resolved at launch, per-robot).
@@ -190,8 +191,8 @@ def _allowlist_lines(kind, name):
"""DDS endpoint allowlist entries for one bridge entry (relative name).
Topic prefixes per the DDS/ROS 2 mapping documented in
- onboard_all/config/dds_router.yaml: rt/ topics, rq/…Request + rr/…Reply
- service pairs, and the five action sub-endpoints.
+ autonomy_bringup/config/dds_router.yaml: rt/ topics, rq/…Request +
+ rr/…Reply service pairs, and the five action sub-endpoints.
"""
ns = "$(env ROBOT_NAME)"
if kind == "topic":
@@ -213,7 +214,7 @@ def render_router_config(data, source_rel="bridge.yaml"):
Deterministic: pure function of the input (entries grouped by direction in
input order; no timestamps, no absolute paths). The output format mirrors
- autonomy_bringup/onboard_local_offboard_global/config/dds_router.yaml —
+ autonomy_bringup/config/dds_router.yaml —
participants on $(env ROS_DOMAIN_ID) / $(var gcs_domain), an rt/rq/rr
allowlist with $(env ROBOT_NAME) interpolation — so the same
interpolate_dds_router.launch.py consumes it unchanged.
diff --git a/tools/gen_docs_catalog.py b/tools/gen_docs_catalog.py
new file mode 100644
index 000000000..4321f3eb2
--- /dev/null
+++ b/tools/gen_docs_catalog.py
@@ -0,0 +1,457 @@
+#!/usr/bin/env python3
+# Copyright (c) 2026 Carnegie Mellon University
+# MIT License - see LICENSE in the repository root for full text.
+"""Generate the docs-site module/stack catalog from the registry index.
+
+RFC #379 §9 ("Docs"): the main site owns an auto-generated marketplace
+catalog rendered from the `airstack-modules-index` registry repo
+(https://github.com/castacks/airstack-modules-index). This script reads a
+LOCAL CHECKOUT of that registry (the docs deploy workflows shallow-clone it;
+developers point at any clone) and emits deterministic Markdown pages under
+``docs/modules/``:
+
+* ``docs/modules/index.md`` — the catalog: one table row per registered
+ module (name, description, type, maintainer, DECLARED compat, links) and
+ one per registered stack.
+* ``docs/modules/.md`` — one page per module: description, install
+ snippet, maintainer, DECLARED-vs-VERIFIED compatibility note pointing at
+ the registry's ``compat/`` matrix, and a link to the module README on
+ GitHub at the registered ref.
+
+The pages are committed so the site never depends on registry availability;
+the deploy workflows regenerate them against the live registry at build time
+(failure-isolated: an unreachable registry or module repo falls back to the
+committed pages / a stub note — RFC #379 §9).
+
+Determinism contract: byte-identical output for identical inputs (registry
+checkout + trunk tree + fetched-modules dir). No timestamps, no environment
+leakage. ``--check`` regenerates into a temp dir and diffs against the
+committed pages (CI drift style; exit 1 on drift).
+
+stdlib + PyYAML only.
+"""
+from __future__ import annotations
+
+import argparse
+import difflib
+import re
+import sys
+import tempfile
+from pathlib import Path
+
+import yaml
+
+TRUNK = Path(__file__).resolve().parent.parent
+REGISTRY_URL = "https://github.com/castacks/airstack-modules-index"
+
+GENERATED_MARKER = (
+ ""
+)
+
+
+# ---------------------------------------------------------------- helpers
+
+
+def _die(msg: str) -> "NoReturn": # noqa: F821 (py<3.11 typing)
+ print(f"gen_docs_catalog: error: {msg}", file=sys.stderr)
+ sys.exit(2)
+
+
+def _load_yaml(path: Path) -> dict:
+ data = yaml.safe_load(path.read_text())
+ if not isinstance(data, dict):
+ _die(f"{path} did not parse to a YAML mapping")
+ return data
+
+
+def _load_entries(directory: Path) -> "list[dict]":
+ """Load all registry entries in a directory, sorted by name."""
+ entries = []
+ if not directory.is_dir():
+ return entries
+ for path in sorted(directory.glob("*.yaml")):
+ entry = _load_yaml(path)
+ entry.setdefault("name", path.stem)
+ entries.append(entry)
+ entries.sort(key=lambda e: e["name"])
+ return entries
+
+
+def _norm_repo_url(url: str) -> str:
+ """Normalize a git URL for equality checks (ssh/https, .git suffix)."""
+ url = url.strip()
+ m = re.match(r"^git@([^:]+):(.+)$", url)
+ if m:
+ url = f"https://{m.group(1)}/{m.group(2)}"
+ if url.endswith(".git"):
+ url = url[:-4]
+ return url.rstrip("/")
+
+
+def _repo_slug(url: str) -> str:
+ """castacks/asm_optitrack from any GitHub URL form; else the URL."""
+ norm = _norm_repo_url(url)
+ m = re.match(r"^https://github\.com/(.+)$", norm)
+ return m.group(1) if m else norm
+
+
+def _short_ref(ref: str) -> str:
+ return ref[:12] if re.fullmatch(r"[0-9a-f]{40}", ref) else ref
+
+
+def _md_cell(text: str) -> str:
+ """Collapse whitespace and escape pipes for a Markdown table cell."""
+ return " ".join(str(text).split()).replace("|", "\\|")
+
+
+def _stack_pins(stack_dir: Path) -> "list[str]":
+ """Normalized module-repo URLs pinned by a trunk stack's modules.repos."""
+ repos_file = stack_dir / "modules.repos"
+ if not repos_file.is_file():
+ return []
+ try:
+ data = yaml.safe_load(repos_file.read_text()) or {}
+ except yaml.YAMLError:
+ return []
+ repositories = data.get("repositories") or {}
+ if not isinstance(repositories, dict):
+ return []
+ return sorted(
+ _norm_repo_url(str(spec.get("url", "")))
+ for spec in repositories.values()
+ if isinstance(spec, dict) and spec.get("url")
+ )
+
+
+def _stacks_using(module: dict, trunk: Path, stack_entries: "list[dict]") -> "list[str]":
+ """Registered trunk stacks whose modules.repos pin this module's repo."""
+ target = _norm_repo_url(module.get("repo", ""))
+ users = []
+ for stack in stack_entries:
+ if _repo_slug(stack.get("repo", "")) != "castacks/AirStack":
+ continue
+ stack_dir = trunk / stack.get("path", f"stacks/{stack['name']}")
+ if target and target in _stack_pins(stack_dir):
+ users.append(stack["name"])
+ return sorted(users)
+
+
+# ------------------------------------------------------------- rendering
+
+
+def _stack_link(stack: dict, trunk: Path, from_depth: int = 2) -> str:
+ """Markdown link to a stack's README (relative for trunk stacks)."""
+ name = stack["name"]
+ rel_readme = Path(stack.get("path", f"stacks/{name}")) / "README.md"
+ if (
+ _repo_slug(stack.get("repo", "")) == "castacks/AirStack"
+ and (trunk / rel_readme).is_file()
+ ):
+ return f"[{name}]({'../' * from_depth}{rel_readme.as_posix()})"
+ return f"[{name}]({_norm_repo_url(stack.get('repo', ''))})"
+
+
+def render_index(
+ modules: "list[dict]",
+ stacks: "list[dict]",
+ trunk: Path,
+) -> str:
+ lines = [
+ "# Module & Stack Catalog",
+ "",
+ GENERATED_MARKER,
+ "",
+ "The **marketplace catalog** of registered AirStack modules and stacks",
+ f"([RFC #379 §7/§9](https://github.com/castacks/AirStack/discussions/379)), rendered from the",
+ f"[airstack-modules-index]({REGISTRY_URL}) registry — one YAML entry per",
+ "module or stack, rosdistro-style. Getting listed = a PR to the registry",
+ f"(see the [registry README]({REGISTRY_URL}#how-to-register-a-module)).",
+ "",
+ "Compatibility shown here is the author-**DECLARED** semver range; the",
+ f"**VERIFIED** matrix is CI-stamped into the registry's [compat/]({REGISTRY_URL}/tree/main/compat)",
+ "directory and is never hand-edited.",
+ "",
+ "## Registered modules",
+ "",
+ "| Module | Description | Type | Maintainer | Declared compat | Links |",
+ "|--------|-------------|------|------------|-----------------|-------|",
+ ]
+ for mod in modules:
+ name = mod["name"]
+ repo = _norm_repo_url(mod.get("repo", ""))
+ users = _stacks_using(mod, trunk, stacks)
+ links = [f"[repo]({repo})"]
+ links += [
+ f"[{u}](../../stacks/{u}/README.md)"
+ for u in users
+ if (trunk / "stacks" / u / "README.md").is_file()
+ ]
+ lines.append(
+ "| [{n}]({n}.md) | {d} | `{t}` | {m} | `{c}` | {l} |".format(
+ n=name,
+ d=_md_cell(mod.get("description", "")),
+ t=mod.get("type", "?"),
+ m=_md_cell(mod.get("maintainer", "?")),
+ c=_md_cell(mod.get("airstack_compat", "?")),
+ l=" · ".join(links),
+ )
+ )
+ lines += [
+ "",
+ "## Registered stacks",
+ "",
+ "A stack is a self-contained topology folder; its pinned `modules.repos`",
+ "*is* a tested-together release set ([RFC #379 §3](https://github.com/castacks/AirStack/discussions/379)).",
+ "Trunk's reference stacks below are also in the site nav under",
+ "**Modules → Reference Stacks**.",
+ "",
+ "| Stack | Description | Declared compat | Wiring | Registry entry |",
+ "|-------|-------------|-----------------|--------|----------------|",
+ ]
+ for stack in stacks:
+ name = stack["name"]
+ wiring_rel = Path(stack.get("path", f"stacks/{name}")) / "wiring.md"
+ if (
+ _repo_slug(stack.get("repo", "")) == "castacks/AirStack"
+ and (trunk / wiring_rel).is_file()
+ ):
+ wiring = f"[wiring.md](../../{wiring_rel.as_posix()})"
+ else:
+ wiring = "*not committed yet*"
+ lines.append(
+ "| {s} | {d} | `{c}` | {w} | [{n}.yaml]({u}/blob/main/stacks/{n}.yaml) |".format(
+ s=_stack_link(stack, trunk),
+ d=_md_cell(stack.get("description", "")),
+ c=_md_cell(stack.get("airstack_compat", "?")),
+ w=wiring,
+ n=name,
+ u=REGISTRY_URL,
+ )
+ )
+ lines += [
+ "",
+ "## See also",
+ "",
+ "- [Modular AirStack walkthrough](../getting_started/modular_airstack.md) — the",
+ " new-developer journey: reference stack → add a module → own stack → fleet",
+ "- [AirStack Modules](../development/modules.md) — `airstack module` CLI, the",
+ " pinning rule, hooks, and the overlay",
+ "- [AirStack Stacks](../development/stacks.md) — stack anatomy, `stack new|diff`,",
+ " wiring snapshots, `doctor`",
+ "- [AirStack Fleets](../development/fleets.md) — fleet files composing stacks",
+ " into deployments (RFC #380)",
+ "- [Module CI](../development/module_ci.md) — the reusable system-test workflow",
+ " module repos call; how compat badges are earned",
+ "- [Interface Conventions Spec](../robot/autonomy/interface_conventions.md) —",
+ " the canonical names/types/QoS modules default to",
+ "",
+ ]
+ return "\n".join(lines)
+
+
+def render_module_page(
+ mod: dict,
+ trunk: Path,
+ stacks: "list[dict]",
+ modules_dir: Path,
+) -> str:
+ name = mod["name"]
+ repo = _norm_repo_url(mod.get("repo", ""))
+ ref = str(mod.get("registered_ref", "main"))
+ users = _stacks_using(mod, trunk, stacks)
+ fetched = (modules_dir / name / "README.md").is_file()
+
+ lines = [
+ f"# {name}",
+ "",
+ GENERATED_MARKER,
+ "",
+ f"> {' '.join(str(mod.get('description', '')).split())}",
+ "",
+ "| | |",
+ "|---|---|",
+ f"| Repository | [{_repo_slug(repo)}]({repo}) |",
+ f"| Type | `{mod.get('type', '?')}` |",
+ f"| Maintainer | {_md_cell(mod.get('maintainer', '?'))} |",
+ f"| License | {_md_cell(mod.get('license', '?'))} |",
+ f"| Registered ref | [`{_short_ref(ref)}`]({repo}/tree/{ref}) |",
+ f"| Declared compat | `{_md_cell(mod.get('airstack_compat', '?'))}` |",
+ f"| Registry entry | [modules/{name}.yaml]({REGISTRY_URL}/blob/main/modules/{name}.yaml) |",
+ "",
+ "## Install",
+ "",
+ "From an AirStack checkout ([AirStack Modules guide](../development/modules.md)):",
+ "",
+ "```bash",
+ f"airstack module add {repo} --version {ref}",
+ "airstack up -f .airstack/generated/docker-compose.modules.yaml",
+ "```",
+ "",
+ "`module add` pins the module in `modules.repos` and syncs it into the",
+ "gitignored `modules/` overlay; the generated compose file mounts it into",
+ "the containers.",
+ "",
+ "## Compatibility: declared vs verified",
+ "",
+ f"The range `{_md_cell(mod.get('airstack_compat', '?'))}` is **DECLARED** by the module author",
+ "(copied from the module's `module.yaml`). The **VERIFIED** record — rows",
+ "stamped exclusively by CI runs of the reusable",
+ "[module-system-tests workflow](../development/module_ci.md) — lives in the",
+ f"registry's [compat/ matrix]({REGISTRY_URL}/tree/main/compat)",
+ f"([compat/{name}.yaml]({REGISTRY_URL}/blob/main/compat/{name}.yaml) once stamped).",
+ "A compatibility claim that isn't CI-verified rots (RFC #379 §5): trust the",
+ "matrix, read the declaration as intent.",
+ "",
+ "## Documentation",
+ "",
+ f"- [Module README on GitHub @ `{_short_ref(ref)}`]({repo}/blob/{ref}/README.md)",
+ ]
+ if fetched:
+ lines += [
+ f"- A snapshot of the module repo was fetched into `modules/{name}/` when",
+ " this page was generated (docs deploy fetch step).",
+ ]
+ else:
+ lines += [
+ f"- *The module repo was not fetched when this page was generated — the*",
+ " *links above go to GitHub at the registered ref (RFC #379 §9 failure*",
+ " *isolation: an unreachable module repo never fails the docs deploy).*",
+ ]
+ lines += [
+ "",
+ "## Registered stacks using this module",
+ "",
+ ]
+ if users:
+ lines += [
+ f"- [{u}](../../stacks/{u}/README.md)"
+ for u in users
+ if (trunk / "stacks" / u / "README.md").is_file()
+ ]
+ else:
+ lines.append(
+ "- None yet. Any stack can pin it in its `modules.repos` "
+ "([AirStack Stacks](../development/stacks.md))."
+ )
+ notes = str(mod.get("notes", "")).strip()
+ if notes:
+ lines += ["", "## Registry notes", ""]
+ lines += [f"> {ln}".rstrip() for ln in " ".join(notes.split()).splitlines()]
+ lines.append("")
+ return "\n".join(lines)
+
+
+# ------------------------------------------------------------------ main
+
+
+def generate(index: Path, out: Path, trunk: Path, modules_dir: Path) -> "dict[str, str]":
+ modules = _load_entries(index / "modules")
+ stacks = _load_entries(index / "stacks")
+ if not modules:
+ _die(f"no module entries found under {index / 'modules'}")
+ pages = {"index.md": render_index(modules, stacks, trunk)}
+ for mod in modules:
+ pages[f"{mod['name']}.md"] = render_module_page(mod, trunk, stacks, modules_dir)
+ return pages
+
+
+def write_pages(pages: "dict[str, str]", out: Path) -> None:
+ out.mkdir(parents=True, exist_ok=True)
+ for rel, content in sorted(pages.items()):
+ (out / rel).write_text(content)
+
+
+def check_pages(pages: "dict[str, str]", out: Path) -> int:
+ """CI drift check: committed pages must match regeneration. 0 = clean."""
+ drift = 0
+ committed = {p.name for p in out.glob("*.md")} if out.is_dir() else set()
+ for rel in sorted(set(pages) | committed):
+ want = pages.get(rel)
+ have_path = out / rel
+ have = have_path.read_text() if have_path.is_file() else None
+ if want == have:
+ continue
+ drift = 1
+ if want is None:
+ print(f"DRIFT: {have_path} is committed but no longer generated", file=sys.stderr)
+ elif have is None:
+ print(f"DRIFT: {have_path} is generated but not committed", file=sys.stderr)
+ else:
+ print(f"DRIFT: {have_path} differs from regeneration:", file=sys.stderr)
+ diff = difflib.unified_diff(
+ have.splitlines(), want.splitlines(),
+ fromfile=f"committed/{rel}", tofile=f"generated/{rel}", lineterm="",
+ )
+ for line in list(diff)[:40]:
+ print(f" {line}", file=sys.stderr)
+ if drift:
+ print(
+ "gen_docs_catalog --check: docs/modules/ pages are stale — rerun\n"
+ " python3 tools/gen_docs_catalog.py --index \n"
+ "and commit the result.",
+ file=sys.stderr,
+ )
+ return drift
+
+
+def main(argv: "list[str] | None" = None) -> int:
+ parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
+ parser.add_argument(
+ "--index", required=True, type=Path,
+ help="local checkout of castacks/airstack-modules-index",
+ )
+ parser.add_argument(
+ "--out", type=Path, default=None,
+ help="output directory (default: /docs/modules)",
+ )
+ parser.add_argument(
+ "--trunk", type=Path, default=TRUNK,
+ help="AirStack checkout root (default: this script's repo)",
+ )
+ parser.add_argument(
+ "--modules-dir", type=Path, default=None,
+ help="dir of fetched module repos, modules// "
+ "(default: /modules; absence per module => stub note)",
+ )
+ parser.add_argument(
+ "--check", action="store_true",
+ help="verify committed pages match regeneration; exit 1 on drift",
+ )
+ parser.add_argument(
+ "--list-refs", action="store_true",
+ help="print 'namereporegistered_ref' per module and exit "
+ "(used by the docs deploy workflows' fetch loop)",
+ )
+ args = parser.parse_args(argv)
+
+ trunk = args.trunk.resolve()
+ index = args.index.resolve()
+ if not (index / "modules").is_dir():
+ _die(f"{index} does not look like a registry checkout (no modules/ dir)")
+ out = (args.out if args.out is not None else trunk / "docs" / "modules").resolve()
+ modules_dir = (
+ args.modules_dir if args.modules_dir is not None else trunk / "modules"
+ ).resolve()
+
+ if args.list_refs:
+ for mod in _load_entries(index / "modules"):
+ print(
+ f"{mod['name']}\t{_norm_repo_url(mod.get('repo', ''))}"
+ f"\t{mod.get('registered_ref', 'main')}"
+ )
+ return 0
+
+ pages = generate(index, out, trunk, modules_dir)
+ if args.check:
+ return check_pages(pages, out)
+ write_pages(pages, out)
+ print(f"gen_docs_catalog: wrote {len(pages)} pages to {out}")
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())