# Sim-to-real drone · trainer guide

This guide covers Volume 2 of the sim2real course, the drone. Volume 1, the desk
arm, has its own trainer guide and kit.

Use the same course-owned gate version for a cohort: kit `2026.10.13`, recorded as
`kit_version` in every evidence file. All numerical thresholds are provisional
starting values; recalibrate after the simulator validation and record the changed
version before comparing learners. Automated PASS assesses submitted evidence,
while trainer approval assesses the implementation, provenance and physical work.
Do not publish invented timings, flights, R² or gate passes.

For every review, record learner/reviewer, date, implementation commit, config
hash, seeds, gate version, artifact hashes, raw log location, automated result,
review result and any required retry. Preserve failed trials. Read the reported
standard deviation and failures together with the mean. Scientific disagreement
with the expected story is not a failed course if the experiment is sound.

## Module 1 · Simulator

**Automated:** hover force within 1% of m·g; zero-initial-velocity freefall within
the explicit 1 mm operational tolerance at g = 9.81 m/s²; open-loop reference
positions within 2 cm and attitudes within 2° over 0.5 s, driven by the canonical
excitation from `bench.py m1-sequence`. Evidence includes both time series and
artifact hashes.

**Review:** inspect the pure `step`, Params and state interface, Newton–Euler
terms including rotor-acceleration reaction torque, quaternion normalization and
kinematics, rotor spin/mixer signs against `conventions.py`, RK4, first-order
motor lag, the load-dependent battery model, and the exact rate-controller/mixer
firmware revision. Confirm the reference comparison set motor lag to zero, removed
Bullet's default damping (`Physics.DYN` or `changeDynamics`), ran physics at
1 kHz and control at 100 Hz, and mapped gym-pybullet-drones' propeller order to
M1–M4. Confirm km came from the firmware's torque-to-thrust ratio and that the reference model was set to the same ratio; its own ratio is about 3.4 times larger. Replay a firmware state/log round trip
with known axis rotations and rate signs. Confirm the comparison is independent of
the learner implementation and has not been rescaled to match afterward. None of
the three checks moves air through a rotor: inflow and ground effect remain
unvalidated until flight data.

**Common failures:** grams in kg slots; degrees in radian slots; quaternion order
or body/world direction reversed; rotor signs swapped; reference defaults differ;
Bullet damping left on; g changed to 9.8 to match the reference; Euler integration
labeled RK4; short logs starting after an unstable transient. Ask the learner to
identify the first discrepant term before retuning a PID.

## Module 2 · Classical control

**Automated:** full 20-seed sim protocol for PID/LQR; nominal RMS <5 cm without
crashes/aborts; at least one perturbed cell with mean maximum error >30 cm or a
crash/abort, or else a documented stress sweep that breaks the baseline; written
cause for each controller/condition. Failed perturbed runs must remain in the
report.

**Review:** trace position error through attitude, thrust and body rates; inspect
anti-windup and actuator limits. Inspect the LQR linearization and controller-rate
zero-order hold. Confirm each named perturbation was applied, including the
thrust-map error, the fan jet's position dependence and turbulence, a midflight
step and delayed noisy observations. Review every seed and failure, with
explanations that distinguish saturation, bias, delay and transients. If the
baseline survived the prescribed set, check that the stress sweep changed only
perturbation magnitude, not the controller. The benchmark enforces 100 Hz sample
count within 1% (one-sample tolerance for short aborted logs), weights RMS/rate
metrics by elapsed time and rejects reuse of one source file or byte-identical
logs across runs. Inspect copied or synthetic source data separately; file
integrity cannot establish scientific authenticity.

**Common failures:** PID tuned separately on each evaluation condition; leaking
true physics into observations; noise applied to benchmark truth; ignoring the
seeded initial offsets; silently clipping the disturbance; deleting crashes;
weakening a robust baseline to manufacture a failure; omitting LQR; comparing
mismatched trajectories.

## Module 3 · Analytical adaptation

**Automated:** an estimate trace of at least ten samples with standard deviations,
starting from a prior at least 10% off the true mass and continuing to at least
3 s; from 2 s on, every estimate within 5% and 2σ within 5%. EKF mass+20 RMS after
the first 2 s at most 1.5× its nominal value or nominal plus 5 mm, whichever is larger,
per trajectory, with no EKF crash or abort in those runs.
Full PID/EKF benchmark and excitation-log artifacts.

