Skip to content

Safety

Quatern puts several layers between new code and a moving robot: verification, a human gate, a live watchdog, and the robot's own stop when commands go quiet. This page describes exactly what each layer does, and what none of them can do.

Quatern is not your robot's safety system

Quatern can't see a person who steps into the robot's path if the map didn't have them. It stops the robot with software commands, not by cutting power. You, as the integrator, remain responsible for the hazard assessment of your robot and its workspace. For any robot that can hurt someone, keep a physical emergency stop within reach.

The layers

From first to last, the stops that can end a run:

  1. The human gate. Nothing deploys without a person answering it.
  2. The watchdog. A separate process that checks the run 20 times a second and calls the robot's emergency stop on the first failed check.
  3. The robot's emergency stop. What the backend does on emergency_stop: on ROS 2, it publishes a zero command on every controller topic and true on the e-stop topic.
  4. The robot's own command-silence stop. The robot stops itself when no command arrives for 0.5 s. This runs on the robot (firmware or controller), not in Quatern.
  5. A physical e-stop, if you have one.

Layers 1–3 are software running on your computer. Layer 4 is what protects you when that computer, its connection or Quatern itself fails. Layer 5 is the only one that doesn't depend on software.

The deploy gate

quatern deploy (or /deploy) opens the gate for your pinned stack. Before it asks you anything, it checks preconditions. If any fails, it prints REFUSED: with every reason, and nothing moves.

Every target needs:

  • A stack for this robot that passed verification (READY) and has a motion plan.
  • A valid actuator calibration less than 1 hour old.
  • The map the stack was verified on: still stored, less than 24 hours old, and not replaced by a newer map of a changed environment.
  • The backend available, with its sensor streams healthy.

Hardware targets additionally need:

  • No placeholder values in the stopping-distance inputs, such as TODO(measure) in the URDF or an unmeasured braking deceleration.
  • A passing stop-on-silence check less than 1 hour old.
  • For robots above the e-stop energy threshold, a sandbox with real isolation (bubblewrap on Linux). On macOS, or without bubblewrap, heavier robots are refused.

A real refusal:

REFUSED:
  - bounded-travel inputs are still placeholders on a hardware target: brake_decel not measured: defaulted to max_linear_acceleration
  - stop-on-silence has not been verified on target 'hardware'; run `quatern doctor --robot c101.default --target hardware --check-silence`

When the preconditions pass, the gate screen shows what will run and every condition that will stop it:

DEPLOY GATE
  robot:      c101 (instance default)
  target:     sim (sim2d, not hardware)
  stack:      stk_c101.default_20261002T033939643180 [ready] from capture c101.default_2026-10-02_sample_v1
  plan:       36 waypoints over 2.54 in grid2d
  max speed:  0.996 rad/s on base/yaw (limit 1.500)
  duration:   10.8 s predicted
  will abort on:
    - the localizer's estimate, or the robot's own state, off the plan by more than: base 0.5, base/rad 0.8, joints 0.35
    - the localizer disagreeing with a source or with the plan past critical (planar 30%) for 5 ticks in a row, once moving 0.5 s
    - no estimate from the localizer for 0.50 s
    - a stream stale (0.50 s, or two periods of a slower source) or under 30% of its rate
    - any DOF outside its URDF bounds or over its velocity limit: base/x 0.45, base/y 0.45, base/yaw 1.5, ...
    - the scan disagreeing with the map (under 35% on mapped structure for 5 scans in a row)
    - an obstacle in the planned path that the map did not have, or a drop-off ahead
  bounded travel after an abort (m, worst case on base/x):
    watchdog path 0.248   silence path 0.450   (v_max 0.45, brake 0.5, detect 0.100s)

