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.
This repository intentionally contains one sampling implementation:
mode1_centroid_anisotropic. It does not hide alternate sampling modes behind configuration flags.
| 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.
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 cpuCUDA 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:0The 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.
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.
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.
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.
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/qualificationThe 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.
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 mof tracking error and concluded that gamma.3/.5/1intersect 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 mand 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_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 |
|---|---|
![]() |
![]() |
| gamma 0.5 | gamma 1.0 |
|---|---|
![]() |
![]() |
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.
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 |
|---|---|
![]() |
![]() |
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 testAny 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 \
--gifThis 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.
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.
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.
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.
For every gamma and seed, run.py saves:
states,controls, and the exact per-steppoly_A/poly_bin 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 |
|---|---|
![]() |
![]() |
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 |
|---|---|
![]() |
![]() |
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.
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.
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.
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 frommetrics.jsonl;plot_rollout_gallery: overlaid raw-policy trajectories by round and gamma; andrender_expansion_mechanism: K plans, selected-B uncertainty, verifier positives, executed first action, accumulated positive/NVP states, and asigma(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.
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/.
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.
default_config.json— the entire public experiment contract.safe_mppi/config.py— typed loading and strict zero-margin/mode checks.safe_mppi/environment.py— dynamics, collision clearance, and goal.safe_mppi/geometry.py— uniform triangular polytope andH_P.safe_mppi/controller.py— mode-1 sampling, costs, and safety filter.safe_mppi/acquire.py— per-gamma execution, NPZ schema, and metrics.safe_mppi/visualize.py— reproducible rollout and level-set figures.safe_mppi/flow_model.py— lightweight task-sized conditional flow.safe_mppi/expansion.py— task-neutral B1 expansion loop.safe_mppi/expansion_visualize.py— result/gallery/video skeletons.
Run the semantic tests with:
python -m pytest -q- 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.















