Skip to content

Repository files navigation

safeMPPI demo in 3D

A small, standalone reference for collecting 3D SafeMPPI rollouts from a point-mass double integrator. The package exposes the taskspace and controller recipe in one JSON file, runs the full gamma grid, records every actual rollout, reports safety/performance metrics, and renders the BLUE nominal polytope with its ten horizon level sets.

Four-pillar in-distribution trajectories colored by gamma

This repository intentionally contains one sampling implementation: mode1_centroid_anisotropic. It does not hide alternate sampling modes behind configuration flags.

Project status

stage status where
Define taskspace, start/goal, obstacles, and double-integrator dynamics implemented default_config.json, environment.py
Define the current mode-1 SafeMPPI recipe implemented controller.py
Acquire data for every gamma, compute metrics, and render figures implemented acquire.py, visualize.py
Task-agnostic B1 Safe Flow Expansion core implemented expansion.py, flow_model.py
Expansion result/gallery/video skeletons implemented expansion_visualize.py
Frozen flow policy → unchanged offline deployment loop diagnostic bridge flow_deployment/
Lab-native pretrained flow → online/frozen handoff implemented flow_deployment/minhyuk_handoff/

The expansion core is deliberately separated from task facts. A new 3D task must provide its own context, dynamics, nominal H_P gate, full-H verifier, and execution cost through the documented adapter. The core does not pretend that the current nominal-polytope controller is a full verifier.

Quick start

Python 3.10 or newer is required.

python -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
python run.py --config default_config.json --output outputs/default_run --device cpu

# Requested biased 20-inch-ball demonstrations (8 paired seeds x 4 gammas)
python run.py --config configs/ball_biased_demo.json \
  --output outputs/ball_biased_demo --device cpu

CUDA is optional. To use physical GPU 2 while keeping the process-local device name unambiguous:

CUDA_VISIBLE_DEVICES=2 python run.py \
  --config default_config.json \
  --output outputs/default_run \
  --device cuda:0

The run produces one NPZ file per episode, manifest.json, metrics.csv, metrics.json, a 3D gamma-colored rollout overlay, and a BLUE nominal-level-set figure.

Experiment 1 — lab-frame SafeMPPI and trajectory tracking

configs/experiment1_lab_ball.json is the canonical quick handoff to the deployment team. It gathers one accepted SafeMPPI reference for each gamma and carries the external setpoint-tracking settings in the same file.

major element Experiment 1
start / goal (-2.1,1.5,.9) / (.7,-1.5,.9) m
obstacle sphere at (-.7,0,.9) m, radius .379 m
gamma .1,.3,.5,1, one accepted reference each
nominal polytope 80 faces, 2 m sensing, H=10
proposal geometry centroid steering off; anisotropic sampling off
lower-route bias .75 exp((z-.9)/.08)
reference limits .3 m/s², .7 m/s, .3 m/s vertical
setpoint follower (p_{\rm cmd}=.85p_{\rm measured}+.15p_{\rm stored})
generative policy completed raw-command raw10 checkpoint; see flow_deployment/minhyuk_handoff/
# 1. Raw SafeMPPI simulation: one accepted reference and nominal-polytope
#    visualization per gamma.
python run.py \
  --config configs/experiment1_lab_ball.json \
  --output results/lab_ball_pretrain/experiment1_one_per_gamma_s0 \
  --device cpu

python scripts/visualize_lab_ball_demos.py \
  --demo-dir results/lab_ball_pretrain/experiment1_one_per_gamma_s0 \
  --output-dir results/lab_ball_pretrain/experiment1_one_per_gamma_s0/qualification

# 2. Fly them, or run the same loop offline first.
#    See crazyflie_sim/hardware (lab-local) or deploy_sim/run_offline.py.

The four accepted SafeMPPI references all reach the goal without collision. Their minimum clearances are .182/.088/.047/.005 m for gamma .1/.3/.5/1.

Hardware result (2026-07-26)

Live SafeMPPI — replanning every 100 ms from the measured Vicon position, not replaying a stored reference — was flown at all four gammas on a Crazyflie 2.1. All four reached the goal, none aborted, none touched the obstacle, with +.215/.094/.162/.066 m between the drone shell and the ball surface.

The measured position tracked the controller's own predicted reference to 27-31 mm RMSE (52 mm worst case), consistent across every gamma. The control loop held its 100 ms period to within +0.5% with zero overruns in 343 cycles.