**Review:** inspect state augmentation for mass and all three disturbance axes,
process/measurement equations, covariance propagation and update stability. Check
the estimate trace over the complete maneuver, not a single fortunate sample.
Confirm the estimate feeds thrust and disturbance cancellation. Ask why mass and
motor constant are not jointly identifiable in a perfect hover. Check multiple
seeds, covariance consistency and the PID comparison with adaptation off. Confirm
vertical speed is logged with every estimate: on the real drone, climb and descent
inflow change thrust and bias an inflow-free filter.

**Common failures:** initializing at true perturbed mass; reporting only the final
estimate; using simulator truth as a sensor; units in covariance matrices;
non-positive covariance; comparing to PID nominal instead of EKF nominal; estimator
working but feedback ignoring it.

## Module 4 · The four objectives

**Automated:** per-window held-out predictions of the thrust-to-mass ratio kf/m
give B a median R² >0.9 over at least three training seeds; A/B/C/D all present
with the same architecture hash and the same training seeds; latent size 8–16, 50-step history; at least 20
held-out pairs, each at least 2% from every training pair; at least three windows
per pair and seed; model and probe artifacts exist and match hashes. A/C/D have no
required performance ranking.

**Review:** all four encoders share architecture/capacity and training data but
have separately trained weights. A reconstructs current observations, B predicts
a multi-step state residual, C uses a stated contrastive flight objective, D
penalizes latent size alone. Check identical budgets and honest reporting of
degenerate objectives. Fit the linear probe/scaler using training data only,
freeze it before held-out evaluation, and inspect the spread of the probed ratio.
Mass and kf probes are diagnostics only: translational dynamics contain them as
kf/m, so ask what measurement would separate them (rotational response with known
inertia, or rotor speed). Read the notebook and reproduce raw predicted values. A
trained-weight hash must not be mistaken for the encoder architecture hash.

**Common failures:** splitting windows from the same parameterized flight across
train/test; fitting the probe on evaluation labels; near-constant targets;
averaging windows before scoring; reporting the best seed; B given extra privileged
inputs; a one-step residual dominated by sensor noise; sensor reconstructor
secretly predicting next state; discarding a strong A/C result because it
conflicts with the course hypothesis.

## Module 5 · Runtime adaptation

**Automated:** in every held-out condition/trajectory, no learned crash or abort and
learned RMS at most 1.10× the EKF's plus 5 mm; summed over the adaptation conditions, learned RMS no higher
than the EKF's; recovery ≤1 s for every +20% step without failure; complete
PID/LQR/EKF/learned/Variant B tables. Audit export has ≤10,000 float32 parameters
total, 50-step history, randomized observation noise/delay, command jitter and
dropped commands, within-episode resampling and total laptop CPU training <2 h.

**Review:** verify privileged phase-1 inputs, domain ranges and PPO rollout/update
logic. Check the reward: with only costs and crash termination, ending an episode
early scores best, so require a survival bonus or a termination penalty larger
than the discounted remaining cost, and read the episode-length curve. Confirm
phase 2 freezes the policy, regresses z, and collects trajectories under that
policy, and that physics is resampled within episodes so histories contain
parameter changes. Verify Variant B freezes the M4 encoder, and review it as a
transfer test: that encoder saw narrower data than the on-policy adaptation
module. Check the exported parameter list against BOTH deployed modules,
profiler/device logs against time, and randomization ranges against actual runner
behavior. Evaluation parameters must be held out at the parameter level. Optional
paid-GPU experiments do not replace the laptop result; report them as a separate
speed comparison.

**Common failures:** cost-only reward with crash termination; physics fixed for a
whole episode; adaptation trained on unrelated random actions; teacher parameters
leaking at test time; counting only policy parameters; measuring one epoch instead
of full training; skipping export biases; sensor delay fixed rather than
randomized; choosing the best seed; hiding an unsuccessful Variant B.

## Module 6 · CAD

