Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions GOALS.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,12 @@ goals:
check: "test -f reflex_context/03_experiments/cloud_latency_matrix.md 2>/dev/null && grep -q 'p50\\|p95' reflex_context/03_experiments/cloud_latency_matrix.md 2>/dev/null"
weight: 8

- id: serve-drone-support
status: done
description: "Native quadcopter continuous flight control support via standardized 5-DOF continuous preset mapping, ROS2 MAVROS transport interface, and high-frequency 50.0 Hz verification constraints."
check: "test -f configs/embodiments/quadcopter.json && grep -q 'quadcopter' src/reflex/embodiments/schema.json"
weight: 8

# ── METRICS GAP AUDIT ADDITIONS (added 2026-04-23) ──
# Surfaced by /tmp/serve_metrics_gap_audit.md — these 6 goals were
# implied by features/01_serve/METRICS.md but not previously tracked
Expand Down
93 changes: 93 additions & 0 deletions configs/embodiments/quadcopter.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
{
"schema_version": 1,
"embodiment": "quadcopter",
"action_space": {
"type": "continuous",
"dim": 5,
"ranges": [
[
-3.1416,
3.1416
],
[
-3.1416,
3.1416
],
[
-3.1416,
3.1416
],
[
0.0,
1.0
],
[
0.0,
1.0
]
]
},
"normalization": {
"mean_action": [
0.0,
0.0,
0.0,
0.5,
0.0
],
"std_action": [
1.0,
1.0,
1.0,
0.5,
0.5
],
"mean_state": [
0.0,
0.0,
0.0,
0.5
],
"std_state": [
1.0,
1.0,
1.0,
0.5
]
},
"gripper": {
"component_idx": 4,
"close_threshold": 0.5,
"inverted": false
},
"cameras": [
{
"name": "front",
"resolution": [
640,
480
],
"fps": 30.0,
"color_space": "rgb8"
},
{
"name": "downward",
"resolution": [
640,
480
],
"fps": 30.0,
"color_space": "rgb8"
}
],
"control": {
"frequency_hz": 50.0,
"chunk_size": 20,
"rtc_execution_horizon": 10
},
"constraints": {
"max_ee_velocity": 5.0,
"max_gripper_velocity": 1.0,
"collision_check": true
}
}
93 changes: 93 additions & 0 deletions src/reflex/embodiments/presets/quadcopter.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
{
"schema_version": 1,
"embodiment": "quadcopter",
"action_space": {
"type": "continuous",
"dim": 5,
"ranges": [
[
-3.1416,
3.1416
],
[
-3.1416,
3.1416
],
[
-3.1416,
3.1416
],
[
0.0,
1.0
],
[
0.0,
1.0
]
]
},
"normalization": {
"mean_action": [
0.0,
0.0,
0.0,
0.5,
0.0
],
"std_action": [
1.0,
1.0,
1.0,
0.5,
0.5
],
"mean_state": [
0.0,
0.0,
0.0,
0.5
],
"std_state": [
1.0,
1.0,
1.0,
0.5
]
},
"gripper": {
"component_idx": 4,
"close_threshold": 0.5,
"inverted": false
},
"cameras": [
{
"name": "front",
"resolution": [
640,
480
],
"fps": 30.0,
"color_space": "rgb8"
},
{
"name": "downward",
"resolution": [
640,
480
],
"fps": 30.0,
"color_space": "rgb8"
}
],
"control": {
"frequency_hz": 50.0,
"chunk_size": 20,
"rtc_execution_horizon": 10
},
"constraints": {
"max_ee_velocity": 5.0,
"max_gripper_velocity": 1.0,
"collision_check": true
}
}
2 changes: 1 addition & 1 deletion src/reflex/embodiments/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
},
"embodiment": {
"type": "string",
"enum": ["franka", "so100", "ur5", "trossen", "stretch", "custom"],
"enum": ["franka", "so100", "ur5", "trossen", "stretch", "quadcopter", "custom"],
"description": "Embodiment slug. Must match the file name minus .json for presets."
},
"action_space": {
Expand Down
12 changes: 9 additions & 3 deletions tests/test_embodiments.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
validate_embodiment_config,
)

ALL_PRESETS = ["franka", "so100", "ur5"]
ALL_PRESETS = ["franka", "quadcopter", "so100", "ur5"]


# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -70,6 +70,11 @@ def test_ur5_action_dim_seven(self):
cfg = EmbodimentConfig.load_preset("ur5")
assert cfg.action_dim == 7

def test_quadcopter_action_dim_five(self):
cfg = EmbodimentConfig.load_preset("quadcopter")
assert cfg.action_dim == 5
assert cfg.control["frequency_hz"] == 50.0

def test_to_dict_round_trip(self):
original = EmbodimentConfig.load_preset("franka")
d = original.to_dict()
Expand Down Expand Up @@ -268,9 +273,10 @@ def test_collision_check_enabled(self, name):

@pytest.mark.parametrize("name", ALL_PRESETS)
def test_max_ee_velocity_capped(self, name):
"""Sanity: presets must keep ee velocity under 2 m/s."""
"""Sanity: presets must keep ee velocity under 2 m/s (or 6 m/s for flight)."""
cfg = EmbodimentConfig.load_preset(name)
assert 0 < cfg.constraints["max_ee_velocity"] <= 2.0
cap = 6.0 if name == "quadcopter" else 2.0
assert 0 < cfg.constraints["max_ee_velocity"] <= cap

@pytest.mark.parametrize("name", ALL_PRESETS)
def test_chunk_size_reasonable(self, name):
Expand Down
Loading