This retired the calibrated deployment plant. That plant had been fitted to a flight recorded before a control-loop timing bug was fixed, and afterwards predicted 176-242 mm of tracking error where the vehicle showed 27-31 mm -- 5.7-8.2x pessimistic. It wrongly predicted that gamma .5 and 1 would strike the obstacle. deploy_sim/plant.py and safe_mppi/lab_plant_replay.py were removed on 2026-07-26; deploy_sim/vehicle.py replaces them and has no fitted parameters. See deploy_sim/README.md.

Real-time SafeMPPI: model validation against hardware

Experiment 1 raw SafeMPPI nominal polytopes

The files the deployment team should inspect are:

role authoritative code
Experiment 1 values configs/experiment1_lab_ball.json
nominal-polytope SafeMPPI safe_mppi/controller.py
once-only acceleration/velocity governor safe_mppi/environment.py
collection and accepted-reference archive safe_mppi/acquire.py
nominal-polytope visualization safe_mppi/visualize.py
offline deployment vehicle deploy_sim/vehicle.py
frozen-reference schema flow_deployment/lab_reference_contract.py
pretrained policy handoff flow_deployment/minhyuk_handoff/
online deployment runner scripts/run_lab_flow_deployment.py
frozen trajectory exporter scripts/export_lab_flow_frozen_references.py

The current lab-native checkpoint predicts raw H=10 accelerations from the 10-D context ([g-p,v,b_{\rm near}-p,\gamma]). It applies no internal governor. Online deployment rebuilds the context from the current measured position at every replan; frozen export applies ReferenceGovernor exactly once and writes dense_positions, governed executed_controls, and raw controls. The current checkpoint is not a visual-encoder model, and it is not a flight-safety guarantee.

Full 50-per-gamma pretraining archive

configs/lab_ball_pretrain.json is the lab-frame data contract for the next expansion task. It does not transform the old (0,0,2) -> (3,0,2) policy. It regenerates SafeMPPI demonstrations directly in the deployment coordinates while leaving every file under deploy_sim/ unchanged.

item fixed value
taskspace / soft geofence x=[-2.5,1.3], y=[-1.7,1.8], z=[.4,2.0] m
start / goal (-2.1,1.5,.9) / (.7,-1.5,.9) m
sphere midpoint (-.7,0,.9) m, radius .379 m
gamma / accepted demos .1,.3,.5,1 / 50 per gamma
horizon / dt / samples 10 / .1 s / 512
MPPI temperature / Gaussian sigma .02 / (.5,.5,.5)
raw command cap .3 m/s^2 per axis
centroid / anisotropic proposal disabled / disabled
initial command (0,0,0) m/s^2
running / terminal / control / smooth cost .25 / 80 / .05 / .35
soft-clearance weight / target 60 / .3 m
progress weight 2
below-ball bias .75 exp((z-.9)/.08)
outside-taskspace cost 5 sum_i expm1(d_i/.05)

The lab collector applies the Minhyuk reference governor directly; it is not an opt-in ablation for this task. The lab config fails closed unless the governor constants and success-only acceptance contract match exactly. For raw SafeMPPI command (u_t^{raw}),

u_t = .4 u_t^raw + .6 u_{t-1}
repeat 10 times at dt=.01:
    v <- v + dt u_t
    v <- min(1, .7/||v||) v
    v_z <- clip(v_z, -.3, .3)
    p <- p + dt v

SafeMPPI predicts this governed reference motion while retaining Minhyuk's cost on the raw sampled commands. The archive stores controls (raw commands, the behavior-cloning target) and executed_controls (once-smoothed reference accelerations) separately. A deployment harness must therefore smooth the learned raw command exactly once.

Collection retries until it has 50 collision-free, in-bounds, goal-reaching rollouts with nonnegative executed one-step nominal-polytope slack for every gamma. The manifest separately retains every rejected attempt, so accepted archive SR=1 is never presented as the planner's pre-retry SR.

python run.py \
  --config configs/lab_ball_pretrain.json \
  --output results/lab_ball_pretrain/native_governed_w075_50pg_s0 \
  --device cpu

python scripts/visualize_lab_ball_demos.py \
  --demo-dir results/lab_ball_pretrain/native_governed_w075_50pg_s0 \
  --output-dir results/lab_ball_pretrain/native_governed_w075_50pg_s0/qualification