**Automated:** mass budget ≤210 g from scale, slicer, datasheet and allowance
lines; thrust-to-weight ≥2 with the +10% payload, from thrust stated at no more
than 3.5 V per cell; CAD Params mass agrees with the budget; no-failure CAD hover
with the provisional 5 cm RMS tolerance; source, export, datasheet and
wiring-plan artifacts retained.

**Review:** inspect the parametric build123d model, component fit and fasteners,
guards, motor/prop clearance, Lighthouse sight lines and battery retention. Use
slicer part masses and scale measurements for components, apply the parallel-axis
theorem and inspect inertia axes/products. Check how datasheet thrust was scaled
to the loaded low-battery voltage and electrical compatibility; direct ESC battery
power is distinct from powering motors through current-limited Bolt connectors.
Render the wiring plan and approve it before anything is soldered. Check that the
battery feeds the ESC directly and the Bolt through its XT30, that only signal
wires run from the Bolt's ESC ports to the ESC, that each port drives the motor
`conventions.MOTOR_NAMES` assigns to it, and that wire gauges and connectors suit
the battery current. Check that the compatibility record names the ESC firmware
(Bluejay or BLHeli_S), the ESC protocol and the motor's pole count, and that the
frame carries both the ESC's and the Bolt's mounting patterns. Confirm imported Params were actually used and M5
randomization is recentered on CAD.

**Common failures:** treating infill parts as solid; battery, wiring or guards
missing from mass; torque/inertia units wrong; only diagonal inertia by assumption;
datasheet thrust for another prop/voltage; a CAD render mistaken for printable fit;
the + wires of the Bolt's ESC ports joined to a battery-powered ESC, which sends
motor current through the Bolt's 8 A path; motor ports wired in another order than
`conventions.MOTOR_NAMES`.

## Module 7 · Build and stock flight

**Automated:** a declared 60 s stock stabilized hover window with no gyro or
accelerometer clipping and no abort, altitude at least 0.2 m throughout and 0.3 m
on average, RMS tilt <5°; weighed mass ≤227 g, so the +10% payload stays below
250 g; 3S battery thresholds with critical ≥3.0 V per cell below low below landing
≤3.8 V per cell; build, BOM, video, preflight and as-built wiring records
present.

**Review:** inspect photos, solder joints, mechanical retention, print orientation
and material; compare weighed BOM against assembly and the photos against the
as-built wiring plan. Review props-off motor order, radio failsafe and kill switch
tests before fitting props. With the ESC on battery power, the Bolt's power switch
leaves the ESC live; check that the operator unplugs the battery to make the
aircraft safe. Confirm the battery
thresholds were persisted on the Bolt (`pm.lowVoltage`, `pm.criticalLowVoltage`)
and that the app lands at its landing threshold well before the firmware's
five-second critical shutdown, which switches the motors off in flight. Check
`pid_vel.thrustBase` was set from the measured hover throttle. On a DShot build,
check the firmware config: bidirectional DShot at 300 kHz with a Bluejay ESC, or
switched off for an ESC without telemetry, and `MOTORS_NBR_OF_POLES` set to the
motor's pole count instead of the default 12. Confirm the
clipping flags come from every 1 kHz sample, not the 100 Hz log. Check Lighthouse
setup, net/tether arrangement, LiPo handling, storage and disposal, insurance and
applicable local obligations. The video must match the logged continuous hover and
firmware mode. Approve the operator's setup separately from the numerical gate.

**Common failures:** gyro or accelerometer clipping flag absent, always zero or
computed from decimated samples; Bolt battery defaults left at their 2S values; DShot
with bidirectional telemetry left on for a stock BLHeli_S ESC, so the arming RPM check
fails; filter hiding
vibration; roll/pitch in radians labeled degrees; thirty seconds duplicated to make
sixty; a log recorded on the ground; log from a different airframe; physical props
fitted during desk tests; a wiring plan left unchanged after a repair or motor
swap. Stop and repair the physical fault before another flight.

## Module 8 · System identification

**Automated:** every required parameter has a measured value, positive
uncertainty, method and units, or is marked unresolved with a limitation, an
assumed range and its source; mass and inertia must be measured. Each measured
interval and each assumed range lies within the trained range. The thrust map at
the hover command and voltage logged in M7 is within 3% of the weighed m·g.
Measured-parameter benchmark config, training range and model artifacts must be
retained. The interval containment rule is an explicit conservative tolerance.

