Skip to content

End-of-line test

One script, run on the finished robot, that checks every sensor, every actuator and the safety chain, then writes a pass/fail report and the end_of_line entry for the robot's build record. It's the last thing that happens to a robot before it's called built, and the first thing to rerun after a repair.

It uses only the robot's own sensors: wheel encoders, IMU, GNSS, lidar, battery. Nothing comes from the simulator's ground truth, so the script that passes in the simulator is the same script you run on the real robot.

make eol-run                      # in the simulator (a box is placed 3.5 m ahead for step 6)
make eol-run SERIAL=MB-0001       # the serial goes in the report and the build-record entry

On the robot: ros2 run mowlark_safety eol_test --ros-args -p serial:=MB-0001 -p blade:=false -p lidar:=false (H2: blade disconnected, no lidar yet; those rows report SKIP).

Before you run it on a real robot

  • About 4 m of clear, flat lawn ahead, nobody within 3 m, and a box (at least 20 cm tall) about 3 m in front of the robot.
  • The test drives the robot and, unless blade:=false, spins the blade. Keep a hand near the STOP button for the whole run.
  • Until gate 3 passes: blade:=false, the blade off the motor and the connector taped.

What it checks

Every command goes through the safety gate, exactly as a mowing mission's would, so the test exercises the whole chain and not just the motors.

Step What the test does Pass if
1 sensors Listens for 5 s with the robot still IMU, GNSS, joint states, lidar and battery at ≥ 90% of their rates; gravity 9.81 ± 0.3 m/s²; gyro bias ≤ 0.02 rad/s; RTK fixed with σ ≤ 5 cm; battery a sane 7S voltage and ≥ 30%; STOP not latched
2 wheels Forward at 0.2 m/s, pivot left, pivot right; then turns back to the start heading on the gyro Each wheel within 10% of the speed the command implies, with the right sign; IMU yaw rate matches within 15%. Catches a swapped motor, a reversed encoder, a wrongly mounted IMU
3 blade Spins up with the 3 s soft start, then commands it off 90% speed within 4 s; below 10% within 2 s
4 STOP Drives at 0.3 m/s with the blade on and presses STOP while the driver keeps commanding motion Standstill ≤ 1 s; blade below 10% ≤ 2 s; nothing moves for 3 s while latched; ring red; release (GO held 2 s) leaves it stopped
5 watchdog Drives with the blade on, then the driver goes silent Drive stops ≤ 1 s; blade below 10% ≤ 3 s
6 obstacle Drives at the box and keeps asking to drive on The safety gate stops it, ≥ 10 cm from the box
7 manual Lists what a script can't do Lift, tilt, bumper, unplugging each interlock connector, hanging the microcontroller: an operator does these from gate 3 (hardware/safety.md) and signs each one on the build record

The blade-stop limit (2 s) is our target, not a measured number: gate 3 measures the real motor's stop time and the limit is set from that, in writing, never quietly in code.

What it found in the simulator

The first run (eol-1) passed everything except the watchdog. When the driver went silent the robot simply carried on: Gazebo's drive plugin, like many real motor controllers, keeps executing the last command it got. Nothing in our software timed commands out. Now the safety gate does: a driver that wants motion has to keep saying so, every 0.5 s or less, or the gate sends zero.

The second run (eol-2) found something else: each time the blade was cut, the whole robot twisted by about 8°. The simulated blade motor had unlimited torque and stopped the blade within one 1 ms physics step; the reaction twisted the chassis. After STOP and the watchdog the robot pointed 16° off and drove past the box. The simulated motor is now torque-limited (±3 N·m), and the blade stops in 0.75 s, a believable number to test against.

Run Watchdog: drive stops Blade stop (commanded) Twist from the blade stops (ground truth) Result
eol-1 never 0.01 s 8° (one stop; the watchdog never cut the blade) FAIL
eol-2 (watchdog) 0.51 s 0.01 s 8° + 8° FAIL (missed the box)
eol-3 (torque-limited blade) 0.54 s 0.75 s < 0.1° PASS

Outputs

  • reports/<run_id>-eol.md: the table above, with every measured value.
  • reports/<run_id>-eol.json: the end_of_line block of the build record (serial, run id, time, every check's result and measurement).
  • The recorded bag (data/runs/<run_id>) and the eol_check telemetry events in Elasticsearch, like every run.