The seed-0 archive required 85/90/120/153 attempts to obtain 50 accepted rollouts at (\gamma=.1/.3/.5/1). These are the pre-retry diagnostics, not metrics computed only on the accepted subset:

gamma attempt SR attempt CR crossing below (z=.9) accepted clearance [m] accepted time [s]
.1 1.000 0.000 1.000 .159 8.05
.3 .800 .200 1.000 .061 7.68
.5 .483 .517 1.000 .037 7.66
1 .327 .654 1.000 .029 7.53

The configured command/reference caps were met exactly in the archive: maximum raw component acceleration .30000001, maximum reference speed .70000008, and maximum vertical speed .30000001 in float32 arithmetic. Replaying every raw command through the governor reconstructs all 200 stored state and dense-position arrays with zero error.

Lab-frame one-ball pretraining demonstrations

Reference-domain properties of the accepted archive

Every stored demonstration is a collision-free, in-bounds, goal-reaching reference by construction. For all 200 accepted references the reference-domain SR/CR is 100%/0% at every gamma, with minimum clearances .159/.061/.037/.029 m for gamma .1/.3/.5/1.

The "already clipped, therefore slow and easy to track" hypothesis does not hold for the present cost. The raw .3 m/s^2 component cap is active on 81/65/61/60% of 10 Hz steps, and the .7 m/s reference speed cap is active on 17/29/31/33% of 100 Hz points for gamma .1/.3/.5/1.

Removed 2026-07-26. This section previously reported a replay of these references through a calibrated deployment plant, which showed .244-.286 m of tracking error and concluded that gamma .3/.5/1 intersect the sphere. That plant was fitted to a flight recorded before a control-loop timing bug was fixed. On hardware, after the fix, live SafeMPPI tracked its own reference to .027-.031 m and no gamma collided. The plant was 5.7-8.2x pessimistic and its conclusions did not survive contact with the vehicle. The replay pipeline (safe_mppi/lab_plant_replay.py, scripts/replay_lab_ball_references.py) and the plant itself were deleted. High-gamma clearance is still the number to watch — but against measured tracking error, not a model's.

The attempt pool must not be confused with this accepted-reference replay. Finite MPPI sampling occasionally makes all 512 H-step proposals infeasible; the current controller then executes its explicitly coded least-bad fallback. Those rejected attempts explain why obtaining the 50 accepted references took 85/90/120/153 attempts across gamma. They do not enter the pretraining archive, and they are not evidence that the nominal-polytope test itself passed an unsafe sequence.

safe_mppi/lab_flow_task.py is the only loader for training a future lab-frame raw-command flow policy from this archive. It uses a 13-D Markov context [ c_t^{lab}=[c_t^{ball}\in\mathbb R^{10}, u_{t-1}^{applied}\in\mathbb R^3] ] and keeps the H-step target as raw commands. The legacy 10-D pretrain_ball_flow.py path is deliberately not wired to this archive because it would silently replay governed states as if raw commands had been applied.

Two limitations are intentionally explicit. First, an all-full-H-infeasible sample bank can still yield a one-step-admissible fallback; this is counted in the attempt diagnostics and is not a full-H verifier guarantee. Second, the lab flow pretraining/evaluation/expansion runner is the next stage; only its governor-aware data/context adapter is committed here. Multiple-ball policy conditioning is also not implemented yet; the present context represents one sphere.

A deploy_sim seed-0 smoke run is a stricter, separate check of the deployment layer. Note that the earlier version of this paragraph reported gamma .3/.5/1 aborting into the sphere or fence; those aborts came from the calibrated plant deleted on 2026-07-26 and were not reproduced on hardware, where all four gammas flew clean. This archive is still controller/reference-domain pretraining data, not a flight-safety certificate.

Ball-below variant

ball_below_config.json + docs/BALL_BELOW.md define a second experiment: start (0,0,2) to goal (3,0,2) with a 20-inch ball at (1.5,0,2), an exponential altitude penalty w exp((z-2)/T) that keeps every rollout below the ball's latitude-0 circle while the seed family fans out across passage angles, a 1 m/s^2 demonstration cap, and a [0.1,0,0] warm-start bias. Analyze a finished run with python -m safe_mppi.ball_analysis --run <output_dir> and render the animated rollouts with python -m safe_mppi.ball_gif --run <output_dir>.

