Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Payload Monte Carlo study

Continue the load-at-a-motor experiment: how much prescribed payload weight can the fixed controller tolerate on the same waypoint mission? Vary the payload mass used in F = m g, keeping its attachment at the front-right motor, 77.8 mm forward and 77.8 mm right of the center, in the motor plane. The QAV-R’s own mass, inertia, geometry, motors, firmware, gains, and mission stay fixed.

The recorded batch contains 18 runs: nine completed missions (including the unloaded reference) and nine firmware crash disarms. The heaviest successful sample was 321.6 g; the lightest failed sample was 359.8 g. These are observations for this mission and controller, not an exact stability boundary. The plant models ground contact at landing points; motion after tipping is not a detailed airframe collision reconstruction.

Start with the recorded trajectories below. For a short hands-on check, run the zero-load reference in step 4. The complete batch is an optional longer experiment; you can replot its archived logs immediately using step 6.

1. Inspect the recorded flights

Loading recorded payload-study trajectories…

Every recorded trajectory is shown on common axes. The zero-load reference is dark, completed missions are blue, and flights stopped by a failure criterion are red. Select a mass to highlight that run; the other paths remain visible. Crosses mark the final sample of interrupted runs. Runs without position telemetry remain in the table.

Static plot of all trajectories and altitude traces

Every recorded payload-study trajectory and altitude trace

18 of 18 runs completed (including the nominal reference).

RunPayload mass (g)ResultPeak armed tilt
reference0.0reference16.9°
payload-limit500.0flight failure179.2°
run-000438.7flight failure179.0°
run-001220.6passed27.5°
run-002198.4passed23.4°
run-003488.3flight failure178.8°
run-00470.0passed15.3°
run-005231.2passed28.9°
run-006359.8flight failure179.5°
run-007430.6flight failure179.2°
run-008430.0flight failure178.6°
run-009321.6passed40.9°
run-010472.2flight failure179.1°
run-011485.5flight failure178.9°
run-012242.2passed28.8°
run-013226.6passed28.6°
run-014225.3passed27.3°
run-015391.5flight failure179.0°

Peak armed tilt includes takeoff and ground contact. The separate sustained-tilt stop criterion activates only after the vehicle has risen above 2 m. The firmware’s own crash check can disarm it before that height.

Download SVG · All trajectory data · Run manifest · Study settings and controller gains · Model library license

Which model produced these results?

Each trial extends the same FastDyn.QavrSidePayload model from the preceding chapter and uses the environment’s pinned Rumoca compiler. The QAV-R geometry, array-valued inertia, motor response, and selected controller gains stay fixed. Only the prescribed payload mass changes. The run manifest records source and compiler provenance alongside the measured results.

Generated zero-load QAV-R variant
within FastDyn;
model PayloadTrial
  import Vehicles;
  import Geodesy;
  import RigidBody;
  extends QavrSidePayload(
    bare_mass=0.5,
    payload_mass=0,
    attachment_b={0.07778174593052023, -0.07778174593052023, 0});
end PayloadTrial;

2. Understand the sampled quantity

The primary model exposes the scalar payload_mass in kilograms:

within FastDyn;

model QavrSidePayload "QAV-R with a downward load at its front-right motor"
  import Vehicles;
  import Geodesy;
  parameter Real payload_mass = 0.05
    "Payload mass used to prescribe its weight; payload inertia is omitted [kg]";
  // Independent coordinates stay writable as FMI parameters. These equal
  // {0.11 / sqrt(2), -0.11 / sqrt(2), 0} for the assumed QAV-R motor layout.
  parameter Real attachment_b[3] = {0.07778174593052023, -0.07778174593052023, 0}
    "Front-right motor (motor 1), from CG in body FLU [m]";

  extends Qavr(plant(external_force_b = force_b, external_moment_b = moment_b));

  Real force_world[3] "Prescribed payload weight in world NWU [N]";
  Real force_b[3] "Applied force expressed in body FLU [N]";
  Real moment_b[3] "Moment about vehicle CG in body FLU [N*m]";
equation
  // The load stays world-down when the aircraft tilts.
  force_world = {0, 0, -payload_mass * plant.gravity};
  force_b = transpose(plant.R) * force_world;
  moment_b = cross(attachment_b, force_b);
  // This is an external load, not an additional rigid body's inertial dynamics.
end QavrSidePayload;

At level attitude, a 50 g mass gives a downward force of 0.49 N and a positive body-FLU roll moment and pitch moment of approximately 0.0381 N m each. A 100 g mass doubles both. The force remains world-down as the aircraft tilts. Each trial compiles its own FMU so the load also reaches any derived expressions evaluated during compilation.

This study uses a zero-load reference, 16 uniformly sampled masses from 0 to 500 g, and an explicit 500 g upper-limit run. NumPy’s PCG64 generator uses seed 462. The upper limit is 100% of the original 500 g vehicle weight: payload_mass / vehicle_mass <= 1.0. At this endpoint the combined static weight is equivalent to 1.0 kg under gravity. The uniform distribution explores the selected range; it does not estimate how frequently real payload masses occur.

This is the external-load approximation from the preceding chapter: changing payload_mass changes the prescribed weight, not the vehicle’s inertial mass. It does not simulate the payload’s swinging or acceleration-dependent tension. Those require additional dynamics.

3. Predict the trend before running

More payload weight requires more total thrust and more unequal motor effort. Let W = 0.50 * 9.8 be the original vehicle weight and P = payload_mass * 9.8 be the applied load. Balancing total thrust, roll, pitch, and yaw at level hover for this square-X motor layout gives:

