Skip to content
Merged
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
35 changes: 24 additions & 11 deletions ros2-poc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,23 @@ OK while data flows, STALE after `--stale-s`, WARN never seen, ERROR for BIAS un
failed command, with rate / last sequence / latency / last error + time), and a
`visualization_msgs/MarkerArray` on `/viz/markers`: the ball rotated by FicTrac's lab-frame
orientation with markings so the rotation is visible, the fly fixed on top facing +X, the arena
as a translucent cylinder, the pattern feature as a bar on the wall at
`wrap180(frame_dir·idx·deg_per_frame + az0)` (the same mapping `fictrac_sim.py --model fly`
uses), and a text readout. The stock `foxglove_bridge` serves it on **ws://localhost:8766**
as a translucent cylinder, the pattern feature as a bar on the wall at `wrap180(display_deg)`
(below), and a text readout. The stock `foxglove_bridge` serves it on **ws://localhost:8766**
(8765 is the Studio bridge).

**The display's rotation, not just its index.** The bridge reports the frame index mod the
pattern's frame count, so with a periodic pattern (a 20-frame grating) `azimuth_deg =
wrap180(frame_dir·idx·deg_per_frame + az0)` stays inside one 36° period while the fly turns
through thousands of degrees. `/viz/arena/display_deg` sums the index steps, each taken the short
way round the modulus. That is the display's unwrapped rotation, which the closed-loop plot sets
against the fly's turn. The modulus comes from `--frames N` when given; otherwise it is learned
within each epoch as the largest index seen + 1, which is enough because a wrap passes through
the largest index. For a pattern that spans the arena (n·deg_per_frame = 360), `wrap180(display_deg)`
equals the feature azimuth, so the 3D bar is unchanged there, and for a grating it now turns
with the pattern. `/viz/fictrac/fwd_dps` and `side_dps` are the rates of FicTrac's integrated
forward/side motion (cols 20/21) in °/s, the units of `speed_dps`, so the kinematics plot reads
on one axis. `pixi run test-viz` checks this math.

```bash
pixi run rig -- --sim --sim-args "--model fly --kp 2 --seed 1" --foxglove # Arena + FicTrac
pixi run rig -- --sim --bias --bias-sim --foxglove # Arena + BIAS (sim)
Expand All @@ -163,14 +175,15 @@ pixi run rig -- --sim --bias --foxglove
# Layout → Import from file… → ros2-poc/foxglove/arena-fictrac.json or arena-bias.json
```

`arena-fictrac.json`: 3D scene, closed-loop plot (fly turn vs feature azimuth vs bias angle),
kinematics plot, frame-index plot, FicTrac light + rate gauge, diagnostics summary/detail, last
error, log. `arena-bias.json`: 3D scene, BIAS state timeline (unreachable / idle / preview /
recording), frame count vs arena index, latest BIAS reply (shows the `artifact` path), reachable /
capturing / writing-video lights, fps gauge, diagnostics, last error, log. Both work live and on a
replayed MCAP once the log moves to MCAP. Verified here only as topics and advertised channels
(no GUI in the container); the layouts are hand-written Foxglove JSON, so a panel whose schema
drifted shows up empty and is one click to re-point.
`arena-fictrac.json`: 3D scene, closed-loop plot (fly turn vs display rotation), bias angle,
kinematics plot (speed, forward, side in °/s), frame-index plot, FicTrac light + rate gauge,
diagnostics summary/detail, last error, log. `arena-bias.json`: 3D scene, BIAS state timeline
(unreachable / idle / preview / recording), camera frame count and the displayed index on separate
plots, latest BIAS reply (shows the `artifact` path), BIAS / camera / video lights, fps gauge,
diagnostics, last error, log. The ROS log panel is live only: the bags do not record `/rosout`.
Both layouts were checked in Foxglove (web 3.4, 2026-10-16) on ww18 hardware runs: each bag was
played back at 1× through this node and its outputs recorded. The files and the checker
(`tools/check_layouts.py`) are on branch `data/ros2-poc-ww18-2026-10-15`.

## Log format: MCAP sizing and the two-channel plan (2026-10-15)

Expand Down
56 changes: 40 additions & 16 deletions ros2-poc/foxglove/arena-bias.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,36 +37,40 @@
"imageMode": {}
},
"Indicator!reachable": {
"foxglovePanelTitle": "BIAS",
"path": "/viz/bias/reachable.data",
"style": "bulb",
"fallbackColor": "#8b949e",
"fallbackLabel": "BIAS: no status yet",
"fallbackLabel": "no status",
"rules": [
{ "operator": "=", "rawValue": "true", "color": "#00e676", "label": "BIAS reachable" },
{ "operator": "=", "rawValue": "false", "color": "#ff5252", "label": "BIAS unreachable" }
{ "operator": "=", "rawValue": "true", "color": "#00e676", "label": "online" },
{ "operator": "=", "rawValue": "false", "color": "#ff5252", "label": "offline" }
]
},
"Indicator!capturing": {
"foxglovePanelTitle": "Camera",
"path": "/viz/bias/capturing.data",
"style": "bulb",
"fallbackColor": "#8b949e",
"fallbackLabel": "capture: unknown",
"fallbackLabel": "unknown",
"rules": [
{ "operator": "=", "rawValue": "true", "color": "#00e676", "label": "capturing" },
{ "operator": "=", "rawValue": "false", "color": "#8b949e", "label": "not capturing" }
{ "operator": "=", "rawValue": "false", "color": "#8b949e", "label": "idle" }
]
},
"Indicator!logging": {
"foxglovePanelTitle": "Video",
"path": "/viz/bias/logging.data",
"style": "bulb",
"fallbackColor": "#8b949e",
"fallbackLabel": "video: unknown",
"fallbackLabel": "unknown",
"rules": [
{ "operator": "=", "rawValue": "true", "color": "#ffd740", "label": "writing video" },
{ "operator": "=", "rawValue": "false", "color": "#8b949e", "label": "no video" }
{ "operator": "=", "rawValue": "true", "color": "#ffd740", "label": "writing" },
{ "operator": "=", "rawValue": "false", "color": "#8b949e", "label": "off" }
]
},
"Gauge!fps": {
"foxglovePanelTitle": "Camera fps",
"path": "/viz/bias/fps.data",
"minValue": 0,
"maxValue": 200,
Expand All @@ -82,10 +86,24 @@
"isSynced": true
},
"Plot!frames": {
"title": "BIAS frame count vs displayed index",
"title": "BIAS camera frames",
"paths": [
{ "value": "/viz/bias/frame_count.data", "enabled": true, "timestampMethod": "receiveTime", "label": "camera frames" },
{ "value": "/viz/arena/index.data", "enabled": true, "timestampMethod": "receiveTime", "label": "arena frame index" }
{ "value": "/viz/bias/frame_count.data", "enabled": true, "timestampMethod": "receiveTime", "label": "camera frames" }
],
"showXAxisLabels": true,
"showYAxisLabels": true,
"showLegend": true,
"legendDisplay": "floating",
"showPlotValuesInLegend": true,
"isSynced": true,
"xAxisVal": "timestamp",
"sidebarDimension": 240,
"followingViewWidth": 60
},
"Plot!arenaindex": {
"title": "Displayed frame index",
"paths": [
{ "value": "/viz/arena/index.data", "enabled": true, "timestampMethod": "receiveTime", "label": "frame index" }
],
"showXAxisLabels": true,
"showYAxisLabels": true,
Expand Down Expand Up @@ -125,6 +143,7 @@
"expansion": "all"
},
"RosOut!log": {
"foxglovePanelTitle": "ROS log (live only: bags do not record /rosout)",
"searchTerms": [],
"minLogLevel": 2,
"nameFilter": {}
Expand All @@ -147,20 +166,25 @@
"second": {
"direction": "row",
"splitPercentage": 55,
"first": "Plot!frames",
"first": {
"direction": "column",
"splitPercentage": 55,
"first": "Plot!frames",
"second": "Plot!arenaindex"
},
"second": "RawMessages!reply"
}
}
},
"second": {
"direction": "column",
"splitPercentage": 28,
"splitPercentage": 34,
"first": {
"direction": "row",
"splitPercentage": 66,
"splitPercentage": 60,
"first": {
"direction": "row",
"splitPercentage": 50,
"direction": "column",
"splitPercentage": 33,
"first": "Indicator!reachable",
"second": {
"direction": "column",
Expand Down
46 changes: 34 additions & 12 deletions ros2-poc/foxglove/arena-fictrac.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,25 @@
"imageMode": {}
},
"Plot!closedloop": {
"title": "Closed loop: fly vs display",
"title": "Closed loop: fly turn vs display rotation (deg, unwrapped)",
"paths": [
{ "value": "/viz/fictrac/turn_deg.data", "enabled": true, "timestampMethod": "receiveTime", "label": "fly turn (deg, unwrapped)" },
{ "value": "/viz/arena/azimuth_deg.data", "enabled": true, "timestampMethod": "receiveTime", "label": "feature azimuth (deg, right +)" },
{ "value": "/viz/arena/bias_deg.data", "enabled": true, "timestampMethod": "receiveTime", "label": "bias angle (deg)" }
{ "value": "/viz/fictrac/turn_deg.data", "enabled": true, "timestampMethod": "receiveTime", "label": "fly turn" },
{ "value": "/viz/arena/display_deg.data", "enabled": true, "timestampMethod": "receiveTime", "label": "display rotation" }
],
"showXAxisLabels": true,
"showYAxisLabels": true,
"showLegend": true,
"legendDisplay": "floating",
"showPlotValuesInLegend": true,
"isSynced": true,
"xAxisVal": "timestamp",
"sidebarDimension": 240,
"followingViewWidth": 30
},
"Plot!bias": {
"title": "Bias angle (deg)",
"paths": [
{ "value": "/viz/arena/bias_deg.data", "enabled": true, "timestampMethod": "receiveTime", "label": "bias" }
],
"showXAxisLabels": true,
"showYAxisLabels": true,
Expand All @@ -54,11 +68,11 @@
"followingViewWidth": 30
},
"Plot!kinematics": {
"title": "FicTrac kinematics",
"title": "FicTrac kinematics (deg/s of ball rotation)",
"paths": [
{ "value": "/viz/fictrac/speed_dps.data", "enabled": true, "timestampMethod": "receiveTime", "label": "speed (deg/s)" },
{ "value": "/viz/fictrac/fwd_rad.data", "enabled": true, "timestampMethod": "receiveTime", "label": "forward (rad, integrated)" },
{ "value": "/viz/fictrac/side_rad.data", "enabled": true, "timestampMethod": "receiveTime", "label": "side (rad, integrated)" }
{ "value": "/viz/fictrac/speed_dps.data", "enabled": true, "timestampMethod": "receiveTime", "label": "speed" },
{ "value": "/viz/fictrac/fwd_dps.data", "enabled": true, "timestampMethod": "receiveTime", "label": "forward" },
{ "value": "/viz/fictrac/side_dps.data", "enabled": true, "timestampMethod": "receiveTime", "label": "side" }
],
"showXAxisLabels": true,
"showYAxisLabels": true,
Expand Down Expand Up @@ -94,6 +108,7 @@
"collapsedSections": []
},
"Gauge!ftrate": {
"foxglovePanelTitle": "FicTrac rate (Hz)",
"path": "/viz/fictrac/rate_hz.data",
"minValue": 0,
"maxValue": 120,
Expand All @@ -103,13 +118,14 @@
"reverse": false
},
"Indicator!fictrac": {
"foxglovePanelTitle": "FicTrac",
"path": "/viz/fictrac/rate_hz.data",
"style": "bulb",
"fallbackColor": "#8b949e",
"fallbackLabel": "FicTrac: no data",
"fallbackLabel": "no data",
"rules": [
{ "operator": ">", "rawValue": "5", "color": "#00e676", "label": "FicTrac flowing" },
{ "operator": "<=", "rawValue": "5", "color": "#ff5252", "label": "FicTrac stalled" }
{ "operator": ">", "rawValue": "5", "color": "#00e676", "label": "flowing" },
{ "operator": "<=", "rawValue": "5", "color": "#ff5252", "label": "stalled" }
]
},
"RawMessages!lasterror": {
Expand All @@ -121,6 +137,7 @@
"expansion": "all"
},
"RosOut!log": {
"foxglovePanelTitle": "ROS log (live only: bags do not record /rosout)",
"searchTerms": [],
"minLogLevel": 2,
"nameFilter": {}
Expand All @@ -139,7 +156,12 @@
"second": {
"direction": "column",
"splitPercentage": 55,
"first": "Plot!closedloop",
"first": {
"direction": "row",
"splitPercentage": 68,
"first": "Plot!closedloop",
"second": "Plot!bias"
},
"second": {
"direction": "row",
"splitPercentage": 60,
Expand Down
Loading
Loading