All ten seeds per gamma with the evolving translucent nominal polytope (camera orbits so the level bands are seen from different angles); right panel is the head-on view from the start:

gamma 0.1 gamma 0.3
ball-below gamma 0.1 ball-below gamma 0.3
gamma 0.5 gamma 1.0
ball-below gamma 0.5 ball-below gamma 1.0

Default experiment

All user-facing values live in default_config.json.

setting default
taskspace [0,5] x [0,5] x [0,3] m
start position (0,0,2) m, velocity (0,0,0) m/s
goal (5,5,2) m
dynamics 3D double integrator, dt=0.1 s
planning horizon H=10
samples per control period 512
gamma grid .1,.2,.3,.4,.5,.7,1
Gaussian sigma (1,1,1) m/s^2
MPPI temperature 0.1
demonstration control cap 1.2 m/s^2 per axis
platform authority, metadata only 3.0 m/s^2 per axis
sensing range 2.0 m
nominal base 80 triangular faces
smoothness weight 0.12
soft proximity preference 25 relu(0.10-clearance)^2
point-robot/hard obstacle/safety/gain/ZOH margins all exactly 0
paired episodes one seed per gamma

The default scene has no obstacles. It is a configuration and execution smoke test, not a safety benchmark. Consequently, collision rate is zero and clearance is reported as null.

Biased 20-inch-ball demonstration task

configs/ball_biased_demo.json is the small-data task requested for the next expansion experiment. “20-inch ball” is interpreted explicitly as a 20-inch diameter: the sphere radius is 0.254 m.

item fixed value
start / goal (0,0,2) m / (3,0,2) m
sphere center (1.5,0,2) m, radius 0.254 m
gamma .1,.3,.5,1
paired demonstration seeds 0,...,7 for every gamma
demonstration acceleration cap 1 m/s^2 per axis
initial warm action (0.1,0,0) m/s^2
horizon / MPPI samples 10 / 1536
running / terminal / control / smoothness 5 / 20 / .05 / .8
proximity / progress auxiliary costs 0 / 0
lower-half bias .05 exp((z-2)/.012) per predicted state

Centroid steering is disabled (centroid_gain=urgency_floor=0, sigma_aniso=1). Thus the only ranking terms are running state error, terminal state error, control effort/smoothness, and the declared lower-half exponential bias. No proximity preference or hidden obstacle cost is used.

The checked-in eight-seed result is fully successful and collision-free. Near the ball (1.1 <= x <= 1.9), every saved trajectory stays below the equatorial plane; the largest observed z is 1.943 m. Reproduce that predicate with python scripts/audit_ball_demo.py. The empirical averages are:

gamma SR CR minimum clearance [m] time-to-goal [s] control variation below-plane fraction
.1 1.00 0 .258 4.46 .811 .958
.3 1.00 0 .115 4.33 .747 .977
.5 1.00 0 .130 4.74 .753 .976
1 1.00 0 .113 4.21 .779 .976

The strongest qualitative predictions hold: gamma .1 keeps the largest clearance, gamma 1 is fastest, and gamma .5 has lower control variation than gamma 1. Adjacent gamma averages are not strictly monotone; the code does not add gamma-specific costs to manufacture that ordering.

all paired rollouts paired rollouts separated by gamma
Biased ball rollouts Per-gamma biased ball rollouts

Nominal polytopes and H_P levels

Deployment testing (deploy_sim/)

The controller here has been flown on a real quadrotor. What breaks real flights is rarely the planning geometry — it is the deployment layer: a finite replan rate, setpoint streaming, a vehicle that lags and overshoots, noisy state estimates, geofences and dropouts.

deploy_sim/ runs a controller through the same loop used on the vehicle, against a quadrotor model with those properties (real onboard Mellinger gains, thrust limits, estimator lag, measurement noise, a jittery ~90 Hz clock). It needs nothing beyond requirements.txt — no simulator, no ROS, no motion capture, no vehicle:

python deploy_sim/run_offline.py --config configs/crazyflie_mppi_corner.json
python deploy_sim/run_offline.py --controller mymodule:MyController   # your own
python deploy_sim/run_offline.py --fault-freeze-at 3.0                # dropout test

Any object with reset() and plan(state, goal, gamma, seed) -> (accel, info) can be tested. See deploy_sim/README.md for the protocol, what the model does and does not capture, and how to read the safety summary; docs/EXPERIMENT_PARAMS.md records the parameters used in the real trials and the measurements behind them.