T1 (front-right, payload motor) = (W + 3 P) / 4
T2 (rear-left, opposite motor)  = (W - P) / 4
T3 (front-left) = T4 (rear-right) = (W + P) / 4

At 500 g of payload, the force is 4.90 N, with 0.381 N m of roll moment and the same pitch moment. The ideal allocation is 4.90 N, 0 N, 2.45 N, 2.45 N for motors 1–4. Motor 2 reaches zero thrust, leaving no room to reduce it further. This motivates testing the endpoint: transients and the fixed controller may lose control before or near that condition.

4. Run the experiment

Use your chosen environment:

fastdyn-config --base configs/copter462.toml \
  --overlay configs/models/qavr-side-payload.toml --output out/payload-base.toml
python utils/payload_study.py --config configs/monte-carlo/payload.toml \
  --run-config out/payload-base.toml --limit 1

The reference run should report:

[study] reference: payload 0.0 g
[study] reference: reference — Final mission item and landing confirmed

Remove --limit 1 to run the full batch. The runner resumes completed samples in the same output directory and refuses to mix changed experiment inputs. Choose a fresh output directory when changing the study or controller. Run one batch at a time because the configured MAVLink ports are shared.

The runner snapshots the current model sources and generates each trial’s Modelica class. Edit the study TOML to change sweep settings; the runner replaces generated trial sources. Develop a different force law in a separately maintained model and validate it before automating its trials.

All persistent settings, including controller parameters, are in TOML:

# Payload range: 0 to 100% of the ORIGINAL vehicle mass (0 to 500 g).
# Attachment: motor 1, front-right, in body FLU.
# Same array-based model and selected gains as the load exercise.
[study]
title = "QAV-R: payload-weight Monte Carlo study"
seed = 462
samples = 16
distribution = "uniform"
attachment_b_m = [
    0.07778174593052023,
    -0.07778174593052023,
    0.0,
]
modelica_models_revision = "dfdb3294f61ab639a8a8be19611a1f69187a3ff7"
rumoca_revision = "21843c115cd4b4c7fa02011d18a6c1e501ebb387"
rumoca_version = "0.10.0"
mission = "virtuals/physics/flight_controllers/courbet/mavlink/copter_mission.waypoints"
vehicle_mass_kg = 0.5
max_payload_to_vehicle_mass_ratio = 1.0

[execution]
output = "out/payload-experiment"
# Optional: set to docs/book/assets/payload-study after reviewing a full batch.
publish = ""
wall_timeout_s = 150
monitor_port = 5565
max_tilt_deg = 60.0
tilt_duration_s = 1.0
max_altitude_m = 50.0

[parameters]
BRD_SAFETY_DEFLT = 0
FS_THR_ENABLE = 0
FS_GCS_ENABLE = 0
DISARM_DELAY = 0
EK3_SRC1_POSZ = 3
EK3_SRC2_POSZ = 3
EK3_SRC3_POSZ = 3
COMPASS_DEC = 0.0
COMPASS_AUTODEC = 0
ARMING_CHECK = 0
GUID_OPTIONS = 0
MOT_PWM_MIN = 1100
MOT_PWM_MAX = 1900
MOT_THST_EXPO = 0.5
MOT_THST_HOVER = 0.2
ATC_RAT_RLL_P = 0.04598495
ATC_RAT_RLL_I = 0.04598495
ATC_RAT_RLL_D = 0.00120043
ATC_RAT_PIT_P = 0.07421205
ATC_RAT_PIT_I = 0.07421205
ATC_RAT_PIT_D = 0.001949416
ATC_RAT_RLL_FLTT = 40.0
ATC_RAT_RLL_FLTD = 40.0
ATC_RAT_PIT_FLTT = 40.0
ATC_RAT_PIT_FLTD = 40.0
ATC_ANG_RLL_P = 4.5
ATC_ANG_PIT_P = 4.5

Expect a Modelica variant, compiled FMU, controller parameter file, run TOML, console log, MAVLink log, and result.json for each trial. The publish setting is optional and is empty by default, so your one-run check does not replace the book’s archived results. Find the new figures under out/payload-experiment/report/. To publish a reviewed full batch into this book, set publish = "docs/book/assets/payload-study" before starting a fresh output directory.

5. Interpret the outcomes

Every run starts fresh firmware and separate RAM files, then loads the same controller parameters. Completing the Copter mission requires the final item and landing confirmation. FastDyn and its separate helper processes are stopped before the next trial starts.

The experiment stops a flight if sampled tilt exceeds 60° across at least 1 s after becoming airborne, altitude exceeds 50 m, or the firmware reports crash disarming. Tilt is acos(cos(roll) * cos(pitch)); this uses the recorded MAVLink samples and does not certify behavior between samples.

A flight that arms but misses the completion deadline is mission incomplete. A compiler or startup failure is a run error. These labels distinguish an observed flight failure from an unavailable simulation; a timeout alone does not establish instability. Partial failed-run trajectories are retained.

A finite Monte Carlo sample can reveal failures at particular masses. It cannot prove an exact stability boundary or establish that every unsampled mass will work. To refine a transition, narrow the mass interval in TOML, use a new output directory, and repeat with the same controller.

6. Replot the archived logs

In your chosen environment, regenerate the plot from the book’s saved measurements:

python utils/monte_carlo_report.py \
  --config docs/book/assets/payload-study/runs.toml \
  --output out/payload-replot

The command reports the total number of runs, available trajectories, and runs without position telemetry. Both interactive and static figures include every available path, including interrupted flights.