System identification¶
Fit a model's physical parameters (Fit) or its noise model (NoiseFit)
to recorded data. See Fit parameters from a log
for a worked recipe.
Parameter fit¶
manta.Fit ¶
MAP parameter fit over recorded windows (see module docstring).
Args:
world — the model. Finalized by the internal Sim; the
fit never mutates it (until result.apply()).
parameters — {name: Prior | Tied | Free | None}. Prior/None
keys resolve against the model's promotable
Parameters (<craft>.<part>.<param>); None =
flat prior (only safe for parameters the data
fully observes). A Tied key is promoted but
derives from another entry's decision variable; a
Free key is a fresh auxiliary name (not a model
parameter) that exists to source ties.
Source code in manta/fit/_map.py
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 | |
solve ¶
Build the windowed prediction-error + prior NLP and solve it.
Args:
windows — the recorded data (≥ 1 Window).
weights — optional per-sensor scalar weights on the squared
residuals ({sensor name/suffix: w}); use
1/σ_meas² to whiten mixed-unit sensors. Default 1.
verbose — IPOPT iteration output.
ipopt_options — extra nlpsol options, merged last.
Source code in manta/fit/_map.py
manta.FitResult ¶
Fitted values + Gauss-Newton posterior diagnostics.
Attributes:
values — {name: fitted value} (float for scalars,
ndarray for vectors) — every promoted
parameter (tied ones derived through their
affine map) plus every Free variable.
labels — one entry per fitted scalar component of the
DECISION vector (tied parameters don't appear;
their source does).
log_scale — per-component bool; True ⇒ the sigmas below
are RELATIVE (log-space).
prior_sigma — per-component prior σ (inf = no prior).
posterior_sigma — per-component Gauss-Newton posterior σ from
(JᵀJ + Σ₀⁻¹)⁻¹. ≈ prior σ ⇒ the data did
not inform this component.
JtJ — data-only Gauss-Newton information matrix in
decision space; its small eigenvalues are the
unidentifiable directions.
objective — final loss value.
stats — IPOPT return stats.
converged — IPOPT's success flag; False ⇒ the values below
are the failed solve's final iterate (a
RuntimeWarning was emitted), not an optimum.
Source code in manta/fit/_map.py
weak_directions ¶
The k least-informed directions of the DATA alone: list of
(eigenvalue, {label: component}) for the smallest eigenvalues
of JᵀJ. A near-zero eigenvalue is an unidentifiable parameter
combination (e.g. the thrust/mass scale).
Source code in manta/fit/_map.py
apply ¶
Write the fitted values — tied parameters derived through
their affine maps — back onto the world's Part instances. A
transform built afterwards (Sim(world), EKF(world), a C++
deploy) bakes them in as constants.
Source code in manta/fit/_map.py
summary ¶
Per-component table: fitted value, prior σ vs posterior σ.
post/prior ≈ 1 flags a component the data did not inform —
its fitted value is your prior talking, not the flight. Tied
parameters follow, showing their derived values and source.
Source code in manta/fit/_map.py
Noise fit¶
manta.NoiseFit ¶
Innovation-NLL fit of noise σ values (see module docstring).
Args:
world — the model (dynamics/geometry at their — ideally
already fitted — declared values).
noise — {channel name/suffix: Prior | None}. Channel names
are the declaration names (drone.imu.gyro_noise,
drone.imu.gyro_bias); priors are relative
(log-space), None = flat.
sensors — measurement outputs the filter consumes (default: all
with traces required in every window).
Source code in manta/fit/_nll.py
solve ¶
Minimize the windows' total innovation NLL + prior over log-σ.
Args:
windows — recorded data; every chosen sensor needs a trace
in every window.
P0 — initial tangent covariance per window, P0 · I.
Keep small when x0 is trusted (synthetic truth);
grow it for estimator-seeded initial states.
Source code in manta/fit/_nll.py
manta.NoiseFitResult ¶
Fitted σ per channel + Laplace posterior diagnostics.
prior_sigma / posterior_sigma are RELATIVE (log-space) widths;
posterior ≈ prior means the data didn't inform that σ. converged
is IPOPT's success flag — False ⇒ the values are the failed solve's
final iterate (a RuntimeWarning was emitted), not an optimum.
Source code in manta/fit/_nll.py
apply ¶
Write the fitted σ back onto the owning parts
(<channel>_sigma attributes); transforms built afterwards
(an EKF(world)'s auto-Q/R, a NoiseDriverd truth sim) use
them.
Source code in manta/fit/_nll.py
Inputs¶
manta.Window
dataclass
¶
One fitting window: a short recorded rollout.
Args:
x0 — nested initial state dict (the sim.state shape:
{craft: {slot: value}}). Slots omitted fall back to the
world's initial state.
u — recorded controls: {input name/suffix: scalar | (K,)}.
A scalar is held for the whole window; inputs omitted hold
their model default.
z — recorded sensor readings: {sensor name/suffix:
(K, dim) | (K,)}. Row k is the reading produced by step k
(the step taken FROM state k). For Fit, only sensors
present here enter the loss; for NoiseFit, every chosen
sensor needs a trace.
dt — fixed step, seconds.
t0 — world-clock time of x0.
manta.Prior
dataclass
¶
Gaussian prior on one fitted parameter.
Args:
sigma — 1-σ width. Scalar (isotropic across the parameter's
components) or a per-component sequence. With log=True
it is RELATIVE (log-space): sigma=0.3 ≈ ±30%.
mean — prior mean. None (default) → the model's declared
value.
log — fit log(p) instead of p, elementwise. Strictly-
positive parameters only (mass, moi, a thrust magnitude
along one axis). Keeps every component positive with no
constraint and makes pure scale ambiguities linear.
NoiseFit ignores this flag — noise σ is ALWAYS fit in
log-space, and sigma there is always relative.
lower — hard lower bound, AMBIENT space (a value, not a log).
upper — hard upper bound, ambient. Scalar or per-component.
Enforced as IPOPT box constraints — the sanity rails
that keep a physically absurd optimum off the table
(a thruster gain that can't exceed the motor's rating,
a mount that must stay inside the hull). The prior pulls
softly; bounds are walls. In NoiseFit they bound σ
itself. The declared/starting value must satisfy them.
Structure¶
Symmetry and sanity are declared, not hoped for: tie identical or
mirrored parameters to one decision variable (Tied), introduce shared
geometry as an auxiliary variable (Free), and wall off absurd values
with Prior(lower=, upper=) box bounds.
manta.Tied
dataclass
¶
Structural tie: this promoted parameter is a fixed affine function
of another fitted parameter (or a Free variable), not a decision
variable of its own — p = scale · p_source + offset, in AMBIENT
space (after any log-reparam of the source).
This is how symmetry is enforced rather than hoped for: identical actuators share one gain, mirrored mounts share one geometry. Fewer decision variables ⇒ better-conditioned fits, and every data point that touches any tied copy informs the shared source.
Args:
source — name/suffix of the fitted parameter (or Free name)
this one derives from. Must itself be a decision
variable — chains of ties are not supported; tie all
copies to the same source.
scale — None (identity — identical copies), a scalar, a
per-component sequence (elementwise, e.g. a mirror's
sign flips (-1, 1, 1)), or a full
(target_dim, source_dim) matrix (e.g. a scalar arm
length mapped to a 3-vector mount position).
offset — additive constant: None (zero), scalar, or
length-target_dim.
Examples::
# four identical motors — one fitted gain
"t1.force_quad": Prior(sigma=3.0),
"t2.force_quad": Tied("t1.force_quad"),
# mirrored mount across the y-z plane
"t2.transform": Tied("t1.transform", scale=(-1, 1, 1)),
# scalar arm length -> the four X-frame mount positions
"arm": Free(0.12, prior=Prior(sigma=0.02, lower=0.0)),
"t1.transform": Tied("arm", scale=[[1], [1], [0]]),
"t2.transform": Tied("arm", scale=[[-1], [1], [0]]),
manta.Free
dataclass
¶
Auxiliary decision variable that is NOT a promoted parameter of
the model — it exists to be the source of Tied entries (a shared
arm length, a common incidence angle). Its key in Fit(parameters=)
is a fresh name, not a part parameter.
Args:
init — starting value (scalar or vector); also the prior mean
unless the prior says otherwise.
prior — optional Prior (sigma/mean/log/bounds), same semantics
as for a promoted parameter.