The temporary flow_deployment/ adapter loads the canonical frozen flow checkpoint and calls it from that unchanged deployment loop. It maps the lab start/goal/sphere into the policy frame, retains the complete controller trace, and produces native deploy_sim outputs plus a frame-comparison figure:

python scripts/run_flow_deployment.py \
  --episodes 20 \
  --output outputs/flow_deployment/pretrained_corner \
  --gif

This is an offline software-interconnection diagnostic. It performs no online expansion or motion-capture collection and supplies no flight-safety guarantee. The adapter pins every Minhyuk deployment file by SHA-256 and refuses to run if one changes.

Dynamics and task geometry

The state is x=[p,v] in R6 and the controller commands acceleration u in R3:

p[k+1] = p[k] + dt v[k] + 0.5 dt^2 u[k]
v[k+1] = v[k] + dt u[k]

The taskspace is a rectangular box. Static sphere rows are [x,y,z,radius]; vertical cylinder rows are [x,y,radius] and span the full taskspace height:

"obstacles": {
  "spheres": [[2.5, 2.5, 2.0, 0.4]],
  "cylinders": [[3.5, 1.5, 0.3]]
}

The requested default start and goal lie on taskspace faces. They are not silently shifted inward. That makes the corresponding box margin zero at those points; visualization therefore chooses a strict-interior rollout state when it needs explicit polytope vertices.

BLUE nominal polytope and H_P

At every control period, geometry.py builds

P_k = 2 m sensing polyhedron
      intersect obstacle tangent halfspaces
      intersect taskspace box halfspaces.

The sensing polyhedron comes from a once-subdivided icosahedron: 42 sphere vertices define 80 nearly uniform triangular faces. There is no Fibonacci direction index. Sphere and cylinder faces are tangent to the raw obstacle radius because every hard margin is fixed to zero.

For P={x: A x <= b} centered at the current robot position c, the normalized field is

H_P(x) = min_i (b_i - a_i^T x) / (b_i - a_i^T c).

H_P(c)=1 for a strict-interior center and H_P(x)=0 on the active boundary. The geometry audit below shows the shared 80 angular directions and the nonuniform radial samples used by the broader 3D experiment: 5 cm spacing through 0.2 m, 10 cm spacing through 0.5 m, then 1 m and 2 m context.

Uniform triangular nominal and H_P construction

The only sampler: mode 1

Mode1SafeMPPI shifts the previously optimized control sequence, adds Gaussian perturbations, and biases a subset of samples toward the nominal-polytope centroid when the local polytope becomes tight. Noise along that centroid direction is anisotropically amplified.

Each sampled horizon is rejected when any step violates

H_P(q[h+1]) >= (1-gamma) H_P(q[h]).

Smaller gamma contracts admissible motion more gradually; gamma 1 admits the raw H_P>=0 boundary immediately. The cost ranks feasible samples using goal error, control effort, the user-approved control-smoothness term, progress, and a soft proximity preference. The proximity term changes ranking only—it does not enlarge obstacles.

Only the first averaged action is executed. The next control period senses again and rebuilds the polytope. If every sampled horizon is infeasible, the implementation explicitly ranks samples by their worst H_P value; the saved online_one_step_slack lets downstream checks detect whether the executed fallback remained admissible.

Outputs and metric definitions

For every gamma and seed, run.py saves:

  • states, controls, and the exact per-step poly_A/poly_b in compressed NPZ form;
  • sampled feasible fraction and executed one-step barrier slack;
  • success rate (SR): reached the goal without collision or taskspace violation;
  • collision rate (CR): any negative dense-time obstacle clearance;
  • average minimum obstacle clearance;
  • average time to reach the goal among reaching episodes; and
  • taskspace-violation rate and average planning time.

The checked-in one-seed reference metrics are in examples/default_metrics.csv. These are mechanism checks, not M25/M100 statistical claims.

default output nominal level sets
Default gamma rollout overlay Default BLUE nominal level sets

ID/OOD mechanism gallery

The repository also preserves the generated conceptual and actual-rollout visuals from the dense pillar experiment. They demonstrate the uniform H_P field, all ten horizon levels, gamma masking, and the distinction between the BLUE online nominal polytope and GREEN post-hoc SOCP verifier.