Bounded travel is the worst-case distance the robot can travel after an abort: through the watchdog path (detect, then brake), and through the silence path (if Quatern stops sending commands entirely and the robot's 0.5 s silence stop has to catch it).

Then you answer:

  • On hardware: Physical e-stop in reach? [y/N]. Any answer continues; it's recorded in the run record. --estop answers yes; --yes alone records no.
  • On every target: Proceed past the gate? THE ROBOT WILL MOVE. [y/N]. Anything but y prints gate declined; nothing moved.

The agent can prepare a deploy, but can't confirm one on hardware

A hardware deploy needs a one-time gate token, minted only when a person confirms the gate in quatern deploy. It's bound to the robot, target and stack and expires after 60 seconds. The agent has no way to mint one. On simulated and mock targets, the agent can deploy without asking.

The watchdog

During a run, the watchdog is its own process, with its own connection to the robot, so a stalled Quatern process can't stall it. It checks 20 times a second, in a fixed order, and the first failure stops the robot.

Check Stops the robot when Default
Localizer process it crashes or breaks its sandbox limits (time, memory, CPU) –
Quatern process the watchdog hears nothing from it 0.50 s
Localizer estimate no new estimate 0.50 s
Robot state its timestamp doesn't change 0.50 s
Joint limits a joint leaves its URDF bounds –
Speed a joint or the base goes over 1.1× its URDF velocity limit –
Sensor freshness a required sensor sends no new sample 0.50 s, or two periods of a slower sensor
Sensor health a required sensor, over a 2 s window, runs under 30% of its rate or drops more than 70% of samples –
Deviation the localizer's estimate, or the robot's own reported state, is too far from the plan base 0.5 m, heading 0.8 rad, joints 0.35 rad
Disagreement the localizer disagrees with a sensor, or with the plan, past the critical band for 5 checks in a row, after 0.5 s of motion planar 30%
Map agreement under 35% of lidar returns land on mapped structure, 5 scans in a row –
New obstacle lidar or depth returns appear in the path ahead where the map was free –
Drop-off the floor disappears ahead where the map had floor –

Warnings print during the run without stopping it:

deploy watchdog: base:localizer_vs_wheel_odom: the localizer and wheel_odom disagree by 18% of the distance travelled over the last 2.0s (warn 15%, critical 30%)

What the watchdog does not check:

  • People and things that were already in the map. The obstacle check only flags returns where the verified map had free space.
  • Sensors that keep sending the same values. Freshness is judged by timestamps. A sensor that keeps stamping new samples with frozen values is only caught indirectly, through deviation, disagreement or map checks.
  • Battery, geofences, or total distance. These aren't checked live.
  • Collisions for arms. Arm plans are checked against joint limits only.

How a run stops

When a check trips, the watchdog calls the robot's emergency stop once, then watches for up to 1 second until every velocity is near zero:

RECEIPT rcpt_c101.default_20261002T034222371708: ABORTED (STOP_OBSERVED)
  abort: deviation:base — the localizer puts base 0.528 from the plan (limit 0.500)
  stop: observed 0.00s after the stop request, travel after stop 0.002 (by watchdog)

If the robot doesn't visibly stop, Quatern doesn't pretend it did. The outcome is recorded as UNKNOWN:

RECEIPT rcpt_c101.default_20261002T034233726834: UNKNOWN (EFFECT_OBSERVED)
  stop: NOT OBSERVED, travel after stop 0.489 (by watchdog)

A normal run ends the same way: Quatern sends the stop at the end of the plan and confirms it.

Stopping a run yourself

  • Press Ctrl+C in the terminal running the deploy. Quatern stops the robot and records the run as CANCELLED.
  • --max-seconds N on quatern deploy ends the run after N seconds (TIMED_OUT).
  • Your physical e-stop, which works whatever state the software is in.

If Quatern or your computer fails

What fails What stops the robot
An error inside Quatern's run loop Quatern catches it, stops the robot, and records FAILED.
The watchdog process dies Quatern's run loop notices and stops the robot itself.
Quatern hangs The watchdog stops the robot after 0.5 s of silence from it.
Quatern is killed, the computer crashes, or the network or cable drops Nothing in Quatern. The robot's own 0.5 s command-silence stop has to catch it.

That last row is why the stop-on-silence check is required before every hardware deploy. Quatern verifies that your robot has this behavior; it can't provide it. It must come from the robot's firmware or controller.

Physical e-stop

Quatern recommends a physical e-stop for any robot that can hurt someone, and treats it as optional for desk-scale robots. It never refuses a deploy over it. On a hardware gate, it estimates the robot's energy (mass × speed²) from the URDF:

  • Above the threshold (10 by default): physical e-stop recommended: mass x speed^2 ~ ... ; not required, the software stops stay active
  • No mass in the URDF: energy class unknown (no <inertial> mass in the URDF); a physical e-stop is recommended if this robot can hurt someone

Your answer to Physical e-stop in reach? is recorded in the run record. Quatern can't check that the e-stop exists or works.

Run records

Every deploy ends with a receipt: a printed summary and a write-once JSON file.

RECEIPT rcpt_c101.default_20261002T033742538588: COMPLETED (STOP_OBSERVED)
  stop: observed 0.00s after the stop request, travel after stop 0.000 (by watchdog)
  max deviation base: 0.188
  live base:localizer vs wheel_odom: 4.5%
Final state Meaning
COMPLETED The plan ran, the stop was observed, and the robot ended within the deviation limits of the last waypoint.
ABORTED A watchdog check stopped the run. The receipt names it (abort: ...).
CANCELLED You pressed Ctrl-C.
TIMED_OUT --max-seconds ended the run.
FAILED An error ended the run, or the robot finished away from the last waypoint.
UNKNOWN A stop was sent but not observed. Treat the robot's state as unknown until you check it.

Receipts are saved in ~/.quatern/receipts/<robot>.<instance>/<stack-id>/<receipt-id>.json and are never overwritten. Each one records the robot, target, stack, recording, calibration and map it ran with; hashes of the code; the gate confirmation and your e-stop answer; commanded and observed motion; live cross-check results; the stop, with its timing and travel; and every warning. Use quatern deploy --json to get the full receipt on stdout.

Receipts stay on your computer.