Run computer-side commands from the project root. Run board-side commands from the deployed program directory on the device.
Board verification results: micro_ros/VALIDATION.md
(packaged as MICRO-ROS-VALIDATION.md).
The native SchurVINS process optionally links rclc + rcl +
rmw_microxrcedds + Micro XRCE-DDS Client. This is micro-ROS, not a custom UDP
protocol. A micro-ROS Agent on the companion computer exposes the node
/tidesense/tidesense and these best-effort, volatile topics:
| Topic | Type | Meaning |
|---|---|---|
/tidesense/pose |
geometry_msgs/msg/PoseStamped |
Tracked body/IMU pose T_odom_body, metres, quaternion xyzw; frame_id=odom |
/tidesense/tracking |
std_msgs/msg/Bool |
Whether the current output has a valid pose; false suppresses pose publication |
Use a sensor-data/best-effort subscription (a reliable subscriber will not match). PoseStamped has no child frame: its pose is the calibrated body/IMU frame, not camera0. Mounting extrinsics to the vehicle's base_link must be applied on the computer. No covariance or velocity is fabricated. No TF is published yet.
This first integration provides optional ROS telemetry. Keyframe images,
landmark messages, remote reset/parameters, map persistence and remote loop
closure are not implemented. No global correction is fed back into VIO.
The micro-ROS build limits entities to one node and three publishers; extend
micro_ros/colcon.meta when adding services/subscriptions. A publisher-only node
needs no rclc executor; all rcl/rmw calls are owned by the communication thread.
The independent --calibrate SENSOR.ini --fps HZ mode publishes two reliable
sensor_msgs/msg/Image topics without running VIO. See CALIBRATION.md
for command lines, timestamps, recording and limitations. It requires the same
build/runtime opt-in. With --imu auto, a third reliable publisher sends /tidesense/imu at 200 Hz.
The image mode uses two publisher slots for left/right images;
ordinary VIO still uses them for pose/tracking. No images are sent by ordinary VIO.
Default build includes micro-ROS (GCC11, A53, static):
scripts/schurvins/build.sh
# Explicit equivalent:
SCHUR_WITH_MICRO_ROS=ON scripts/schurvins/build.shStandalone build without the micro-ROS library:
SCHUR_WITH_MICRO_ROS=OFF scripts/schurvins/build.shRequires host ROS 2 Humble development tools (default /opt/ros/humble/setup.bash,
override MICRO_ROS_HOST_SETUP), Python venv support, git, CMake and internet for
the initial fetch. micro_ros/build.sh creates a private Python environment under
install/micro-ros, downloads the exact commits in micro_ros/sources.repos,
and builds install/micro-ros/firmware/build/libmicroros.a. No host ROS library
is linked into the target. Host C/Python build generators run on the build PC.
The pinned RMW source receives 0001-isolate-client-key-rng.patch: upstream
client-key initialization calls global srand()/rand(), also used by SchurVINS
feature shuffling. The patch uses a local POSIX rand_r seed, so starting or
reconnecting micro-ROS cannot reseed the estimator's random sequence.
The existing NEON environment switches still apply; their defaults remain OFF.
The TideSense build_all / build_tidesense_apps profile defaults these NEON
switches to ON and always builds with micro-ROS enabled for the SD image.
Outputs are separate, so an optional build does not replace the standalone VIO:
- Standalone:
install/soc_sg2002_tidesense_arm64_sd/schurvins-gcc11-static-a53.tar - Optional micro-ROS:
install/soc_sg2002_tidesense_arm64_sd/schurvins-gcc11-static-micro-ros-a53.tar
For direct CMake use: -DSCHUR_WITH_MICRO_ROS=ON and optionally
-DSCHUR_MICRO_ROS_ROOT=/path/to/firmware/build.
Even the micro-ROS-enabled binary starts with communication OFF.
The original arguments and run.sh work unchanged:
./run.sh CALIB.yaml STEREO.csv IMU.csv POSES.csv
SCHUR_MICRO_ROS=0 ./run.sh --vio CALIB.yaml SENSOR.ini auto POSES.csv 0Enable it explicitly, using the computer's IP, not TideSense's .99:
SCHUR_MICRO_ROS=1 SCHUR_MICRO_ROS_AGENT=192.168.123.2 \
./run.sh --vio CALIB.yaml SENSOR.ini auto POSES.csv 0| Environment | Default | Meaning |
|---|---|---|
SCHUR_MICRO_ROS |
0 |
Exactly 0 or 1; runtime opt-in |
SCHUR_MICRO_ROS_AGENT |
unset | Required numeric computer IPv4 when enabled |
SCHUR_MICRO_ROS_PORT |
8888 |
Agent UDP port, 1–65535 |
SCHUR_MICRO_ROS_DOMAIN |
0 |
ROS domain, 0–232; match host ROS_DOMAIN_ID |
--help reports whether micro-ROS was compiled. Requesting communication from a
standalone-only binary fails with an explicit build-switch instruction.
Smoke modes never publish a usable pose with their test-only calibration.
--self-test is synthetic data; do not feed its poses to navigation.
Disabled: no communication thread, ROS context, socket or dynamic ROS/image buffer allocation.
Enabled: pose output uses a bounded 64-sample FIFO (about 320 ms at 200 Hz),
with a short producer lock; overflow drops the oldest sample. An invalid tracking
update clears queued valid poses. The worker runs at nice +5. UDP best effort avoids reliable publication
waits. Agent absence/disconnect never waits inside the VIO processing thread;
the worker pings and reconnects in the background. Exit interrupts retry waits.
Counters on exit report connections, published poses and overwritten/busy samples.
There is no historical replay after an outage. Consumers must detect stale data;
a disconnected device cannot deliver a final tracking=false message.
Use the Humble micro-ROS Agent. For ROS 2 subscribers, use Cyclone DDS in the
currently tested host environment (ros-humble-rmw-cyclonedds-cpp). The Agent
itself still uses Fast DDS internally; the two DDS implementations interoperate.
On the companion computer, install that ROS package if it is not already present:
sudo apt install ros-humble-rmw-cyclonedds-cppThe existing host Fast DDS 2.6.10 receiver exhibited an initial writer-matching failure: it discovered the topic but received no samples until the Agent was restarted. Changing reliability or multicast settings did not resolve it. Cyclone DDS received native VIO samples on the first connection. This is a measured host compatibility limitation, not a claim about all Fast DDS versions.
This SDK also provides scripts/schurvins/micro_ros/build-agent.sh to build a
pinned host Agent after the micro-ROS library build. Its workspace setup is
install/micro-ros/agent-install/local_setup.bash.
source scripts/calibration/ros2-env.sh
export FASTRTPS_DEFAULT_PROFILES_FILE=scripts/schurvins/micro_ros/calibration-agent.xml
ros2 run micro_ros_agent micro_ros_agent udp4 -p 8888Inspect from another terminal using the same ROS domain:
source scripts/calibration/ros2-env.sh
ros2 node list --no-daemon
ros2 topic echo /tidesense/pose geometry_msgs/msg/PoseStamped --qos-reliability best_effort --no-daemon
# Run in another terminal with the same environment:
ros2 topic echo /tidesense/tracking std_msgs/msg/Bool --qos-reliability best_effort --no-daemonThe transport is UDP/IP over the existing USB RNDIS connection. Allow UDP 8888
on that interface. Do not assume the computer always has 192.168.123.2.
File replay preserves dataset acquisition timestamps (including the original CSV
output). It is not paced and does not publish /clock; recorded historical
stamps will not match the host's current wall time.
Live camera/IMU acquisition uses a common monotonic domain. For ROS publication
only, the app samples CLOCK_REALTIME - CLOCK_MONOTONIC once at startup and adds
that offset to the image acquisition timestamp. This requires a correctly set
board clock; it does not synchronize the two computers. Synchronize clocks
before starting live VIO. Clock jumps during a run are not applied to the offset.
The odom frame is local to a VIO run and can restart on estimator reset. This initial interface does not provide a trajectory/session ID or a reset event; consumers must not concatenate separate runs into a global map. Such identifiers belong in the future keyframe/loop-closure protocol.
Configure -DSCHUR_BUILD_MICRO_ROS_TESTS=ON to build
schurvins_micro_ros_test, which needs no camera/IMU and sends known poses plus
periodic tracking loss. On the computer, run (in a ROS 2 environment):
python3 scripts/schurvins/tests/micro_ros_receiver.py --seconds 45 --output /tmp/micro-ros-test.jsonOn the board, run the fixture with SCHUR_MICRO_ROS=1 and the Agent address.
Starting the Agent late and restarting it during the 40-second fixture checks
recovery without restarting the board process. The receiver asserts pose values,
acquisition timestamps, frame IDs, tracking loss and ROS node discovery.
tests/board-micro-ros.py automates that fixture test using a dedicated domain 23
and port 18888. It takes --binary, --agent, --output and an optional
--host-ip; set SSHPASS and source the host ROS/Agent workspace first. The
default receiver RMW is Cyclone DDS; --rmw selects another installed RMW for
compatibility testing. The script waits for the ROS subscriber to be ready before
starting the board fixture.
VIO with IMU defaults to SCHUR_PREDICTION=1: the camera corrects the estimator
at approximately 20 Hz, while every new IMU sample propagates position, velocity
and orientation and publishes /tidesense/pose at approximately 200 Hz after
initialization. It uses the corrected IMU biases and gravity from the visual
backend. A delayed visual correction is applied at its own timestamp, then the
buffered IMU samples are replayed to the latest timestamp. Correction replay does
not republish old timestamps. Live propagation runs on a separate worker.
SCHUR_PREDICTION=0 restores visual-rate ROS poses. The positional POSES.csv
argument always stores visual-rate corrected poses. Optionally set
SCHUR_PREDICTION_CSV=/mnt/data/predicted.csv to record IMU-rate predictions:
timestamp_ns,px,py,pz,qx,qy,qz,qw,vx,vy,vz,tracking. CSV timestamps retain the
acquisition clock; live ROS timestamps use the existing realtime mapping.
Prediction also works with SCHUR_MICRO_ROS=0.
No valid prediction is published before IMU initialization, after reported loss of tracking, after an IMU gap of 15 ms or more, or when the latest visual correction is older than 500 ms. Recovery requires a fresh covered correction. These checks depend on SchurVINS reporting tracking correctly; they do not detect all divergence or make a bad visual correction accurate. This is propagation, not a 200 Hz visual solve, and visual corrections may cause output jumps.
The sensor's actual sampling rate determines output rate. Best-effort transport can drop messages; monitor exit counters. Unpaced offline replay emits predictions at dataset timestamps as fast as computation permits, so ROS wall-clock receive rate during replay is not a 200 Hz hardware test.