in-distribution: four pillars/open middle OOD: restored center pillar + overlapping spheres
ID gamma mask OOD gamma mask

See the complete per-gamma visual atlas for all 14 actual rollout GIFs and their outcomes. The GIFs are evidence imported from the broader experiment; run.py in this minimal repository generates static PNG figures, not those detailed three-panel animations.

Standalone B1 Safe Flow Expansion

The implementation in safe_mppi/expansion.py is a single AFE arm: no expert fallback, demo replay, proximal leash, curriculum, recovery starts, rollback, or evaluation temperature search. ConditionalFlowMLP is a lightweight default, but a task can replace it while preserving the same expansion loop.

For candidate plan U, context c, and optionally its paired initial flow base x_0, the current policy supplies the penultimate noised representation

phi_s(U,c,x_0) = trunk([(1-s)x_0+sU, c, time(s)]).

The paired form is enabled explicitly; endpoint-only phi_s(U,c) remains available for controlled ablations. Normalize the representations and initialize the RBF length scale from 50 pretrained samples:

ell = mean_{i<j} || normalize(phi_i) - normalize(phi_j) ||_2
k(z,z') = exp(-||z-z'||^2 / (2 ell^2))
sigma_n^2(z) = k(z,z) - k(z,Z+) [K(Z+,Z+) + lambda I]^{-1} k(Z+,z).

The default recent_current_phi reference re-embeds recent full-verifier positives under the current round's policy. The implementation also exposes named, auditable frozen/cumulative/sliding references for experiments; a numerical GP matrix is never carried across a representation update. For successful-trajectory sliding references, rows are sampled uniformly over the complete successful trajectories rather than taking only late FIFO windows. At each active context, sample K plans and acquire B sequentially:

pi(j | pending) proportional to exp((sigma_j - max sigma) / beta).

Every successfully evaluated selected-B query enters D under the default archive rule; only full-H safety positives enter D+. Archive rules that commit only complete successful executed trajectories are separate options and are never silently mixed with query-level replay. The task adapter, not the generic core, defines GREEN validity, progress, terminal-tail handling, and the execution ranking. No eligible plan means NVP; the BLUE nominal one-step value is diagnostic only.

After a round, recent D+ replay has equal mass over gamma -> (round, episode) -> context -> positive query. With optimizer_steps_per_round=None, every eligible positive is used exactly once without replacement; microbatch_repeats controls repeated optimization on each microbatch separately. Optional negative gradients use

g = g_positive - rho g_negative,
rho = alpha ||g_positive|| / (||g_negative|| + eps).

alpha=0 is exactly the positive-only update. NVP-context queries are the negative population; they do not become positive GP observations.

checkpoint_000.pt is always the untouched pretrained policy. Updates produce checkpoint_001.pt,...,checkpoint_N.pt; result curves and galleries must evaluate those saved checkpoints with a separate, untilted raw-policy seed bank.

Variables to change first

These are code defaults, not a universal tuned recipe.

variable default meaning
rounds / parallel episodes per gamma 10 / 2 independent synchronous trajectories
K / B 16 / 4 generated plans / sequential full-verifier queries
batch / optimizer steps / microbatch repeats 32 / exact pass / 1 replay optimization budget
learning rate / first-layer scale 3e-5 / 1 first layer can move more slowly during expansion
replay window W 2 rounds labeled replay horizon
RBF GP cap / noise 256 / 1e-2 uncertainty reference size and regularization
GP reference recent_current_phi re-embed recent positives under current phi_s
sliding-row selector trajectory_uniform sample throughout each successful trajectory
beta / adaptive beta / ESS target .05 / false / .5 fixed tilt by default; adaptive ESS is opt-in
flow base std / paired representation 1 / false base sampling and noised-feature ablation
archive / execution all_queries / min_cost query-level data; task-native ranking
negative alpha 0 positive-only update

For a fixed initial beta, collect representative uncertainty score pools, call calibrate_fixed_beta(pools, target=.5), write the returned scalar into ExpansionConfig.beta, and leave adaptive_beta=False. Do not choose beta from the absolute magnitude of sigma alone.

Porting to another 3D task

Implement the five methods in ExpansionTask: reset, context, verify, advance, and terminal. The verifier returns Verification(valid, hp_eligible, margin, execution_cost, progress, progress_eligible, error). That is the only place where task-specific dynamics, a 3D nominal polytope, SOCP geometry, progress, and the native SafeMPPI cost belong. Supply 50 pretrained embeddings or an explicitly justified RBF length scale, then call run_safe_expansion.

