Components¶
flapsim.actuators.sinusoidal_stroke
¶
Kinematic sinusoidal stroke: q = direction * A sin(2 pi f t + phase).
SinusoidalStroke
¶
Bases: ActuatorPlugin
Impose a sinusoidal angle on one or more stroke joints.
kinematic mode writes position and velocity (so aerodynamic models see
the stroke rate); position_only writes the position with zero velocity,
which the cycle-averaged floating model uses to avoid injecting energy.
Commands cmd/<id>/frequency_hz and cmd/<id>/amplitude_rad override
the configured values; the current values are published as
<id>/frequency_hz, <id>/amplitude_rad and <id>/phase.
flapsim.actuators.pd_position
¶
PD position actuator with torque limits, or a kinematic position hold.
PDPosition
¶
Bases: ActuatorPlugin
Hold joints at targets with a gravity-compensated PD law.
joints maps joint names to {kp, kd, max_torque, target, role}.
role is a free label (for example pitch or roll) that lets
controllers address joints by function. Targets come from
cmd/<id>/<joint> signals when present and are clipped to the joint
range; the active targets are published as <id>/<joint>. In
kinematic mode the joint is set to the target after each step.
flapsim.actuators.servo
¶
Position servo: PD hold with a rate-limited, first-order lagged target.
Servo
¶
Bases: PDPosition
A :class:PDPosition whose targets move towards the commanded values
at most rate_limit_rad_s fast through a first-order lag of
time_constant_s (0 for none), like a hobby servo. Publishes
<id>/<joint> (current target) and <id>/<joint>/desired.
set_target(joint_name, value, *, clip=False)
¶
Jump the servo (and its desired value) to value immediately.
flapsim.actuators.dc_motor_crank
¶
Brushed DC motor driving the stroke through a gearbox and crank.
DCMotorCrank
¶
Bases: ActuatorPlugin
Voltage-driven DC motor, gearbox and crank imposing q = d A sin(theta_c).
Electrical: i = (V - k_e w) / R, tau_m = k_t i. Mechanical, on the
motor shaft: J dw/dt = tau_m - b w - c sign(w) + tau_aero / N where
tau_aero is the aerodynamic torque about the stroke joints projected
onto the crank (load: aero, from the aero plugin's wing signals) and
N the gear ratio. The crank angle theta_c advances at w / N
and every stroke joint follows direction * A sin(theta_c + phase).
Commands: cmd/<id>/voltage_v. Publishes speed_rad_s,
frequency_hz, current_a, voltage_v, torque_motor,
torque_load, power_w, crank_angle.
no_load_speed(voltage_v=None)
¶
Motor speed where the back-EMF balances the voltage (no friction).
flapsim.sensors.noise
¶
Measurement conditioning shared by the sensor plugins.
Conditioner
¶
Sample-and-hold, first-order low-pass, pure delay, noise, bias, quantization.
update(true_value, time) returns the current reading. A new sample is
taken whenever time reaches the next sampling instant (rate_hz;
every call when None). The sample passes a low-pass with time
constant tau (seconds, 0 disables), enters a delay line of
round(delay_s * rate) samples, then receives Gaussian noise of
standard deviation noise_std, a bias drawn uniformly from
[-bias_range, bias_range] at every :meth:reset, and is quantised to
multiples of resolution (0 disables). All parameters may be scalars
or arrays matching size.
flapsim.sensors.imu
¶
Inertial measurement unit: attitude estimate, gyro and accelerometer.
IMU
¶
Bases: SensorPlugin
Attitude (AHRS-like low-pass of the true angles), body rates and
specific force at rate_hz.
Defaults reproduce the IMU model used for the IMU-only hover policies:
attitude low-pass 20 ms with 0.02 degree noise, gyro noise 0.002 rad/s,
accelerometer noise 0.02 m/s^2 with a per-episode bias of up to
0.01 m/s^2. The accelerometer measures R^T (a - g), so a hovering or
resting body reads [0, 0, +9.81]. Publishes <id>/euler,
<id>/gyro, <id>/accel and <id>/quat (true attitude, for
reference).
rotation_to_rpy(rotation)
¶
Body-to-world rotation matrix to roll, pitch, yaw (Z-Y-X, radians).
flapsim.sensors.rangefinder
¶
Downward (or any-direction) range finder such as a time-of-flight sensor.
RangeFinder
¶
Bases: SensorPlugin
Slant range along a body-fixed direction, delayed and noisy.
plane intersects the ray with the ground plane z = ground_height
(the slant range is height / cos(tilt) with cos clamped at
min_cos); ray casts a MuJoCo ray against all geoms. Defaults
reproduce the hover-policy TOF: 20 ms delay, 5 mm noise, 4 m range, and a
range rate from differenced readings through a 50 ms low-pass. Publishes
<id>/range and <id>/range_rate.
flapsim.sensors.encoder
¶
Joint position and velocity measurements.
JointEncoder
¶
Bases: SensorPlugin
Quantised, noisy joint positions and velocities at rate_hz.
counts_per_rev sets the position resolution of hinge joints (None
for continuous). Publishes <id>/<joint>/position and
<id>/<joint>/velocity.
flapsim.sensors.wrench
¶
Load cell: the interaction wrench between a body and its parent.
BodyWrench
¶
Bases: SensorPlugin
Force and torque transmitted from a body's parent (MuJoCo cfrc_int).
For the root body of a fixed-base robot this is the reaction of the
mounting, that is the force a load cell under the robot would read (its
weight minus the lift). Values are in the world frame about the body's
centre of mass, averaged over average_s seconds when given.
Publishes <id>/force and <id>/torque.
flapsim.controllers.hover_pid
¶
Cascaded altitude and attitude PID for hovering with a floating base.
HoverPID
¶
Bases: ControllerPlugin
Hold a position (or cycle through waypoints) with a flapping-frequency altitude loop and a gimbal attitude loop.
State is averaged over one stroke period and the loops update once per
period. Commands are published for a stroke actuator
(cmd/<stroke>/frequency_hz) and a gimbal actuator
(cmd/<gimbal>/<pitch_joint>, cmd/<gimbal>/<roll_joint>).
flapsim.controllers.schedule
¶
Time-scheduled set points for any plugin's commands.
SetpointSchedule
¶
Bases: ControllerPlugin
Publish cmd/<target>/<name> from [[time, value], ...] tables.
With interpolate the value is linearly interpolated between the table
times, otherwise it holds the latest entry (step changes). Before the
first time nothing is published, so the plugin's own default applies.
Useful for frequency ramps, gimbal step inputs and scripted manoeuvres.
flapsim.controllers.callback
¶
Run a user Python function as a controller.
CallbackController
¶
Bases: ControllerPlugin
Call callback(sim, controller) -> {command: value} at rate_hz.
Returned keys are published as commands: "stroke/frequency_hz" becomes
cmd/stroke/frequency_hz (keys already starting with cmd/ are used
as is). None or an empty mapping publishes nothing. The controller
keeps a state dictionary for the callback's own use.
flapsim.io.recorder
¶
Sample signals into memory and export them.
MemoryRecorder
¶
Bases: RecorderPlugin
Record matching signals every every seconds (default: every step).
signals is a list of shell-style patterns ("aero/F_world",
"wings/*/alpha"). Use :meth:get for one signal as an array of shape
(samples, ...), :meth:to_dict, :meth:to_npz or :meth:to_csv.
flapsim.io.overlays
¶
Viewer and renderer overlays: force arrows and centre-of-pressure markers.
ForceArrows
¶
Draw each wing's aerodynamic force as an arrow from its centre of pressure.
Use as sim.run(viewer=True, on_frame=ForceArrows()) or pass it to the
video recorder. scale is metres of arrow per newton. The callable
accepts a viewer handle (uses user_scn) or an MjvScene.
flapsim.io.video
¶
Offscreen rendering to image sequences, GIF or MP4.
render_frames(sim, *, duration, fps=30.0, width=640, height=480, camera='camera', overlay=None, realtime_factor=1.0)
¶
Step sim and yield RGB frames (height x width x 3 uint8).
realtime_factor below one slows the video down (more simulated steps
per frame are skipped). Needs an offscreen OpenGL backend
(MUJOCO_GL=osmesa, egl or a display for glfw).
record_video(sim, path, *, duration, fps=30.0, width=640, height=480, camera='camera', overlay=None, realtime_factor=1.0)
¶
Write a .mp4 (imageio with ffmpeg), .gif (Pillow) or a directory
of .png frames, chosen by the path suffix.
flapsim.envs.gym_env
¶
A Gymnasium environment over any FlapSim :class:~flapsim.core.Simulation.
Observations are named signals (imu/gyro, tof/range, ...) scaled and
concatenated; actions in [-1, 1] are mapped affinely onto plugin
commands (cmd/gimbal/pitch_joint, cmd/stroke/frequency_hz, ...) with
optional first-order smoothing. Reward and termination are plain callables
so tasks stay outside the core.
Observation
dataclass
¶
One block of the observation vector taken from a signal.
Action
dataclass
¶
One action element mapped onto a plugin command.
SimulationEnv
¶
Bases: Env
Gymnasium wrapper: decisions every decision_dt seconds.
make_sim builds the simulation once; episodes call sim.reset().
reward(env, obs, action) returns a float or (float, info_dict);
terminated(env) returns a bool; on_reset(env) may randomise the
state. With include_previous_action the previous normalised action is
appended to the observation.
flapsim.envs.rewards
¶
Reward and termination helpers for hover tasks (true state, training only).
root_state(sim)
¶
Position, world velocity, tilt angle and body rates of the root body.
hover_reward(target_height=0.5, *, weights=None, stable_bonus=0.5, stable_tolerance=None, crash_penalty=-10.0, terminated=None)
¶
exp(-(w_z |dz| + w_tilt tilt + w_v |v| + w_w |w| + w_a |da|^2)) plus a
bonus when hovering steadily and a penalty when the termination test fires.
Defaults reproduce the IMU-hover training reward. Returns a callable
reward(env, obs, action) -> (value, terms).
hover_terminated(*, min_height=0.08, max_height=1.5, max_horizontal=1.5, max_tilt_deg=65.0)
¶
Episode failure: too low or high, drifted away, or tipped over.
flapsim.study
¶
Parameter studies: evaluate a function over a grid of cases, in parallel.
grid_cases(grid)
¶
Cartesian product of parameter values as a list of case dictionaries.
parameter_study(evaluate, grid=None, *, cases=None, workers=1, progress=None)
¶
Call evaluate(**case) for every case of grid (or cases).
evaluate must be importable (module level) when workers > 1 since
cases run in separate processes. Exceptions are captured per case.