This project badge reports repository CI; it is not a Modelica Association certification. The checked rules and their MSL rationale are documented in CONTRIBUTING.md.
Reusable Modelica building blocks for rigid-body simulation, estimation, control, and verification.
This repository is the aerospace engineering workspace for CogniPilot vehicle development. Vehicle physics, flight-control source models, named vehicle parameterizations, avionics-facing plant interfaces, missions, and qualification criteria live here. Firmware repositories consume exported artifacts; they do not own alternate copies of these models.
Avionics/: transport-independent sensor, navigation, and estimator lifecycle contracts shared by drivers, estimation, and control.Estimation/: structured estimator prediction and correction functions, including the replaceableStrapdownINSESKF and manifold UKF.LinearAlgebra/: dimension-generic matrix algorithms used by estimation and control code.Polynomials/: dimension-generic Hermite construction, derivative evaluation, and integrated derivative-cost matrices.LieGroups/: Lie groups including SO(2), SO(3), SE(2), SE(3), and SE_2(3), with replaceable quaternion, DCM, MRP, and Euler rotation representations. All 12 axis sequences are available in both body-fixed and space-fixed form, includingB232andS123.Geodesy/: reusable local-frame and geodetic conversion helpers, including the East-North-Up projection and its inverse about aGeodeticOriginused to turn global lat/lon/alt waypoints into local-frame references.RigidBody/: reusable six-degree-of-freedom rigid-body dynamics.MathUtilities/: shared clipping, filtering, angle, rate, and norm helpers.Control/: reusable control laws and sampled controller building blocks, including the multirotor SE_2(3)/SO(3) log-linear controller and theMultirotor.RateLoopandMultirotor.Allocationinner-loop building blocks.Planning/: forward-only bounded-curvature path planning, including all six classical Dubins path families.Vehicles/Templates/: parameterized fixed-wing and quadrotor plants.Vehicles/Cubs2/andVehicles/Rdd2/: named parameterizations, flight-control models, avionics plant interfaces, and qualification missions. RDD2 includes both its cascaded sampled controller and a thin vehicle parameterization of the reusable log-linear controller.tools/: non-library Python orchestration for validation, qualification, export, CI caching, and reports. Nothing under this directory is part of the Modelica package API.
The repository keeps automation outside its Modelica package trees, following
the same separation used by the
Modelica Standard Library.
The structure check validates within declarations, package.order coverage,
and package metadata without requiring Nix.
Avionics is the pure-Modelica boundary between sensor drivers,
navigation algorithms, and control. Its IMU, motion-capture, GPS, and optical-
flow records use physical quantities with explicit ENU-world and FLU-body frame
conventions; transport adapters may map Synapse/FlatBuffers messages into these
records, but this library has no dependency on those message definitions.
Every estimator publishes Avionics.NavigationEstimate. It
contains only algorithm-independent physical state: position, velocity,
acceleration, body and world angular velocity, and mutually consistent
quaternion, direction-cosine-matrix, and roll-pitch-yaw attitude forms. Internal
bias states, tangent ordering, and covariance stay private because they are not
comparable across arbitrary filter implementations.
Estimation.StrapdownINS.PartialEstimator is the common aided inertial-
navigation interface. StrapdownINS.ESKF implements a 15-state local
right-error ESKF, while StrapdownINS.UKF implements a 31-point manifold UKF
over the same position, velocity, attitude, and IMU-bias state. The ESKF name
is intentional: additive bias states mean that calling it an invariant Kalman
filter would overstate its mathematics. Both filters accept identical sensor,
noise, initialization, and terrain-plane assumptions so their closed-loop
comparison is meaningful.
Sensor timestamp_s is capture time. valid may remain true while a usable
sample is held; fresh pulses for one estimator tick when a new sample arrives,
preventing a slow sensor value from being fused repeatedly. Latency compensation
belongs in a reusable fixed-lag wrapper over a private estimator-backend contract:
the wrapper buffers backend snapshots and IMU increments, corrects at the capture
time, and replays prediction to the present. This keeps replay logic shared while
allowing each backend to retain its own opaque state and uncertainty model.
The CUBS2 deployment boundary is intentionally narrower than the closed-loop
test model. Vehicles.Cubs2.OuterLoop is the deployable Modelica controller;
it sends pilot-style commands to an onboard inner-loop stabilizer whose
proprietary implementation is unavailable to this project. Closed-loop CUBS2
tests use OnboardStabilizerSurrogate only as an explicit simulation fixture.
Those tests verify the open outer loop and its assumed boundary behavior; they
do not establish equivalence to, or qualify, the unseen stabilizer.
The vehicle library is execution-neutral. Its names describe physical or control meaning only. Tooling outside the library decides whether a model is executed directly or exported for another runtime.
Control.Multirotor.LogLinear contains the alternate controller as one
documented package. It exposes the left-invariant state error, bounded position
integral, SE_2(3) outer loop, SO(3) attitude loop, and sampled controller model.
Vehicles.Rdd2.LogLinearController supplies only the RDD2 parameters. A
differentially flat trajectory connects directly through its world-frame
position, velocity, acceleration, and yaw-reference quaternion.
GPS waypoint navigation runs entirely on this local-frame controller. The
reusable Planning.Bezier.waypointTrajectory builds a piecewise-septic
differentially flat trajectory that passes through each waypoint at a commanded
velocity (zero for rest-to-rest), feeding world-frame position, velocity, and
acceleration to the log-linear controller; its collective thrust and body-rate
command pass through Control.Multirotor.RateLoop and
Control.Multirotor.Allocation onto the four rotors.
Vehicles.Rdd2.Test.TruthWaypointMission flies a truth-feedback comparison
baseline. Vehicles.Rdd2.Test.WaypointMission takes off, flies a box, and
lands in the local East-North-Up frame using the optical-flow-aided ESKF, and
Vehicles.Rdd2.Test.GlobalWaypointMission authors the same box in
latitude/longitude/altitude, projects it back through a fixed mission origin,
and flies it using the GPS-aided ESKF. Parallel UkfWaypointMission and
UkfGlobalWaypointMission models fly the same missions with the UKF. All four
aided tracks are compared with the truth-feedback baseline so controller/plant
failures can be distinguished from estimator or aiding failures.
The optical-flow sensor traces a normalized image feature grid against the
configured plane n' p = d; it therefore does not require that the observed
surface be aligned with local vertical. Raw line-of-sight flow contains both
translation and camera rotation. The compensated velocity and slant-range
measurement use the same plane geometry as each filter's observation model.
The default plane is horizontal because that is a common local-terrain
approximation, and an inclined-plane regression prevents that default from
becoming a hidden assumption.
An aerospace change follows one source path:
- Edit a reusable plant under
Vehicles/Templates/, or a named vehicle, controller, and mission underVehicles/Cubs2/orVehicles/Rdd2/. - Run the named vehicle qualification. Each mission exercises the controller
and plant together and writes reviewable traces and reports beneath
artifacts/vehicles/. - Export the same controller source as eFMI Production Code.
- Export the same avionics plant interface as FMI 3 Co-Simulation.
- Let the consuming firmware repository integrate those generated artifacts.
Landing-gear contact is intentionally eventful. Direct simulation preserves the touchdown relations. The current source-FMU profile rejects plant exports that contain event/action partitions; it must continue to fail closed until the FMI execution kernel preserves those partitions and its conformance tests cover them.
Install the cross-platform repository command from the checkout:
python -m pip install -e .
modelica-models doctorPython owns the task definitions and process orchestration. Run qualifications and exports with the same command on Linux, macOS, or Windows:
modelica-models qualify cubs2
modelica-models qualify rdd2
modelica-models qualify all
modelica-models export cubs2-controller
modelica-models export cubs2-plant
modelica-models export rdd2-controller
modelica-models export rdd2-estimator
modelica-models export rdd2-plantRumoca's CLI and matching Python bindings are required for qualification;
OpenModelica or a working Docker installation is required for the full test
suite. Tool locations can be overridden with MODELICA_MODELS_RUMOCA,
MODELICA_MODELS_OMC, and MODELICA_MODELS_DOCKER. Set
MODELICA_MODELS_ROOT when running outside the checkout.
Nix is optional. It only provides pinned compilers, Python dependencies, and environment variables; it does not define tasks or task-specific wrappers:
nix develop
modelica-models qualify rdd2
# One command without entering the shell:
nix run . -- testCI runs the CUBS2 and RDD2 qualifications as independent jobs. RDD2 flies the
truth-feedback baseline plus optical-flow- and GPS-aided ESKF and UKF missions. Each
job uploads its traces, PNG plots, and a self-contained HTML report, with a
direct artifact link in the GitHub Actions job summary. The two jobs use
fail-fast: false, so both vehicles are qualified even if one fails.
RDD2 sensor noise is deterministic and compiler-portable. Each Gaussian sample
is produced by a Box-Muller transform of indexed uniform fixtures, then scaled
from the exact measurement covariance or continuous IMU/bias-walk spectral
density supplied to the selected filter. This is deliberately not the stateful
Modelica Standard Library/OpenModelica Xorshift sequence. Qualification checks
the empirical variance and cross-correlation against the declared assumptions;
enableSensorNoise=false provides a noiseless diagnostic run.
Arguments after -- are forwarded to the selected qualification, exporter,
or report implementation.
These commands render the explicit optical-flow and GPS mission models and
write a PNG, self-contained HTML report, JSON observation report, and selected
CSV under artifacts/vehicles/rdd2/mission-plots/<mode>/. They report model
traces; they do not qualify a physical aircraft or issue a flight-acceptance
verdict.
Invoke the reporter through the Python orchestration command:
modelica-models mission-report optical -- --engine omc
modelica-models mission-report gps -- --engine omcLive simulation refuses tracked modifications or untracked files among
Modelica sources, scenario TOML, Resources/, and package.order, and checks
again after simulation so a report cannot label dirty inputs with the clean
revision. Provenance records resolved compiler and Python paths and their
SHA-256 digests. Exact receipted compiler and model revisions are mandatory
whether the tools came from Nix or a native installation.
The optical command always selects Vehicles.Rdd2.Test.WaypointMission; the
GPS command always selects Vehicles.Rdd2.Test.GlobalWaypointMission. Defaults
match the collected model evidence: 45 seconds with a 5 ms output step. The
GPS report shows the existing 0.5 m model navigation-error criterion as a
diagnostic. The optical report shows absolute-position drift without applying
a threshold because velocity-only optical aiding does not observe absolute
position.
The frozen Rumoca flight compiler revision 9860c307 is not available from the
current optional environment, so live Rumoca reporting fails closed instead of
silently using a different compiler. A receipted Rumoca CSV can still be
replayed:
modelica-models mission-report optical -- \
--engine rumoca --rumoca-csv /path/to/trace.csv \
--rumoca-receipt /path/to/trace.receipt.json
modelica-models mission-report gps -- \
--engine rumoca --rumoca-csv /path/to/trace.csv \
--rumoca-receipt /path/to/trace.receipt.jsonReplay refuses a CSV without a sidecar whose JSON fields exactly identify its mode, model, compiler, model revision, duration, cadence, and SHA-256 digest. The receipt schema is:
{
"schema": "rdd2-mission-trace-receipt-v1",
"engine": "rumoca",
"mode": "gps",
"model": "Vehicles.Rdd2.Test.GlobalWaypointMission",
"compiler": {
"name": "Rumoca",
"identity": "rdd2-flight-freeze-2-9860c307",
"revision": "9860c30781242ff65dfcf47b136385ac5ecf4350"
},
"model_revision": "a9e5037ab3e57b3fac6ca783c0bdbfdd2b6dd98e",
"stop_time_s": 45.0,
"step_s": 0.005,
"csv_sha256": "<exactly 64 lowercase hexadecimal characters>"
}OpenModelica replay uses the same schema with engine omc, identity
OpenModelica/a96aa1a682c463b0fd2d285b486c09a8b7fe496d, and revision
a96aa1a682c463b0fd2d285b486c09a8b7fe496d. Trace validation also requires
finite samples, time zero, strict ordering, exact cadence, and complete
requested coverage. Use --stop-time and --step to match a shorter receipted
trace. These commands are currently Linux-only.
Named vehicle missions write a canonical, execution-neutral trajectory log:
time_s,x_m,y_m,z_m,roll_rad,pitch_rad,yaw_rad
The trajectory comparison tool treats one such log as the gold standard, interpolates any number of named candidate logs at its timestamps, renders full trajectory/component/error plots, and enforces duration, position, altitude, and attitude limits. It intentionally knows nothing about the system that produced a log:
modelica-models trajectory-compare -- --helpQualification artifacts remain ignored build outputs under
artifacts/vehicles/. A downstream repository can compare generated, bench,
or flight logs by adapting them to this seven-column contract; no downstream
execution terminology or log decoder belongs in this library.
All mathematical test definitions are Modelica classes under Tests/. The
suite uses Modelica assert equations for Lie-group identities, generic
linear-algebra systems, geometric error-state filters, Dubins paths, and analytic rigid-body
trajectories.
No Python source generation or numerical oracle is involved.
Run the complete suite with the installed Python command:
modelica-models testIndividual checks are available through the same interface:
modelica-models check-structure
modelica-models test omc
modelica-models test rumoca
modelica-models test plotsFrom an uninstalled source checkout, replace modelica-models with
python -m tools.modelica_models_cli. The runner uses the declared
OpenModelica container when Docker is operational, then falls back to a local
omc. It handles paths, temporary directories, process failures, and artifact
validation without platform-specific scripts. Python does not compute test
oracles: mathematical pass/fail checks remain Modelica assertions executed by
OpenModelica and, where currently supported, Rumoca.
The CI command also simulates reproducible planning examples and writes CSV
data plus PNG plots under artifacts/planning/. The primary aircraft figure is
a closed figure eight whose two crossing headings share exactly one center
position. Redundant identical poses are coalesced, leaving seven waypoint
occurrences and six nonzero Dubins legs. The initial flight plan is overlaid with
its flyable, continuous-curvature Dubins-polynomial smoothing. A separate plot
checks curvature and curvature-derivative continuity across every leg and
waypoint junction, and a mission-wide signed bank-angle plot shows the
coordinated left/right command. Generated artifacts are ignored by Git and
uploaded by the GitHub Actions workflow.
The Dubins-polynomial coefficients are obtained with a fixed-cost analytical
procedure: one joint unconstrained quadratic solve over the free junction
derivatives, followed by one closed-form true-spatial-derivative repair pass.
The default derivative weights are {0.3, 1, 0, 50} through third order. The
CI gallery also renders four paper-style comparisons with the Dubins reference,
optimized path, and nominal segment junctions on common axes.
The plotted example also checks a stated aircraft flight envelope in Modelica: minimum turn radius, coordinated bank angle, and coordinated bank rate at the declared flight speed. Thus "flyable" means that those explicit assumptions pass assertions; it is not inferred from geometric smoothness alone.
To regenerate only the planning artifacts:
modelica-models test plotsCI uses the same Python commands. Its optional Nix layer pins compiler binaries
and caches only the Rumoca runtime when that pin changes. OpenModelica runs the complete
assertion simulation using the pinned container image, with a local omc as
the fallback when Docker is unavailable. Rumoca independently parses, resolves, flattens, and lowers
the same Tests.All model to DAE form, then executes SO(2) and SE(2) assertion
smoke models with its simulation backend. The complete assertion model remains
the OpenModelica simulation authority because Rumoca does not yet structurally
lower every aggregate static assertion and array-valued dynamic equation.
SE3.Generic and SE23.Generic accept any package implementing
SO3.Interfaces.PartialRotation. Concrete quaternion, MRP, DCM, and Euler-B321
packages are provided. A specific Euler sequence can be selected directly:
package VehicleState = LieGroups.SE23.Generic(
redeclare package Rotation = LieGroups.SO3.EulerSequences.B232);Quaternion and DCM representations are generally preferable for global
simulation. Euler representations are useful at interfaces where a named
sequence is required. Proofs using local log coordinates should explicitly
establish the chart-domain guard supplied by LieGroups.Analysis.
The library keeps simulation equations and proof expressions aligned:
RigidBody.stateDerivativeis the pure vector field used byRigidBody6DOF.RigidBody.mechanicalEnergy,wrenchPower, andpowerBalanceResidualexpose the lossless passivity identity.LieGroups.Analysissupplies coordinate-independent attitude potential, principal-log domain checks, and dimension-generic tangent-space quadratic candidates.LinearAlgebra.contractionResidual,quadraticForm, andquadraticFormDerivativeprovide dimension-generic certificate expressions.- The ESKF exposes its nominal discrete map, tangent transition, process covariance, measurement map, measurement covariance, and local residual as separate pure functions for reachable-set propagation.
These functions produce certificate quantities; they do not claim a proof by themselves. Bounds on chart validity, disturbances, numerical integration, and the sign of the resulting matrix inequalities remain explicit proof obligations.
Planning.Dubins computes the globally shortest member of the six classical
forward-only, constant-turn-radius path families: LSL, RSR, LSR, RSL, RLR, and
LRL. Positions are Cartesian x/y coordinates and headings are in radians.
The classical plan search includes RLR/LRL by default and accepts
allowThreeTurnPaths=false when those reversal-heavy paths are inappropriate.
Aircraft trajectory examples disable them by default and automatically select
the shortest feasible LSL, RSR, LSR, or RSL path, so callers do not select a
family manually.
Planning.Dubins.Path path;
Planning.Dubins.Pose midpoint;
equation
path = Planning.Dubins.plan(
{0.0, 0.0}, 0.0,
{10.0, 10.0}, 0.5 * 2.0 * asin(1.0),
5.0);
midpoint = Planning.Dubins.evaluate(path, 0.5);The path record exposes its selected family, physical length, turn radius, and three normalized segment lengths. Turn segment lengths are angles in radians; straight segment lengths are distance divided by turn radius. This makes the piecewise curvature and segment domains explicit for tracking and reachability analysis.
Planning.DubinsPolynomial represents a smooth trajectory as a piecewise
polynomial left-normal offset from an underlying Dubins path. Polynomial
abscissas use physical nominal path distance rather than normalized segment
coordinates, keeping derivative costs dimensionally consistent between arcs
and straight segments.
The evaluator accepts any coefficient count and returns:
- position and heading;
- true unit-speed spatial derivatives through third order;
- signed curvature and curvature derivative;
- transverse offset and the offset-to-nominal metric scale.
smoothOffsets constructs a septic seed that fixes position and heading at
the endpoints and segment junctions while making curvature and curvature rate
continuous. derivativeCost and Polynomials.derivativeCostMatrix provide the
quadratic objectives needed to penalize offset, curvature-related derivatives,
and higher derivatives. The coordinated-flight helpers map curvature and its
metric derivative to fixed-speed roll angle and roll rate.
The supplied smoothing seed constrains transverse offset and its first
derivative to zero at nominal segment junctions. This makes its C3 continuity
exact. An optimizer that permits nonzero junction offsets must additionally
apply a nonlinear true-derivative repair iteration; use
junctionContinuityResidual as the acceptance certificate. Every candidate
must also retain a positive metricScale, since a zero value indicates a
singular or locally reversed offset curve.