Can a social reflex stop a robot but never restart it? Testing a dual-dwell safety gate (Webots)
Can a social reflex stop a robot but never restart it? Testing a dual-dwell safety gate (Webots)
The author explores a safety architecture for robots where perception can stop movement but not restart it, using a dual-dwell system in a Webots simulation. The design includes a 'Doer-Checker' pattern, a reflex layer with hysteresis, and a latch mechanism to prevent premature restarts. The system is tested in simulation and verified through replay, though it is not intended for real-world deployment without human oversight.
The author presents a deterministic safety architecture for robots, using a dual-channel system with a 'Doer-Checker' pattern to ensure that perception can stop a robot but not restart it. The system is tested in simulation and includes a replay mechanism for verification, though it is not intended for real-world deployment without human oversight.
Source: ROS Discourse — Community updates · Read original article ↗
Article text · Original source · English
On this page
Hi everyone,
Following up on my earlier experiments with supervisory gates (Demo 8) : Webots Demo 8: Supervisory Gate Multi-Fault Lifecycle & Watchdog Hardening (QERRA-v2 Classical) I’ve built a new test to evaluate the recovery side of a safety event.
My underlying vision in robotics is harmonious human-robot social collaboration —specifically in vulnerable spaces like hospitals or eldercare facilities & beyond. We want robots to react when someone calls for help or collapses.
But here is the friction I constantly see: to make a robot socially aware, we have to rely on probabilistic AI models. If we let an uncertified perception model dictate the motor controllers, a lagging thread or hallucination can turn a 70 kg robot into a runaway hazard. I wanted to build a deterministic “floor” underneath the AI.
The safety contract I wanted to test is simple: Perception can take away the permission to move, but it can never give it back.
Before sharing the data, I want to be completely upfront. I am not a classically trained robotics engineer or a certified safety assessor. I am an independent solo developer in Greece, and I build this with AI coding tools (Claude, Gemini). I am sharing this here because I want to learn from those of you actually deploying robots around humans, and see if this architecture holds up. (If i ever get an answer!
).
The Architecture: Doer-Checker
I built a dual-channel pattern and tested it on a TIAGo base in Webots:
- The “Doer” (Navigation Brain): An unprivileged process that proposes velocities (2.00 rad/s) over UDP. It has no direct access to the wheels.
- The “Checker” (Supervisory Gate): A pure mathematical function step(state, params, inputs) → StepResult running on a strict 32 ms cycle. It is the only thing that writes to the actuators.
- The Reflex Layer (QERRA-HSR): Evaluates distress. It enforces a 1.0s hysteresis dwell before it steps down from CRITICAL to CLEAR, preventing the robot from “chattering” if camera confidence fluctuates.
- The Latch: After any trip, the Gate requires the cause to be clear for a continuous 1.0s, followed by an explicit operator reset.
The Run (14.0 Seconds, 438 Control Cycles)
This is a single continuous simulation take. Times below are from the audit log.
| Sim time | What happens | Gate Response |
|---|---|---|
| 0.03 s | Boot | TRIPPED (proposer not heard yet) |
| 1.312 s | Scripted reset | Accepted → RUN at 2.00 rad/s |
| 4.000 s | Distress injected (0.85 conf), HSR CRITICAL | TRIPPED on the same 32ms cycle (cycle 125), wheels clamped to 0.000 |
| 5.024 s | Scripted reset while distress is active | Refused (RESET_REJECTED_HAZARD) |
| 7.008 s | Injected distress removed | Still TRIPPED |
| 8.032 s | HSR back to CLEAR (after 1.0s hysteresis dwell) | Still TRIPPED, resets refused (RESET_REJECTED_NOT_READY) |
| 9.056 s | Reset on the first allowed cycle | Accepted → RUN at 2.00 rad/s |
| 14.016 s | End of run, log closed |
Note: “Same cycle” means the wheel command went to 0.000 on the exact control cycle the status became CRITICAL. (Time spent computing the Gate decision in Python: p50 = 1.38 ms, max = 4.50 ms).
A quick lesson learned: I initially predicted recovery at 8.2s. But there are actually two dwells stacking here: the reflex layer’s 1.0s dwell (to ensure the scene is calm), followed by the Gate’s own 1.0s internal safety dwell. The earliest reset possible was 9.03s. It was a great lesson in why writing down expected cycles before running is so important.
Replay Audit
Because the Gate is a pure function, every cycle is hash-chained into a JSONL log. Using an offline verifier (verify_log.py --replay), I fed the recorded inputs back through the function. All 438 cycles reproduced bit-for-bit with 0 execution divergences. (Head hash: eccb0d85416cf4a651205f34f3cc0d9275bff6e2b3baa512e4b23dbef0c2714a).
(Transparency note: While the replay is deterministic, live UDP on Windows is not. An earlier take of this scenario tripped at 10.2s due to a host scheduling load that stalled the proposer for 160ms. The gate failed-closed exactly as designed).
Honest Boundaries (What this does not show)
- No live detection. The 0.85 distress flag is a constant injected by my test script. Real CV detection (with false positives/negatives) is the hardest part, and is not tested here.
- No human in the loop. The resets are injected by a script to test the exact microsecond boundaries. Because of this, the video shows the robot auto-restarting after the dwells finish. In reality, a human must assess the scene before hitting that reset button.
- Not a certification claim. This is an independent simulation (Webots/Windows/Python), not an industrial RTOS. I am not affiliated with PAL Robotics.
My Questions for the Community
- If you run robots in shared spaces (hospitals, retail): after a protective stop caused by a person, what is your actual restart rule? Remote confirmation? A physical button on the chassis?
- Is “perception may only stop the robot, but never start it” a contract you use in practice, or do you let perception modify trajectories directly inside the safety layer?
- In a real ROS 2 stack, I envision putting a node like this directly between the final cmd_vel mux and the base controller, blocking all other publishers. Has anyone implemented a strict latch-and-reset pattern this deep in the stack? What went wrong?
Links:
- Video (31s, Webots window, unedited):
Evidence logs: QERRA-v2-classical/docs/demo9 at main · marunigno-ship-it/QERRA-v2-classical · GitHub
Thank you for your time, and I welcome any brutal feedback you have on this architecture.
Marussa
Source:ROS Discourse — Community updates · discourse.openrobotics.org