Skip to content
Open
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ build/
install/
log/

# Python caches
__pycache__/
*.py[cod]

# imported ROS projects
easy_handeye2/
ros2_aruco/
Expand Down
123 changes: 123 additions & 0 deletions waybionic_sensors/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# waybionic_sensors

IMU publishing and sensor health for the WayBionic ground station.

The package publishes `sensor_msgs/msg/Imu` and reports IMU health on
`/diagnostics` so the merged `waybionic_rviz_plugins` DiagnosticsPanel can show a
live `imu.heartbeat`. It runs entirely on a mock source today, and defines the
boundary a real driver will plug into once electrical confirms the sensor.

## Quickstart

```bash
source /opt/ros/jazzy/setup.bash
cd <workspace>
rosdep install --from-paths src --ignore-src -r -y
colcon build --packages-select waybionic_sensors --symlink-install
source install/setup.bash

ros2 launch waybionic_sensors imu_publisher.launch.py
```

Check the output:

```bash
ros2 topic hz /waybionic/imu/data_raw
ros2 topic echo /waybionic/imu/data_raw --once
ros2 topic echo /diagnostics --once
```

## RViz walkthrough

```bash
ros2 launch waybionic_sensors imu_demo.launch.py
```

This enables the synthetic orientation and the rotating demo TF so there is
something to look at. Both are off in `imu_publisher.launch.py`.

## Heartbeat in the diagnostics panel

```bash
# Terminal 1
ros2 launch waybionic_sensors imu_publisher.launch.py

# Terminal 2
ros2 launch waybionic_rviz_plugins engineer_view.launch.py use_mock_diagnostics:=false
```

The panel shows `imu.heartbeat` as OK with an age in seconds. To watch it go
stale without unplugging anything:

```bash
ros2 launch waybionic_sensors imu_publisher.launch.py mock_stall_after_sec:=5.0
```

The mock stops after five seconds, and the row turns STALE once the sample age
passes `stale_timeout_sec`.

## What is measured and what is generated

The raw topic carries gyroscope and accelerometer data only. It marks
orientation unavailable with `orientation_covariance[0] = -1`, because an
accelerometer and a gyroscope cannot observe absolute heading. The synthetic
orientation lives on its own `data_demo` topic and is off by default, so nothing
can mistake it for a measurement.

Full details, units, covariance conventions, and the parameter list are in
`docs/IMU_CONTRACT.md`.

## Package layout

```text
waybionic_sensors/
waybionic_sensors/
imu_reading.py # Hardware-independent sample type: the boundary contract
mock_source.py # Synthetic sample generation, no ROS types
hardware_reader.py # Driver interface plus an unimplemented stub
imu_messages.py # sensor_msgs/Imu and TF construction, covariance rules
imu_diagnostics.py # DiagnosticArray construction, heartbeat and freshness
imu_publisher_node.py # ROS node that only wires the above together
launch/
imu_publisher.launch.py
imu_demo.launch.py
config/
imu_demo.rviz
docs/
IMU_CONTRACT.md
HARDWARE_INTERFACE.md
PR_NOTES.md
test/
```

Each stage is separately testable: sample generation, message construction,
diagnostics, and the hardware boundary have no dependency on one another.

## Hardware status

No physical IMU driver exists yet. `hardware_reader.py` defines the interface
and deliberately implements no serial protocol, because the sensor model,
transport, and packet format are unconfirmed. The open questions for electrical
are tracked in `docs/HARDWARE_INTERFACE.md`.

Running with `use_mock:=false` is still meaningful: no samples are published and
`imu.heartbeat` reports STALE, which is what a missing sensor should look like.

## Tests

```bash
colcon test --packages-select waybionic_sensors
colcon test-result --all --verbose
```

Coverage spans message semantics and covariance, mock generation and stalling,
diagnostics levels and units, the hardware boundary, package structure, and a
runtime suite that spins the node to check timestamps, frame IDs, rate, demo
defaults, and the heartbeat transitioning from OK to STALE.

## Related docs

