The hedgehog test: why the mower needs to look down
Chris put it simply: "I don't know how you safely do this without lidar." RTK knows where the mower is. It knows nothing about what's in front of it. So Phase 3b gave the mower two lidars and an obstacle course, and asked one question: what does each lidar fail to see?
The physics budget, first
At 0.5 m/s, with about 0.15 s to notice (a 10 Hz lidar plus processing) and 1 m/s² of braking, the mower needs 0.075 + 0.125 ≈ 0.20 m to stop. So the safety field is a corridor the robot's width plus 10 cm, out to 0.7 m past the nose (stop the drive and the blade), with a slow-down field out to 1.5 m.
The safety gate
A new node, safety_gate, sits between every driver and the motors:
driver → /cmd_vel_raw, /blade/cmd_vel_raw → [safety_gate] → /cmd_vel, /blade/cmd_vel → robot
It can slow the robot, stop it, and stop the blade. It can never start the blade: it only passes through or zeroes what the driver asks for. On the real robot it's one layer above the hardware interlock, which doesn't trust it either.
The course
Six objects, one per run, 3 m ahead on the robot's path: a pole, a 20 cm toy box, a toddler lying across the path (25 cm tall), a 20 cm ball, a hedgehog (10 cm), and a garden hose (4 cm). Plus an empty control run, because a safety sensor that cries wolf gets switched off. Two lidars: - 2D: one horizontal scan plane in the nose, 15 cm above the grass. - 3D: 16 beams from −25° to +5°, 30 cm up, looking down at the lawn ahead.
Results (make obstacle-course)
| Obstacle | 2D lidar | 3D lidar |
|---|---|---|
| pole | stopped, 0.67 m to spare | stopped, 0.67 m to spare |
| box (20 cm) | stopped, 0.68 m | stopped, 0.67 m |
| lying child (25 cm) | stopped, 0.69 m | stopped, 0.69 m |
| ball (20 cm) | stopped, 0.68 m | stopped, 0.67 m |
| hedgehog (10 cm) | HIT, never seen | stopped, 0.67 m |
| hose (4 cm) | HIT, never seen | stopped, 0.67 m (seen at 1.25 m) |
| nothing (control) | no false stops | no false stops |
The 2D lidar's blind spot is everything shorter than its scan plane. On a lawn, that's exactly the stuff that matters: wildlife, hoses, toys, small children. A mower's safety lidar has to look down.
The bug that faked a miss
The first run said the pole wasn't seen, and the robot ended up 2.6 m past it. Gazebo's spawner ignores the pose
written inside an SDF string and uses its own -x/-y/-z (default: the origin). The pole had spawned inside the robot.
The report caught it because "clearance −2.61 m" isn't physically possible for a static pole.
The honest caveat
The simulated lawn is a perfectly flat plane, so "anything more than 3 cm above the ground" is a clean obstacle test. Real grass is 5–10 cm tall, bumpy and sloped. On a real lawn that rule sees obstacles everywhere. Separating grass from hedgehog is the actual hard problem, and it's next: realistic grass in the sim, ground segmentation, and a false-alarm rate as a first-class metric. Then moving obstacles: a dog crossing at full speed.