Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
480f7ae
Add fixed-trajectory evaluation tests
pvkumara5 Apr 27, 2026
b616dbf
Remove module docstring from test_fixed_trajectory.py
pvkumara5 Apr 30, 2026
d06effb
Aj/GitHub ci cd (#347)
andrewjong Apr 27, 2026
ee99e88
Add fix for boot volume size blocking orchestrator
andrewjong Apr 27, 2026
78f50e7
Add floating IPs to CI/CD
andrewjong Apr 28, 2026
d22aa32
Bump gh runner_version to latest
andrewjong Apr 28, 2026
a74b591
Update cicd defaults
andrewjong Apr 28, 2026
0a2c7c8
Rename integration-tests.yml to system-tests.yml
andrewjong Apr 28, 2026
862e12b
Add debugging tips and add to mkdocs
andrewjong Apr 28, 2026
6b70d16
Use venv instead of pip3 to fix error: externally-managed-environment
andrewjong Apr 28, 2026
504644c
Explicitly fail autonomy test if images not yet built
andrewjong Apr 28, 2026
8d200d2
Enable using docker cache from docker registry to speed up docker ima…
andrewjong Apr 28, 2026
cbe56f9
Fix bug
andrewjong Apr 28, 2026
5d494e3
Update docs and change docker image build/push to also run on self-ho…
andrewjong Apr 28, 2026
144a7fd
Enable trigger docker build workflow on via manual dispatch
andrewjong Apr 28, 2026
4a5f184
Increase instance volume size so that space doesn't run out when buil…
andrewjong Apr 28, 2026
7512e5b
Update to always try build all images
andrewjong Apr 28, 2026
f27e432
Create dummy file for docker compose push to pass
andrewjong Apr 28, 2026
be68158
Add omni_pass.env with guest access to AirLab nucleus
andrewjong Apr 28, 2026
5cd5f7e
Update ci/cd tests to make sure image is present before running tests
andrewjong Apr 28, 2026
70e1584
Make sure images for profiles get built
andrewjong Apr 29, 2026
073aa69
Update system tests to not build images if pull available
andrewjong Apr 29, 2026
c70e7fd
Make build/pull quiet
andrewjong Apr 29, 2026
3aa9326
Pin empy version to fix ROS2 jazzy version bug
andrewjong Apr 29, 2026
3bbd970
Switch image to desktop so that tests run successfully
andrewjong Apr 29, 2026
bca80ef
Add docker image signing to workflow
andrewjong Apr 29, 2026
53b1286
Change pytest mark 'autonomy' to 'takeoff_hover_land'
andrewjong Apr 29, 2026
2e0130b
update comments on workflow
andrewjong Apr 29, 2026
6fa3843
Recurisve checkout of airstack
andrewjong Apr 29, 2026
622e510
Log more to GitHub
andrewjong Apr 29, 2026
2f6b43f
Better error logging for ci/cd orchestrator
andrewjong Apr 30, 2026
2bb8de0
Add check system resources before spawning server; if resources not a…
andrewjong Apr 30, 2026
7197a15
Make it so that pytest no longer triggers from pushes on PR; make it …
andrewjong Apr 30, 2026
8e12672
Update PR template
andrewjong Apr 30, 2026
92231d7
Update AGENTS.md
andrewjong Apr 30, 2026
d4d41d6
Fix finding baseline metrics
andrewjong Apr 30, 2026
1295869
Update workflow to comment instead of react
andrewjong Apr 30, 2026
df244c2
Fix bug
andrewjong Apr 30, 2026
3765172
Try fix another bug
andrewjong Apr 30, 2026
80fc6d0
Update omni_pass_TEMPLATE.env to use 'guest'; update default on syste…
andrewjong Apr 30, 2026
be72b37
Auto prepend 'build_packages' mark to ensure code is built before tests
andrewjong Apr 30, 2026
7f06a53
Lower default stress-iterations to 1 and single takeoff-velocity to 0.5
andrewjong Apr 30, 2026
ac4c4e1
Johnliu/px4 cpu optimization (#348)
JohnYanxinLiu Apr 30, 2026
1d437c7
Add new skills
andrewjong May 1, 2026
fc438e1
Revise pull request template for clarity and detail
andrewjong May 5, 2026
d3a0d45
Johnliu/rtx lidar update (#351)
JohnYanxinLiu May 8, 2026
63cfd44
Krrish/coord pr (#350)
krrishj18 May 11, 2026
910fe42
Scene prep bug fix (#354)
krrishj18 May 20, 2026
47958ba
Add workflows to (1) enforce correct branch merge convention (2) upda…
andrewjong May 20, 2026
648e7d7
Update docs on branches
andrewjong May 20, 2026
a682ded
Update workflow to handle develop version increment
andrewjong May 20, 2026
2ccade7
Release 0.18.0
andrewjong May 20, 2026
179de2a
Bump VERSION to after sync from main
github-actions[bot] May 20, 2026
35ac516
feat(osmo): VS Code/Cursor dev workflow on NVIDIA OSMO (#352)
smash0190 May 22, 2026
ce2ae2d
Add fixed-trajectory evaluation tests
pvkumara5 Apr 27, 2026
cb9c96c
Spherical lookahead bug that fixed the circle test and caused the cir…
pvkumara5 Jun 3, 2026
59eef1b
Added in code that consolidated all the results code so the user can …
pvkumara5 Jun 5, 2026
c6ba9d7
Results for 10 tries headless summary statistics
pvkumara5 Jun 5, 2026
df16cb9
Fixed the logging files so now it only outputs one summary file and i…
pvkumara5 Jun 9, 2026
6e147ef
deleted cleanup_old_results.sh which was a local tool for cleaning up…
pvkumara5 Jun 9, 2026
e4df6c6
Added preliminary docs to explain changes made
pvkumara5 Jun 9, 2026
6182b93
Changed .env to say 0.19.0-alpha.4
pvkumara5 Jun 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added --gui
Empty file.
Empty file added --num-robots
Empty file.
Empty file added --sim
Empty file.
Empty file added --stress-iterations
Empty file.
Empty file added --trajectory-types
Empty file.
11 changes: 11 additions & 0 deletions -v
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
access control disabled, clients can connect from any host
============================= test session starts ==============================
platform linux -- Python 3.12.13, pytest-9.0.3, pluggy-1.6.0 -- /usr/local/bin/python3.12
cachedir: /tmp/.pytest_cache
rootdir: /home/pranavkumara/Desktop/AirStack/tests
configfile: pytest.ini
plugins: dependency-0.6.1, timeout-2.4.0
collecting ... collected 0 items

- generated xml file: /home/pranavkumara/Desktop/AirStack/tests/results/2026-05-28_14-14-04/results.xml -
============================ no tests ran in 0.00s =============================
4 changes: 2 additions & 2 deletions .agents/skills/add-ros2-package/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Determine where the package should live based on its function:

**Local Layer:**
- Local planner: `robot/ros_ws/src/local/planners/<package_name>`
- Local controller: `robot/ros_ws/src/local/c_controls/<package_name>`
- Local controller: `robot/ros_ws/src/local/controls/<package_name>`
- Local world model: `robot/ros_ws/src/local/world_models/<package_name>`

**Global Layer:**
Expand Down Expand Up @@ -508,7 +508,7 @@ After creating the package:

- **AirStack Examples:**
- Reference planner: `robot/ros_ws/src/local/planners/droan_local_planner`
- Reference controller: `robot/ros_ws/src/local/c_controls/trajectory_controller`
- Reference controller: `robot/ros_ws/src/local/controls/trajectory_controller`
- Package template: `assets/package_template/`

- **Next Skills:**
Expand Down
10 changes: 6 additions & 4 deletions .agents/skills/add-task-executor/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@ Examples: coverage survey, object search, object counting, semantic search, fixe
|-------------|---------|
| `ExplorationTask.action` | Random or systematic area exploration |
| `CoverageTask.action` | Systematic lawnmower-pattern coverage survey |
| `ObjectSearchTask.action` | Finding instances of a named object class |
| `ObjectCountingTask.action` | Counting all instances of an object class in an area |
| `NavigateTask.action` | Navigating to a target pose |
| `TakeoffTask.action` | Vertical takeoff to a target altitude |
| `LandTask.action` | Controlled landing |
| `FixedTrajectoryTask.action` | Following a pre-defined waypoint trajectory |
| `SemanticSearchTask.action` | Finding a location described in natural language |
| `ChatTask.action` | Natural-language chat-driven task |

If none of these fits, add a new `.action` file to `task_msgs` following the same pattern (see Step 0 below).

Expand All @@ -58,7 +60,7 @@ float32 max_flight_speed
float32 time_limit_sec # 0 = no limit
# ... task-specific fields
---
# Result — returned when task completes or is cancelled
# Result — returned when task completes or is canceled
bool success
string message
# ... task-specific result fields
Expand Down Expand Up @@ -181,7 +183,7 @@ void execute(std::shared_ptr<GoalHandle> goal_handle)
if (cancel_requested_) {
auto result = std::make_shared<YourTask::Result>();
result->success = false;
result->message = "Task cancelled";
result->message = "Task canceled";
task_active_ = false;
goal_handle->canceled(result);
return;
Expand Down
240 changes: 240 additions & 0 deletions .agents/skills/attach-gossip-payload/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
# Skill: Attach Custom Payload to PeerProfile (Gossip Protocol)

## When to use
When you want to broadcast any ROS message to all peer robots via the gossip
protocol — for example, a frontier map, sensor summary, or task status.

## Background

Each robot runs a `gossip_node` that periodically broadcasts a `PeerProfile`
to all other robots on the gossip domain (default domain 99). The profile
carries structured fields (GPS, heading, waypoint) plus an open-ended
`payloads` array of serialized ROS messages.

**Key files:**
| File | Purpose |
|------|---------|
| `common/ros_packages/coordination/coordination_bringup/config/gossip_payloads.yaml` | Lists topics to attach as payloads — **edit this to add payloads** |
| `coordination_bringup/coordination_bringup/gossip_node.py` | Reads config, subscribes, attaches payloads on each publish tick |
| `coordination_bringup/coordination_bringup/peer_profile.py` | `PeerProfile` helper class with `add_payload` / `get_payload` API |
| `coordination_msgs/msg/PeerProfile.msg` | Wire format — `payloads` is `PeerProfilePayload[]` |
| `coordination_msgs/msg/PeerProfilePayload.msg` | `string payload_type` + `uint8[] payload_data` |

## How to add a payload (config-driven — no code changes)

### Step 1 — Edit `gossip_payloads.yaml`

```yaml
payload_topics:
# existing entries ...

# Your new payload:
- topic: "/{robot_name}/your/topic"
type: "your_msgs/msg/YourType"
```

- `{robot_name}` is automatically substituted at runtime (e.g. → `/robot_1/your/topic`)
- If the topic hasn't published yet, the payload is silently skipped — no crash
- `type` must be the fully-qualified ROS 2 type string

### Step 2 — Rebuild and restart gossip_node

```bash
bws --packages-select coordination_bringup
ros2 launch coordination_bringup gossip.launch.xml
```

### Step 3 — Verify

Check that the payload is being attached:
```bash
ros2 topic echo /gossip/peers --field payloads
# should show entries with your payload_type string
```

Or use the registry monitor:
```bash
ros2 run coordination_bringup peer_registry_monitor
# shows payload_types per peer
```

## How to read a payload on the receiving side

```python
from coordination_bringup.peer_profile import PeerProfile

def on_peer_msg(self, msg):
profile = PeerProfile.from_ros_msg(msg)

# Get a specific payload by type string
rays = profile.get_payload("visualization_msgs/msg/MarkerArray")
if rays is not None:
# use rays as visualization_msgs/msg/MarkerArray
pass

# List all payload types present
print(profile.payload_types())

# Get all payloads deserialized
for payload in profile.get_all_payloads():
print(type(payload))
```

## Step 4 — Add GCS visualization

After adding a payload to `gossip_payloads.yaml`, add a handler so it appears in
Foxglove. Each payload is published to its own topic:
`/gcs/payload/{robot_name}/{payload_name}`

This means Foxglove exposes full visualization controls (point size, color mapping,
marker type, etc.) for each payload independently.

**File:** `gcs/ros_ws/src/gcs_visualizer/gcs_visualizer/payload_visualizer_node.py`

### 4a — Read the payload type from `gossip_payloads.yaml`

Open `common/ros_packages/coordination/coordination_bringup/config/gossip_payloads.yaml`
and note the `type:` field for your new entry. This determines how to deserialize it.

If your type is **unique** (not already in `PAYLOAD_HANDLERS`), go to step 4b.
### 4b — Add handler and register in `PAYLOAD_HANDLERS`

`PAYLOAD_HANDLERS` is keyed by **payload name** (the last segment of the topic path
in `gossip_payloads.yaml`). This means multiple payloads of the same ROS type work
without any special casing.

Add a handler and register it:

```python
PAYLOAD_HANDLERS = {
'filtered_rays': ('visualization_msgs/msg/MarkerArray', _handle_filtered_rays),
'raw_frontiers': ('sensor_msgs/msg/PointCloud2', _handle_raw_frontiers),
'voxel_rgb': ('sensor_msgs/msg/PointCloud2', _handle_rgb_voxels),
'navigation_mode': ('std_msgs/msg/String', _handle_navigation_mode),
'your_name': ('your_msgs/msg/YourType', _handle_your_payload), # ← add
}
```

The key `'your_name'` must match the last path segment of the topic in `gossip_payloads.yaml`.
For example, `/{robot_name}/rayfronts/voxel_rgb` → key is `voxel_rgb`.

Handler signature — all handlers must match exactly:
```python
def _handle_your_payload(self, robot_name, msg, i, now):
# msg — deserialized ROS message (already in global ENU / 'map' frame)
# i — stable robot index (use for marker IDs: i * 100000 + unique_offset)
# now — current ROS timestamp (builtin_interfaces/Time)
# transform and publish to the payload's dedicated topic:
self._pub_for(f'/gcs/payload/{robot_name}/your_name', YourMsgType).publish(out)
```

### 4c — Visualization options

For `PointCloud2` payloads, choose one approach:

**Default:** Publish as raw `PointCloud2` — Foxglove GUI controls point size, shape, and color. No extra code needed.

**Preconfigured shape/size:** Convert to a `CUBE_LIST` `MarkerArray` in the handler (see `voxel_rgb` for a real example). Use this when you want a fixed visual style regardless of user layout settings:

```python
from gcs_visualizer.gcs_utils import point_cloud2_to_cube_marker

def _handle_your_payload(self, robot_name, msg, i, now):
marker = point_cloud2_to_cube_marker(
msg, 0.0, 0.0, self._display_z_offset(),
ns=f'{robot_name}_your_name',
marker_id=i * 100000,
stamp=now,
lifetime=Duration(sec=2, nanosec=0),
fallback_color=None, # uses per-point rgb field; set to (r, g, b, a) for a solid color
scale=0.5, # cube size in meters
)
if marker is not None:
out = MarkerArray()
out.markers.append(marker)
self._pub_for(f'/gcs/payload/{robot_name}/your_name', MarkerArray).publish(out)
```

### 4d — Available transform helpers (`gcs_utils.py`)

Check `gcs/ros_ws/src/gcs_visualizer/gcs_visualizer/gcs_utils.py` before writing
transform logic. Add a new helper there if none fits.

| Helper | Use for |
|--------|---------|
| `transform_marker_array(ma, bx, by, bz)` | `MarkerArray` → translated `MarkerArray` |
| `transform_point_cloud2(cloud, bx, by, bz)` | `PointCloud2` → translated `PointCloud2` (preserves all fields including `rgb`) |
| `point_cloud2_to_cube_marker(cloud, bx, by, bz, ns, marker_id, stamp, lifetime, scale)` | `PointCloud2` → `CUBE_LIST` Marker with fixed voxel size and per-point RGB |

### 4e — Rebuild GCS

```bash
docker exec airstack-gcs-1 bash -c "bws --packages-select gcs_visualizer && sws"
```

Verify topics exist:
```bash
docker exec airstack-gcs-1 bash -c "ros2 topic list | grep /gcs/payload"
```

---

## Current payloads

| Topic in `gossip_payloads.yaml` | Type | GCS topic | Foxglove controls |
|--------------------------------|------|-----------|-------------------|
| `/{robot_name}/filtered_rays` | `visualization_msgs/msg/MarkerArray` | `/gcs/payload/{robot}/filtered_rays` | Fixed (MarkerArray) |
| `/{robot_name}/raw_frontiers` | `sensor_msgs/msg/PointCloud2` | `/gcs/payload/{robot}/raw_frontiers` | Full (raw PointCloud2) |
| `/{robot_name}/rayfronts/voxel_rgb` | `sensor_msgs/msg/PointCloud2` | `/gcs/payload/{robot}/voxel_rgb` | Fixed (CUBE_LIST MarkerArray, 0.5 m) |
| `/{robot_name}/navigation_mode` | `std_msgs/msg/String` | `/gcs/payload/{robot}/navigation_mode` | Status string |

## Architecture notes

- `gossip_node` runs on the **robot's domain** (e.g. domain 1 for `robot_1`)
and can subscribe directly to any topic on that domain, including Rayfronts
- The gossip DDS Router bridges `/gossip/peers` to the shared gossip domain
(default 99) — the payload bytes travel inside the PeerProfile message,
so payload topics do **not** need their own DDS router entries
- Payloads are re-serialized every publish tick from the latest cached message;
stale data is never cleared between ticks (latest-wins per topic)
- Payload size matters for gossip bandwidth — avoid attaching large point clouds
at high rates; 1 Hz (the default gossip rate) is usually fine

## Message deduplication

Every `PeerProfile` message is identified by the triple:

```
(robot_name, gps_fix.header.stamp.sec, gps_fix.header.stamp.nanosec)
```

The stamp is set **at publish time** by the originating robot. Each receiver
maintains a seen-set (size 50, FIFO eviction) and drops any message whose ID
has already been processed.

**Expected behavior:** every drone will forward/receive a message at least
once — this is intentional. The seen-set prevents infinite re-processing but
does not prevent the initial fan-out that comes from all robots being on the
same shared DDS domain.

**Relay fields (reserved for future use):**
- `uint8 source` — `SOURCE_DIRECT (0)` or `SOURCE_RELAYED (1)`
- `uint8 relay_hops` — number of hops the message has traversed

These fields exist in the wire format and Python API but relay logic is not
yet implemented. The seen-set deduplication is already wired to handle it
correctly when relay is activated.

## Registry behavior

- Each robot keeps a **per-robot inbox** (latest message per peer, drained at
5 Hz) and a **global registry** (latest-wins, monotonic per robot timestamp)
- Registry entries are **never evicted** — a crashed robot stays visible
indefinitely until the node is restarted
- The registry is published to `/{robot_name}/coordination/peer_registry` with
RELIABLE + TRANSIENT_LOCAL QoS so late-joining nodes get the full snapshot

## QoS note

Payload subscriptions use `GOSSIP_QOS` (BEST_EFFORT, KEEP_LAST 1). If your
source topic uses RELIABLE QoS you may need to adjust — see `gossip_node.py`.
Loading