Import your robot¶
FlapSim loads URDF and MJCF through MuJoCo's MjSpec. What those formats
cannot say about a flapping robot goes into a manifest, robot.yaml, next to
the model file.
1. Inspect the model¶
flapsim inspect my_robot.urdf
prints every body with its parent and mass, every joint with its type, axis and range, and the sites. Note the wing bodies, the stroke joints and the passive feathering joints.
2. Write the manifest¶
schema: flapsim.robot/1
name: MyFlapper
aliases: [my_flapper]
model:
file: urdf/my_robot.urdf # relative to this file; .xml for MJCF
root_body: base_link # a direct child of the world body
base: {type: fixed, position: [0.0, 0.0, 0.5]}
timestep: 5.0e-5
integrator: implicitfast
patches: # what URDF cannot express
joints:
left_feathering: {stiffness: 2.0e-7, armature: 1.0e-10,
solref_limit: [4.0e-4, 1.0]}
right_feathering: {stiffness: 2.0e-7, armature: 1.0e-10,
solref_limit: [4.0e-4, 1.0]}
equalities:
- {type: joint, targets: [left_feathering, right_feathering], polycoef: [0, 1]}
bodies:
base_link: {when: free, mass: 0.03, com: [0, 0, 0],
fullinertia: [5.0e-5, 5.0e-5, 2.0e-5, 0, 0, 0]}
exclude_self_collision: true
torso: {body: torso} # body whose velocity the aero models use
air_density: 1.225
wings:
- name: left
body: left_wing
side: left
frame:
span: [0.0, 0.0, -1.0] # root -> tip, along the feathering axis (body frame)
chord: [0.0, -1.0, 0.0] # leading edge (hinge) -> trailing edge
stroke_joint: {name: left_stroke, sign: 1}
feathering_joint: {name: left_feathering, sign: 1}
planform: {profile: rectangular, length: 0.07, mean_chord: 0.02}
mass_model: {type: spar_plate, mass: 6.26e-5, spar_fraction: 0.8}
shoulder_offset: 0.014
- name: right
body: right_wing
side: right
mirror_of: left # shares the left frame at the neutral pose
stroke_joint: {name: right_stroke, sign: -1}
feathering_joint: {name: right_feathering, sign: 1}
planform: {profile: rectangular, length: 0.07, mean_chord: 0.02}
mass_model: {type: spar_plate, mass: 6.26e-5, spar_fraction: 0.8}
shoulder_offset: 0.014
Wing frame¶
span points from the wing root to the tip along the feathering axis;
chord points from the leading edge (the hinge line) towards the trailing
edge. Both are unit-free vectors in the wing body's frame. FlapSim derives
the surface normal, so the side of the hinge on which the plate lies is never
ambiguous. A site whose +X is the chord and +Y the span works too. A
mirrored wing can simply say mirror_of: <other wing>.
Joint signs¶
Positive stroke angle is a right-handed rotation about the stroke axis
(side * Z of the aero frame), positive feathering angle a right-handed
rotation about +Y for both wings. sign converts the MuJoCo joint
coordinate. If you get it wrong the loader warns:
wing 'left': positive stroke_joint rate is not a right-handed rotation about
side * Z (the stroke axis); flip the joint's 'sign'.
It also warns when the wing's centre of mass lies on the wrong side of the
hinge for the chosen chord.
Planform¶
rectangularorellipticwithlengthandmean_chord;polyline:chord_polyline: [[r, c], ...]in metres from root to tip;integrals: the eight blade-element integrals directly;pitch_axis_fraction(hinge inside the chord) orleading_edge_offset(root spar between hinge and leading edge).
flapsim.wings.from_polygon and from_mesh compute the integrals from an
outline or an STL file.
Mass model¶
CAD exports often place a wing's centroid far behind the hinge. For a
membrane wing that is unphysical, and it inverts the passive feathering
phase (negative lift). Declare mass_model in wing coordinates (normal,
span, chord), so mirrored wings share the same numbers:
spar_plate (a leading-edge spar carrying spar_fraction of the mass plus a
membrane plate), thin_plate, or explicit (com, diaginertia).
3. Load and check¶
from flapsim.robot import load
robot = load("path/to/robot.yaml") # or base="free"
print(robot.wings, robot.geometry_warnings())
flapsim inspect path/to/robot.yaml # lists the resolved wings too
4. Give it a setup file¶
Copy sim.yaml from the example robot, change the joint names, and run
flapsim run path/to/sim.yaml. Robots without CAD can start from
flapsim.robot.flat_plate_flapper(), which builds a two-wing flat-plate
robot and its manifest in Python.