- `docs/IMU_CONTRACT.md` — topics, units, covariance, and parameters
- `docs/HARDWARE_INTERFACE.md` — questions for electrical and how to add a driver
- `docs/PR_NOTES.md` — review notes, design rationale, and runtime evidence
- `waybionic_rviz_plugins/docs/DIAGNOSTICS_BACKEND_INTEGRATION.md` — the diagnostics contract this package publishes against
93 changes: 93 additions & 0 deletions waybionic_sensors/config/imu_demo.rviz
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
Panels:
- Class: rviz_common/Displays
Name: Displays
- Class: rviz_common/Views
Name: Views
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.03
Value: Lines
Name: Grid
Normal Cell Count: 0
Offset:
X: 0
Y: 0
Z: 0
Plane: XY
Plane Cell Count: 10
Reference Frame: <Fixed Frame>
Value: true
- Class: rviz_default_plugins/TF
Enabled: true
Frame Timeout: 15
Frames:
All Enabled: true
Marker Scale: 0.3
Name: TF
Show Arrows: true
Show Axes: true
Show Names: true
Tree:
{}
Update Interval: 0
Value: true
- Class: rviz_default_plugins/Imu
Enabled: true
Name: IMU acceleration (raw)
Topic:
Depth: 10
Durability Policy: Volatile
Filter size: 10
History Policy: Keep Last
Reliability Policy: Reliable
Value: /waybionic/imu/data_raw
Value: true
alpha: 1
color: 204; 51; 204
history_length: 1
queue_size: 10
scale: 0.1
Enabled: true
Global Options:
Background Color: 48; 48; 48
Fixed Frame: base_link
Frame Rate: 30
Name: root
Tools:
- Class: rviz_default_plugins/Interact
- Class: rviz_default_plugins/MoveCamera
- Class: rviz_default_plugins/Select
Transformation:
Current:
Class: rviz_default_plugins/TF
Value: true
Views:
Current:
Class: rviz_default_plugins/Orbit
Distance: 2.0
Enable Stereo Rendering:
Stereo Eye Separation: 0.06
Stereo Focal Distance: 1
Swap Stereo Eyes: false
Value: false
Focal Point:
X: 0
Y: 0
Z: 0
Focal Shape Fixed Size: true
Focal Shape Size: 0.05
Invert Z Axis: false
Name: Current View
Near Clip Distance: 0.01
Pitch: 0.5
Target Frame: <Fixed Frame>
Value: Orbit (rviz)
Yaw: 0.8
Saved: ~
85 changes: 85 additions & 0 deletions waybionic_sensors/docs/HARDWARE_INTERFACE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# IMU Hardware Interface

Everything the software side needs from electrical before a real IMU driver can
be written, plus what is deliberately left unimplemented until those answers
arrive.

**Status: awaiting answers from electrical.** No value below is confirmed. The
package ships a mock source and an unimplemented driver stub rather than a
guessed serial protocol.

## Questions for electrical

### Sensor

| # | Question | Answer |
|---|----------|--------|
| 1 | Exact sensor model and manufacturer part number? | _unanswered_ |
| 2 | Link to the datasheet used for the selection? | _unanswered_ |
| 3 | Does the device output a fused quaternion, or only raw accelerometer and gyroscope? | _unanswered_ |
| 4 | Is there a magnetometer, and will it be usable near the motors? | _unanswered_ |
| 5 | Is on-chip filtering enabled, and at what cutoff? | _unanswered_ |

### Transport

| # | Question | Answer |
|---|----------|--------|
| 6 | How does the IMU reach the ground-station computer: direct USB, USB-serial bridge, microcontroller relay, or I2C/SPI on a board? | _unanswered_ |
| 7 | If a microcontroller sits in between, what is its packet format: framing bytes, field order, endianness, checksum? | _unanswered_ |
| 8 | Baud rate or bus clock, and is it configurable? | _unanswered_ |
| 9 | Device path or enumeration rule on Linux, and should a udev rule be provided? | _unanswered_ |
| 10 | Does the device timestamp its own samples, or must the host stamp on receipt? | _unanswered_ |

### Data format

| # | Question | Answer |
|---|----------|--------|
| 11 | Units as transmitted: g or m/s^2, deg/s or rad/s, raw counts with a scale factor? | _unanswered_ |
| 12 | Axis convention and mounting orientation relative to `base_link`? | _unanswered_ |
| 13 | Sample rate, and is it fixed or configurable? | _unanswered_ |
| 14 | Per-axis noise density or RMS noise for covariance values? | _unanswered_ |
| 15 | Is bias/scale calibration applied on-device, or expected from the host? | _unanswered_ |

### Integration

| # | Question | Answer |
|---|----------|--------|
| 16 | Where is the IMU physically mounted, and what is its transform from `base_link`? | _unanswered_ |
| 17 | Is there a status or fault line worth surfacing in `/diagnostics`? | _unanswered_ |
| 18 | Expected behaviour on cable disconnect: silence, error frames, or reconnect? | _unanswered_ |

## Known unknowns

Until the answers arrive, these stay open and are not guessed anywhere in code:

- Wire protocol and framing. `hardware_reader.py` defines an interface only.
- Real covariance values. The mock uses placeholder standard deviations exposed
as parameters (`angular_velocity_stddev`, `linear_acceleration_stddev`).
- Whether a fused orientation will ever be available. Until it is, the raw topic
marks orientation unavailable.
- The static transform from `base_link` to `imu_link`. The demo TF is a
visualisation aid with a placeholder 0.1 m offset, not a mounting claim.

## Adding the driver later

The node reads samples through
`waybionic_sensors.hardware_reader.ImuHardwareReader`. Adding hardware means
implementing that interface in a new module and constructing it instead of
`UnconfiguredImuReader`:

```python
class MyImuReader(ImuHardwareReader):
def open(self): ...
def read(self, stamp_ns) -> Optional[ImuReading]: ...
def close(self): ...
def describe(self) -> str: ...
```

The driver owns transport and parsing, and converts to the REP-103 units of
`ImuReading`. Message construction, covariance, diagnostics, and TF need no
changes. Parser tests should be added at that point using recorded packets from
the real device.

Live mode already works end to end with the stub: the node publishes no samples
and `imu.heartbeat` reports STALE, which is the correct depiction of a missing
sensor.
Loading
Loading