**Review:** inspect raw scale, thrust-stand, battery-voltage and pendulum data.
The stand's microcontroller and the Bolt log on separate clocks; check that the
streams were matched by laptop time and that each thrust point averages a settled
hold. The stand must hold the rotor at least two diameters from any surface and
match the installed arm and guard geometry. Per-motor kf needs rotor-speed information
(bidirectional DShot telemetry from a Bluejay ESC, or a tachometer) or a clearly
distinguished effective command-to-thrust model. Telemetry RPM divides by the
compiled pole count, so check it against a tachometer or the motor's datasheet
pole count before kf is fitted. A thrust-only load
cell does not identify torque constant; require a torque-arm fixture or an
equivalent arrangement and documented error. Check inertia axes/cross terms or
justified symmetry measurements, motor lag, rate-loop response, drag fitted from
steady-speed flight in the `DRAG_MODEL` form, internal resistance from a
four-motor load step, sensor noise for position, velocity and body rate, and
command delay and drops. Review voltage-dependent forward and inverse thrust maps
down to the M9 low-battery voltage, confidence/uncertainty meaning and repeated
trials. Compare measured and CAD parameters and verify changed ranges caused an
actual retrain. Unresolved parameters need a defensible assumed range, not a
convenient one.

**Common failures:** no error bars; uncertainty stated as a percentage but consumed
as SI half-width; a stand rotor close to the bench; confusing motor lag with
communication delay; fitting thrust vs command and calling it kf in omega² units;
inferring km from thrust alone; marking a measurable parameter unresolved; only
editing the range JSON while keeping the old trained model.

## Module 9 · The hardware comparison

**Automated:** complete five-repeat hardware matrices per controller/condition/
trajectory: off-board PID/EKF/learned, onboard learned, and RAPTOR in its own
onboard report when buildable. At least three recorded parity sequences agree
within 1e-4; worst-case onboard inference is at most 2 ms. Per cell, onboard
learned RMS may not exceed 1.10× off-board learned RMS beyond the one-sided 95%
flight-to-flight noise bound; the learned controller may not crash in any
deployment; every abort has a written reason. PID/EKF/RAPTOR outcomes may be worse
or better. RAPTOR omission needs an actual Bolt build blocker and log, not an
assumed limit. Reports must identify distinct deployment modes and raw flight
files and contents, and share the same vehicle, comparison and repeat seed; the
two learned reports share the model hash. The submitted model artifact must match
that hash. Evidence commit/config/seed binds to the off-board manifest; other rows
retain their own deployment metadata.

**Review:** confirm every off-board controller uses the same app, 100 Hz interface,
volume, trajectory, actuator limits and measurement method, with the Lighthouse
crossing-beam method recorded for every flight. Compare scheduled and actual
randomized run order, battery voltages and fan/payload positions and the fan's
measured air speeds; retained aborts remain visible. Review output clipping, tilt
takeover, geofence, timeout landing, low-voltage landing and kill-switch tests.
Check parity inputs cover realistic history and boundary values, the inference
integration path (app task or in-loop controller) and the measured timing budget.
Inspect video for every controller under payload, published complete tables and
available raw data. Validate the model exported is the one measured off-board.

**Common failures:** taking onboard estimated pose as truth; building the
firmware's Lighthouse ground-truth option, which removes position control;
cherry-picked runs; each controller flown in a different tracking volume; RAPTOR
forced through the thrust/body-rate route; timing mismatch hidden by averaging;
parity tested only on zero input; a single successful hover presented as a
complete matrix; unsupported RAPTOR claimed as flown. A tuned classical controller
winning is a valid and publishable course result.

## Accept, retry or pending

Record **accept** only after the automated result and the applicable human review
are both supported. Record **retry** with the failed criterion and concrete
diagnostic experiment. Record **pending** for missing artifacts, unperformed
flights, unavailable model builds or unresolved equipment support. A green CI
run with PENDING templates is never a completed course. Do not invent a trainer
signature, instructor effort estimate, hardware compatibility result or launch
claim to fill a blank in the record.
