Ahmed Doghri Logo Image
Ahmed Doghri

safepathshield

The policy wants the goal, the shield refuses the collision. safepathshield projects unsafe commands onto the barrier, and now ships the discrete-time filter that holds at every control rate instead of the one that only held at mine.

safepathshield robot path safety field
safepathshield working browser demo

A Learned Policy Is Not a Safety Case

A goal-seeking policy can be effective on average and still issue one command that crosses an obstacle boundary.

safepathshield wraps the nominal velocity in an analytical control-barrier projection: unchanged when the command is safe, minimally corrected when the barrier inequality would be violated.

The Demo

The nominal controller records 39 collision steps. The shield intervenes 75 times, keeps clearance positive, records zero collisions, and still reaches the goal with 0.05978 error.

The fixture is a deterministic single-integrator robot with one circular obstacle. Physical deployment needs full dynamics, uncertainty, actuator limits, state estimation, and hardware validation.

The Guarantee Only Held at One Control Rate

A safety filter enforces its inequality at the current instant, and then a real controller applies that velocity for an entire discrete time step. The projection checked the continuous-time condition and never accounted for how far the robot actually travels during the step.

At a slower but entirely ordinary 3.3 Hz control rate, the "safe" shielded controller drove straight into the obstacle. A randomized sweep over control rate, start position, and obstacle size found the original filter colliding in 21 of 40 tuning scenarios and 11 of 20 held-out ones, penetrating up to 0.29 units past the boundary. The discrete-time-exact projection, which solves for the true next position rather than a linear approximation of it, collides in zero scenarios on both sweeps.

A Safety Filter Has to Ship the Safe One

Auditing this page, I checked which filter the tool actually hands a user, and it was still the continuous-time one. The fix existed in a parallel module, the sweep proved it, the README explained it, and safepathshield demo ran the version that collides at half the control rates anyone would plausibly use. For a safety layer that's the whole ballgame: a correct algorithm nobody's code path reaches protects nothing.

The CLI, the JSON API, and the workbench now default to the discrete-time projection, and --legacy reproduces the published numbers exactly. The default reaches the same goal with the same 75 interventions. Tests assert the shipped entry points are bound to the fixed filter, that it collides on none of the 60 scenarios, and that the legacy filter still collides on some, which is the reason the default changed. 13 tests pass across Python 3.10 through 3.13.

Research Basis

Current work combines learned control-barrier functions with formal verification and reinforcement-learning safety layers. Read the control-barrier verification work at PMLR 2025.

Try It

No dependencies, and it writes an SVG of the path:

git clone https://github.com/ahmeddoghri/safepathshield && cd safepathshield
pip install -e .
safepathshield demo                 # discrete-time exact, the shipped default
safepathshield demo --legacy        # the continuous-time filter
python -m safepathshield.eval_v2    # 60 scenarios: 32 collisions vs 0
safepathshield render path.svg      # see the correction happen
safepathshield serve                # workbench on :8090

Tools Used

Python
Robotics
Control Barrier Functions
Safe RL
Discrete-Time Verification
Simulation
Docker