Robotics Quick Start¶
This guide is for robotics developers who want to prove that libgnss++ can ingest real GNSS data, publish ROS2-friendly outputs, and leave replayable artifacts behind.
Use this path when your next consumer is Autoware, Nav2, a localization stack, or a rosbag-based field workflow.
10-minute offline loop¶
Build the native tools:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
python3 apps/gnss.py doctor
Run a short RTK replay from the bundled PPC-Dataset checkout:
mkdir -p output/adoption
python3 apps/gnss.py solve \
--rover data/PPC-Dataset/tokyo/run1/rover.obs \
--base data/PPC-Dataset/tokyo/run1/base.obs \
--nav data/PPC-Dataset/tokyo/run1/base.nav \
--mode kinematic \
--preset low-cost \
--ratio 2.4 \
--max-epochs 200 \
--out output/adoption/tokyo_run1_rtk.pos \
--kml output/adoption/tokyo_run1_rtk.kml
Open the local dashboard:
python3 apps/gnss.py web --port 8085 --root .
Then visit http://127.0.0.1:8085. The dashboard reads existing artifacts
from output/, so it is useful even when a run was produced by CI, Docker, or
a field laptop.
Add a quality gate¶
For robotics work, do not stop at "a trajectory was produced". Run a sign-off command that emits machine-readable metrics and fails on explicit thresholds:
python3 apps/gnss.py ppc-rtk-signoff \
--dataset-root data/PPC-Dataset \
--city tokyo \
--run run1 \
--preset low-cost \
--ratio 2.4 \
--max-epochs 200 \
--out output/adoption/tokyo_run1_signoff.pos \
--summary-json output/adoption/tokyo_run1_signoff.json \
--require-positioning-rate-min 70 \
--require-effective-epoch-rate-min 5
The resulting JSON is the artifact to archive in CI or attach to a regression report. It records positioning rate, fix rate, timing, thresholds, and dataset provenance.
Realtime smoke test¶
Robotics users need to know whether a profile can keep up with recorded sensor
time. gnss robotics-smoke runs a short PPC RTK replay and gates the runtime
metrics that matter for deployment triage:
python3 apps/gnss.py robotics-smoke \
--profile realtime \
--max-epochs 200 \
--realtime-factor-min 1.0 \
--effective-epoch-rate-min 5.0
Profiles:
| Profile | Purpose | Default gate |
|---|---|---|
quick |
Fast wiring check for build/data/artifact flow | 50 epochs, no realtime gate |
realtime |
Robotics 5 Hz replay triage | 200 epochs, realtime_factor >= 1.0, effective_epoch_rate_hz >= 5.0 |
full |
Dataset-level availability plus runtime gate | Full run, positioning rate plus realtime gates |
The summary includes:
| Metric | Meaning |
|---|---|
solver_wall_time_s |
Host wall-clock time spent solving the replay |
realtime_factor |
Dataset time span divided by solver wall time |
effective_epoch_rate_hz |
Solved epochs per wall-clock second |
For a 5 Hz GNSS stream, realtime_factor >= 1.0 and
effective_epoch_rate_hz >= 5.0 mean the offline solver is faster than the
recorded stream on that machine. Live deployment still needs serial/NTRIP I/O
margin, but this smoke catches obvious CPU regressions before field testing.
positioning_rate_pct is computed against the full reference trajectory, so it
is intentionally not gated by default for bounded --max-epochs smoke runs.
For a full replay, add --positioning-rate-min <pct> when you want availability
and runtime checked together.
Open the web UI after the smoke run:
python3 apps/gnss.py web --port 8085 --root .
The Robotics realtime smoke panel shows pass/fail state, realtime factor,
epoch rate, wall time, solution span, tuning knobs, threshold comparisons, and
direct links to the summary JSON, .pos, reference, rover/base/nav, and run
directory artifacts. That panel is the fastest way to debug whether a robotics
run is CPU-bound, data-limited, or failing a gate. Failed runs show why:
lines such as realtime factor 0.98 < 1.00 or
effective epoch rate 4.9 Hz < 5.0 Hz.
Docker path¶
The runtime image installs the gnss dispatcher on PATH. Mount the repo or a
dataset directory into /workspace:
docker build -t libgnsspp:latest .
docker run --rm -it -p 8085:8085 -v "$PWD:/workspace" \
libgnsspp:latest web --host 0.0.0.0 --port 8085 --root /workspace
For batch runs:
docker run --rm -it -v "$PWD:/workspace" libgnsspp:latest solve \
--rover /workspace/data/PPC-Dataset/tokyo/run1/rover.obs \
--base /workspace/data/PPC-Dataset/tokyo/run1/base.obs \
--nav /workspace/data/PPC-Dataset/tokyo/run1/base.nav \
--mode kinematic \
--preset low-cost \
--ratio 2.4 \
--max-epochs 200 \
--out /workspace/output/adoption/tokyo_run1_rtk.pos
ROS2 receiver loop¶
The ROS2 package is under ros2/gnss_raw_driver. It publishes:
| Topic | Type | Use |
|---|---|---|
/gnss/fix |
sensor_msgs/NavSatFix |
Standard robot localization input |
/gnss/raw |
gnss_raw_driver/GnssRawEpoch |
Parsed pseudorange/carrier/Doppler/SNR |
/gnss/raw_binary |
std_msgs/UInt8MultiArray |
Lossless rosbag capture for replay |
Run the ROS2 field doctor before plugging the output into a robot:
python3 apps/gnss.py ros2-doctor --device /dev/ttyUSB0
It checks whether ros2 and colcon are on PATH, whether the package has
been built and sourced, whether the serial device exists and is readable, and
prints copy-ready launch, rosbag record, and topic debug commands. Use
--json when you want the same checks in CI or a field-laptop diagnostic
report.
Build the ROS2 package:
cd ros2
colcon build --symlink-install --packages-select gnss_raw_driver
source install/setup.bash
Start a serial receiver. protocol:=auto detects UBX or SBF once enough bytes
arrive:
ros2 launch gnss_raw_driver gnss_raw_driver.launch.py \
device:=/dev/ttyUSB0 \
baud_rate:=115200 \
protocol:=auto \
frame_id:=gnss \
publish_raw_binary:=true
Record the raw stream and the standard fix topic:
ros2 bag record /gnss/raw_binary /gnss/raw /gnss/fix
Inspect the bag before you leave the test site:
python3 apps/gnss.py ros2-bag-doctor \
--bag <bag-directory> \
--summary-json output/ros2_bag_doctor_summary.json
The doctor reads metadata.yaml plus sqlite storage directly, so it does not
need ROS2 Python imports. For sqlite bags it reports topic types, message
counts, duration, estimated rates, timestamp gaps, and whether
/gnss/raw_binary is available for lossless decoder replay. For MCAP bags, it
uses the optional Python mcap package when available to measure message-level
rates and gaps from the .mcap file. If that package is not installed, it falls
back to metadata.yaml and reports partial-metadata: topic presence, message
counts, and duration are checked, while message rates and timestamp gaps are
clearly marked as not measured. Missing /gnss/raw_binary means researchers can
still inspect /gnss/fix, but cannot replay improved decoders from the bag.
Create a field handoff report:
python3 apps/gnss.py field-report \
--out output/field_report.md \
--json-out output/field_report.json
The report aggregates setup readiness, ROS2 launch/record diagnostics, bag
doctor summaries, realtime smoke results, and next commands into one Markdown
file plus a machine-readable JSON artifact.
gnss web auto-discovers output/field_report*.json, links the Markdown/JSON
report, previews the Markdown, and shows the same next commands in the browser
for field debugging.
Decode a recorded raw-binary bag into ROS topics plus file artifacts:
ros2 bag play <bag-directory>
ros2 run gnss_raw_driver gnss_bag_processor_node --ros-args \
-p protocol:=auto \
-p output_pos:=output/adoption/bag_replay.pos \
-p output_kml:=output/adoption/bag_replay.kml
Integration checks¶
Use this checklist before wiring the output into a robot localization graph:
| Check | Why it matters |
|---|---|
frame_id matches your localization frame contract |
Prevents silent TF mistakes |
/gnss/fix.status.status changes across no-fix, float, fixed states |
Downstream filters can gate bad epochs |
covariance is present on /gnss/fix |
EKF/graph weighting is not arbitrary |
/gnss/raw_binary is recorded |
Decoder improvements can be replayed later |
gnss ros2-bag-doctor summary is archived |
Topic gaps and missing replay data are visible after the field test |
a *-signoff JSON is archived |
Performance regressions become comparable |
Next steps¶
- Use Dataset Gallery to add a public or internal dataset.
- Use Validation to choose the right sign-off command.
- Use Interfaces when you need CLI, Python, ROS2, and web entrypoints in one workflow.