Aerodynamics¶
flapsim.aero.base
¶
Composable aerodynamics: flow state, per-wing kinematics, model loads.
An :class:AeroModel turns one wing's kinematics into :class:WingLoads
(a force at a point plus a pure moment, all in the wing aero frame). An
:class:AeroEngine evaluates the shared kinematics once per wing, sums the
loads of every model, applies :class:AeroModifier factors (for example
ground effect) and writes the resulting wrench to MuJoCo. Plugins wrap the
engine; see :mod:flapsim.aero.stack.
FlowState
dataclass
¶
Torso velocity relative to the air and the air properties, per step.
WingLoads
dataclass
¶
Loads of one model on one wing, in the wing aero frame.
force acts at point (relative to the wing body origin);
moment is a pure couple. diagnostics are scalars published as
wings/<wing>/<model>/<key>.
AeroModel
¶
Base class of additive per-wing aerodynamic models.
AeroModifier
¶
Multiplies the summed loads of a wing by a factor (ground effect ...).
flapsim.aero.models.quasi_steady
¶
Translational quasi-steady normal force (Whitney & Wood 2010 / FlapX).
flapsim.aero.models.rotational_lift
¶
Rotational circulation force (Sane & Dickinson 2002).
RotationalLift
¶
Bases: AeroModel
F_rot = C_rot rho theta_dot * integral c(r)^2 |U(r)| dr along the normal.
C_rot = pi (0.75 - x0) by default, with x0 the hinge position as a
fraction of the chord (Sane & Dickinson 2002, eq. 4). Positive when the
wing pitches up relative to the oncoming flow. The force acts at the
point_fraction of the chord behind the leading edge (default half
chord) at the spanwise centre of pressure. Diagnostics: C_rot,
circulation_integral.
flapsim.aero.models.added_mass
¶
Added-mass (non-circulatory) loads of a thin flat-plate wing.
AddedMass
¶
Bases: AeroModel
Reaction of the accelerated fluid on a thin flat-plate wing.
Per unit span the added mass of a flat plate moving normal to itself is
m_a = rho pi c^2 / 4. The model splits its effect in two:
- Explicit force from the prescribed accelerations: the normal flow
at section
risU_n(r) = U_i1 r + U_i0(stroke and body motion); its time derivative over one step givesF = integral m_a dU_n/dt dralong the normal, applied at the mid-chord of thec^2-weighted span centroid. - Implicit inertia for the passive feathering: the added rotational
inertia about the hinge (:func:
feathering_added_inertia) is added to the feathering joint's armature when the model is bound. Applying it as an explicit force from a finite-difference feathering acceleration is unstable for light wings, because the added inertia then lags the motion by one step; MuJoCo integrates armature implicitly.
include_feathering_inertia=False skips the armature change. The
armature uses the manifest's air density at bind time. Diagnostics:
dU_i1, dU_i0, added_inertia.
feathering_added_inertia(params, rho, c4=1.0)
¶
Added rotational inertia of a flat plate about its feathering hinge.
Per unit span the plate carries m_a = rho pi c^2 / 4 at its mid-chord,
a distance d_mid from the hinge, plus the added inertia
rho pi c^4 / 128 about the mid-chord (Sedov). Integrated over the
span with the planform integrals (c^2 -> z_cp0, c^4 -> c4).
flapsim.aero.models.wake_capture
¶
Empirical wake-capture force after stroke reversal (Dickinson et al. 1999).
WakeCapture
¶
Bases: AeroModel
Transient normal force when the wing meets the wake it shed before reversal.
Empirical: when the relative flow U_i at the span centre of pressure
changes sign, the velocity magnitude just before reversal U_rev is
stored and a force C_wc 1/2 rho S U_rev^2 exp(-(t - t_rev) / tau) is
applied along the new flow direction, with tau = tau_chords c / U_rev.
C_wc and tau_chords are tuning constants (defaults 1.0 and 0.5);
there is no first-principles value. Diagnostics: U_rev, elapsed.
flapsim.aero.models.ground_effect
¶
Ground-effect lift augmentation as a multiplicative factor.
GroundEffect
¶
Bases: AeroModifier
Scale a wing's loads by a factor that grows near the ground.
exponential: 1 + gain * exp(-h / (length_scale * R)) with h
the height of the wing's origin above ground_height.
cheeseman_bennett: the rotor-in-ground-effect formula
1 / (1 - (R / (4 h))^2), capped at max_factor. Both are
empirical and tuned by the user.
flapsim.aero.stack
¶
Aero plugins built on :class:~flapsim.aero.base.AeroEngine.
AeroStack
¶
Bases: AeroPlugin
Sum several additive aero models on every wing, with modifiers.
models lists model names or {type: name, ...} mappings (see
flapsim.aero.base.available_models()), modifiers likewise.
Reads atmosphere/rho and atmosphere/wind_world when an atmosphere
plugin publishes them. Publishes aero/F_world, aero/lift_total,
aero/rho and per wing wings/<name>/<key> totals plus
wings/<name>/<model>/<key> contributions. imposed_body_velocity
and imposed_body_rates replace the torso velocity seen by the models
(identification and testing only).
QuasiSteadyAero
¶
Bases: AeroStack
Per-step quasi-steady blade-element forces (the single-model stack).
Publishes aero/F_world, aero/lift_total and, for every wing,
wings/<name>/F_N, alpha, F_world, torque_world,
cop_world and the quasi_steady/ contributions (C_N,
M_aero, M_rd, chord_CoP, span_CoP). options are
:class:~flapsim.config.AeroOptions fields.
flapsim.aero.atmosphere
¶
Air density, steady wind, gusts and turbulence.
Atmosphere
¶
Bases: AtmospherePlugin
Publish atmosphere/rho and atmosphere/wind_world every step.
rho defaults to the ISA density at altitude_m. wind is a
steady world-frame vector; gust adds one of one_minus_cos
(amplitude, start, duration, direction), sine (amplitude,
frequency_hz, direction, phase) or step (amplitude, start,
direction); turbulence adds an Ornstein-Uhlenbeck process per axis
(sigma m/s, tau s, seed). Add this plugin before the aero
plugins that should see the wind.
isa_density(altitude_m)
¶
International Standard Atmosphere density in the troposphere [kg/m^3].
flapsim.aero.body_drag
¶
Quadratic drag on bodies (fuselage, legs, tail).
BodyDrag
¶
Bases: ForcePlugin
F = -1/2 rho C_d A |v| v per body axis, relative to the wind.
bodies maps body names to {cd, area} where each may be a scalar
(isotropic) or a 3-vector in the body frame. Density comes from
atmosphere/rho when published, else rho or the manifest. Forces
are added to xfrc_applied at the body centre of mass, so place this
plugin after any plugin that overwrites a body's wrench. Publishes
<id>/<body>/F_world.
flapsim.aero.cycle_averaged
¶
Cycle-averaged lift and control moments for floating-base hover studies.
The robot is treated as one rigid body. Lift k f^2 acts along the body
thrust axis, tilted by the gimbal angles; gimbal angles also produce control
moments, scaled by (f / f_ref)^2; linear and angular damping oppose the
body velocity. All coefficients can be identified from the per-step model
with :func:flapsim.aero.identify.identify_cycle_averaged (flapsim sweep)
and loaded from the resulting parameter file.
CycleAveragedAero
¶
Bases: AeroPlugin
hover_frequency_hz
property
¶
Frequency at which the cycle-averaged lift balances the robot weight.
flapsim.aero.identify
¶
Identify cycle-averaged parameters from the per-step model.
The fixed-base robot is a virtual wind tunnel: sweep the stroke frequency to
fit the lift coefficient, deflect each gimbal joint to measure how the mean
force direction and the mean moment change per radian, and impose body
velocities and rates on the aero stack to measure damping. The result is a
flapsim.cycle_averaged/1 parameter file for
:class:~flapsim.aero.cycle_averaged.CycleAveragedAero.
lift_sweep(make_sim, frequencies, *, amplitude_rad=None, settle=0.2, cycles=10.0, stroke_id='stroke', aero_id='aero')
¶
Mean world force per frequency and the least-squares lift = k f^2 fit.
gimbal_sensitivity(make_sim, *, frequency_hz, joints=None, angle_rad=0.1, settle=0.2, cycles=10.0, stroke_id='stroke', gimbal_id='gimbal', aero_id='aero')
¶
Per gimbal joint: d(mean force)/d(angle) and d(mean torque)/d(angle).
Central differences at +/- angle_rad; forces and torques are expressed
in the root body frame.
damping_sensitivity(make_sim, *, frequency_hz, speed=0.5, rate=1.0, settle=0.2, cycles=10.0, stroke_id='stroke', aero_id='aero')
¶
Linear and angular damping from imposed body velocities and rates.
Returns linear (-dF_i/dv_i) and angular (-dT_i/dw_i) in the
root body frame, using central differences on the aero stack's imposed
body velocity and rates.
identify_cycle_averaged(make_sim, *, frequencies=(20.0, 30.0, 40.0, 50.0), reference_frequency_hz=None, amplitude_rad=None, gimbal_angle_rad=0.1, include_damping=True, settle=0.2, cycles=10.0, stroke_id='stroke', gimbal_id='gimbal', aero_id='aero')
¶
Run all sweeps and assemble a flapsim.cycle_averaged/1 parameter set.
flapsim.fluid.aero
¶
Quasi-steady blade-element aerodynamics for one flapping wing.
Pure functions, no MuJoCo state. Inputs and outputs use SI units.
Wing aero frame¶
+X is the surface normal, +Y the feathering (span) axis and
+Z = X x Y. The feathering axis runs along the leading edge and the
chord extends from it along -Z, so the centre of pressure always lies at
[0, side * span_CoP, -chord_CoP] with chord_CoP >= 0. Positive
psi is a right-handed stroke rotation about side * Z and positive
theta a right-handed feathering rotation about +Y; side is +1
for a left wing and -1 for its mirror image, which shares the same frame
orientation at the neutral pose. U_i is the relative flow speed along
+X at the span centre of pressure, so F_N carries the sign of the
flow and M_aero = -d_cp * c * F_N always turns the trailing edge
downstream, which is what drives passive feathering.
wrench follows MuJoCo's world-frame order [Fx, Fy, Fz, Tx, Ty, Tz]
about the wing centre of mass. The formulation follows Whitney & Wood
(2010) as used by the FlapX simulator.
wing_kinematics(params, side, psi, psi_dot, theta, theta_dot, linear_velocity_body, angular_velocity_body, options=None)
¶
Relative-flow coefficients and angle of attack of one wing.
The flow along +X (U_i) and along +Z (U_o) is affine in
the span coordinate: U = U1 * r + U0. The angle of attack is
evaluated at the spanwise centre of pressure r_cp = R r33 / r22.
wing_loads_from_kinematics(params, kin, options=None)
¶
Normal force, pitching moment and damping from :func:wing_kinematics.
wing_aero(params, side, psi, psi_dot, theta, theta_dot, linear_velocity_body, angular_velocity_body, options=None)
¶
Calculate one wing's local aerodynamic force, moments and CoP.
world_wrench(wing, side, aero, read_only=False)
¶
Convert one wing's local aero result into a world-frame wrench.
normal_force_coefficient(alpha)
¶
FlapX C_N(alpha) empirical fit.
center_of_pressure_fraction(alpha)
¶
FlapX chordwise d_cp(alpha) empirical fit.