Quasi-static kinematic and force analysis for a Stephenson six-bar leg linkage. Loads mechanism geometry from MotionGen export files or from an intermediate JSON format that can be hand-edited.
- Position kinematics — all joint positions vs crank angle (loop-closure via circle-circle intersection)
- Velocity kinematics — angular velocities of all links
- Motor torque — via principle of virtual work (validated against Newton-Euler)
- Joint reaction forces — full Newton-Euler static equilibrium on each link
- Coupler curve — path traced by the wheel/output point
- Animation — animated linkage motion through full crank rotation
python3 -m venv venv
source venv/bin/activate
pip install pydy sympy numpy scipy matplotlibThe workflow has two stages that can be run together or separately:
- Parse a
.motiongenfile into a human-readable JSON - Simulate from the JSON (or directly from the
.motiongen)
This lets you export from MotionGen once, then tweak joint positions, link lengths, or actuator parameters in the JSON without re-exporting.
python motiongen_parser.py path/to/mechanism.motiongen -o mechanism.jsonEdit mechanism.json in any text editor to adjust geometry, then run the simulation from it.
# Interactive linkage + synced charts (slider, play/pause, speed)
python sixbar_sim.py inputs/mechanism.motiongen
# Pass mechanism.json instead to skip re-parsing the .motiongen
python sixbar_sim.py mechanism.json
# Use a custom settings file
python sixbar_sim.py inputs/mechanism.motiongen --settings path/to/settings.json
# Headless GIF export (no GUI required)
python sixbar_sim.py inputs/mechanism.motiongen --no-gui --export-gif outputs/linkage.gif
# Export link dimensions as Markdown + PDF
python sixbar_sim.py inputs/mechanism.motiongen --export-dimensions outputs/mechanism_dimensions.md --no-guiThis reads settings.json by default for masses, payload, motor limits, and other run settings, runs the full kinematics/dynamics, and opens an interactive viewer.
The mechanism is converted to the configured output units even if the input .motiongen was authored in a different unit system.
If export.gif_path is set in settings (or --export-gif is passed), the run also writes an animation GIF.
If --export-dimensions is passed, the run writes <stem>_dimensions.md and <stem>_dimensions.pdf — a table of all link centerline lengths, ground-frame pivot spacings, and the joint topology. The PDF is rendered via fpdf2 (pure Python, no system dependencies). Autoscale is applied before the lengths are recorded, so the numbers match the printed geometry.
The viewer includes:
- Absolute crank-angle slider (drag to inspect a configuration)
- Linkage motion panel with coupler trail
- Synchronized cursors across torque, joint-force, and coupler charts
- Play/Pause and playback speed control
- Optional GIF export via
--export-gif(orsettings.jsonexport.gif_path)
If the run prints Current backend: agg, install a GUI backend first:
- Ubuntu/Debian:
sudo apt install python3-tk(TkAgg) - Or install Qt bindings in your venv:
pip install PyQt5
settings.json drives the simulation inputs. See the file itself for inline comments on every field; the main knobs are:
units.length,units.torque,units.angle— output/working unitslength_scale— uniform scale applied to imported mechanism geometryautoscale.enabled/autoscale.target_delta_y— when enabled,length_scaleis computed automatically so point E's Y travel matchestarget_delta_y(inunits.length); the staticlength_scalevalue above is overriddenpayload.mass_kgandpayload.direction— mass applied at point E (converted to force withgravity)motor.speed_deg_per_s,motor.torque_limit,motor.torque_nominalexport.gif_pathandexport.gif_fpslink_masses— per-link mass in kgn_steps,start_angle,end_angle,gravity
start_angle and end_angle are in units.angle. Set both to simulate a partial sweep, or leave both null for a full rotation.
For backward compatibility, payload.weight (in N) is still accepted if payload.mass_kg is omitted.
Supported unit values:
units.length:mm,cm,m,in,ftunits.torque:N.m,N.mm,lbf.in,lbf.ftunits.angle:deg,rad
from sixbar_sim import SixBarSim, SimulationSettings, SixBarInteractiveViewer
# Accepts either a .motiongen or .json file
settings = SimulationSettings.load_json("settings.json")
sim = SixBarSim(
"mechanism.json",
target_length_unit=settings.length_unit,
target_angle_unit=settings.angle_unit,
length_scale=settings.length_scale,
)
results = sim.run_with_settings(settings)
# Save a GIF
sim.animate(results, save_path="linkage.gif")
# Or open the interactive viewer
viewer = SixBarInteractiveViewer(sim, settings, results)
viewer.show()Note: when using the library directly, autoscale is not applied — set length_scale yourself, or mirror the autoscale block in sixbar_sim.py's CLI entry point.
from motiongen_parser import load_motiongen, Mechanism
# Parse and export
mech = load_motiongen("mechanism.motiongen")
mech.save_json("mechanism.json")
# Load back from JSON
mech = Mechanism.load_json("mechanism.json")
print(mech.summary())L1 (crank) : B → A motor at B
L2 (ground) : B – C – D ternary, fixed frame
L3 (coupler) : E – F – G ternary, E = wheel/output point
L4 : D → G binary
L5 : C – F – H ternary, connects both loops
L6 : A → H binary
Loop 1: B – A – H – C (ground segment B–C)
Loop 2: C – F – G – D (ground segment C–D)
settings.json # Simulation inputs: payload, motor, masses, sweep
motiongen_parser.py # MotionGen → intermediate JSON → Python data structures
sixbar_sim.py # Kinematics, force analysis, interactive viewer, GIF export
mechanism.json # Intermediate JSON (generated, editable)
inputs/ # MotionGen source files
*.motiongen
outputs/ # Generated outputs
*_dimensions.md / *_dimensions.pdf # Link dimension sheets (--export-dimensions)
*.gif # Animations (--export-gif)
