Skip to content

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.

to_hdf5(path)

Write one dataset per signal (needs h5py).

to_csv(path)

Write one row per sample; vector signals expand to name[i] columns.

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.