expansion_visualize.py provides three task-neutral outputs:

  • plot_expansion_results: result curves from metrics.jsonl;
  • plot_rollout_gallery: overlaid raw-policy trajectories by round and gamma; and
  • render_expansion_mechanism: K plans, selected-B uncertainty, verifier positives, executed first action, accumulated positive/NVP states, and a sigma(phi_s) colorbar.

Pass an event_callback to run_safe_expansion to receive the state before/after replanning, all K candidates and marginal uncertainties, selected B indices, verifier outputs, and the executed index. The task adapter converts candidate controls to 3D paths and nominal/verifier polytope vertices before constructing MechanismFrame; the generic core does not guess that geometry.

Reference mechanism video: B1 rounds 0/5/10/15.

Ball-task deployment: pretraining + expansion + representation audit

docs/BALL_FLOW_EXPANSION.md deploys this loop end to end on the 20-inch-ball task with the 10-D context c_t = [g-p, v, b_near-p, gamma] and a 30-D plan flow (scripts/pretrain_ball_flow.py, scripts/run_ball_expansion.py, scripts/evaluate_ball_expansion.py, safe_mppi/ball_flow_diagnostics.py). It reports raw temperature-1 success/collision, above/below/left/right route coverage, untilted GREEN-verifier validity, gamma trends against the SafeMPPI demonstrator, sigma-tilted acquisition anatomy, and an automated fixed-bank audit of the penultimate noised representation (t-SNE panels, local probes, and the high-sigma new-mode discovery rate), plus an automated data-size x model discovery sweep (scripts/sweep_ball_flow.py) and the final coverage video: expansion iterations progressively cover the ball with generated trajectories while the metric curves grow. Curated figures live in docs/assets/ball_flow/.

Mode presence: tilted acquisition vs raw sampling It is a format reference, not evidence for this new 3D ball task.

The portable canonical pretraining bundle is checked in at results/global50_reference/pretrain_global10_h48p32_s0: 200 successful SafeMPPI demonstrations (50 per gamma), 7,039 training windows, the 4,574-parameter 41 -> 48 -> 32 (=phi_s) -> 30 raw-time flow, calibration features, and its manifest. Its fixed raw audit is SR 0.4625 with window validity 0.9040; it is a reproducible starting policy, not a flight-readiness claim.

Code reading order

  1. default_config.json — the entire public experiment contract.
  2. safe_mppi/config.py — typed loading and strict zero-margin/mode checks.
  3. safe_mppi/environment.py — dynamics, collision clearance, and goal.
  4. safe_mppi/geometry.py — uniform triangular polytope and H_P.
  5. safe_mppi/controller.py — mode-1 sampling, costs, and safety filter.
  6. safe_mppi/acquire.py — per-gamma execution, NPZ schema, and metrics.
  7. safe_mppi/visualize.py — reproducible rollout and level-set figures.
  8. safe_mppi/flow_model.py — lightweight task-sized conditional flow.
  9. safe_mppi/expansion.py — task-neutral B1 expansion loop.
  10. safe_mppi/expansion_visualize.py — result/gallery/video skeletons.

Run the semantic tests with:

python -m pytest -q

Scope and limitations

  • The robot is a point mass; real vehicle radius, tracking error, and hardware latency are absent.
  • Obstacles are static spheres or full-height vertical cylinders.
  • The SafeMPPI data collector implements online nominal H_P. The ball-task expansion adapter separately implements the cloned verifier's trajectory-fitted max-margin face constraints in 3-D for spherical obstacles; this GREEN verifier is not part of the expert controller.
  • The checked-in ball checkpoint is an ideal-double-integrator research model. It is not a Crazyflie flight certificate; deployment requires an explicit coordinate/action interface and independent evaluation on the target vehicle.
  • The 80x9 learned encoder, adaptive gamma, wind robustness, and moving-obstacle prediction are not implemented here.
  • The platform authority is recorded for downstream work, while this demonstration controller is intentionally capped at 1.2 m/s^2.
  • One paired seed per gamma is sufficient to reproduce the supplied visuals, not to support a general performance claim.

About

safeMPPI demo in 3d (optionally) safe flow expansion needed for the full run

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages