Simulate and optimize planar leg mechanisms using PSO and GA
Project description
leggedsnake
LeggedSnake is a Python toolkit for designing, simulating, and optimizing planar walking linkages. It layers a pymunk physics engine and multi-objective optimizers on top of pylinkage's kinematic model, so you can go from a mechanism sketch to an evolved walker in a few lines of code.
One Strider mechanism optimized over 10 generations of a genetic algorithm, rendered walking on flat ground with 4 phase-offset legs (2 per side, mirrored).
Installation
From PyPI:
pip install leggedsnake
From source with uv:
git clone https://github.com/hugofara/leggedsnake
cd leggedsnake
uv sync
Quick start
A walker in five lines — the canonical Theo Jansen "Strandbeest" built from the Holy Numbers, mirrored left/right and cloned into four phase-offset pairs for stance stability, rendered in a live pyglet window:
import leggedsnake as ls
walker = ls.Walker.from_jansen(scale=0.1)
walker.add_opposite_leg() # mirror for left/right pair
walker.add_legs(3) # 4 legs per side → 8-leg Strandbeest
ls.video(walker, duration=10) # live simulation
Fewer legs pitch 10–20° and can tip over; four legs per side keeps at least two feet in stance at every crank angle, which is why real Strandbeests have many legs.
Other classical mechanisms ship as one-line factories too:
Walker.from_strider, Walker.from_klann, Walker.from_chebyshev,
Walker.from_watt, and Walker.from_catalog (pylinkage's topology
catalog).
To build a custom mechanism, declare its topology (nodes + edges) and dimensions separately:
from math import tau
import leggedsnake as ls
from leggedsnake import (
HypergraphLinkage, Node, Edge, NodeRole,
Dimensions, DriverAngle, Walker,
)
hg = HypergraphLinkage(name="MyWalker")
for node_id, role in [
("frame", NodeRole.GROUND), ("frame2", NodeRole.GROUND),
("crank", NodeRole.DRIVER),
("upper", NodeRole.DRIVEN), ("foot", NodeRole.DRIVEN),
]:
hg.add_node(Node(node_id, role=role))
for edge in [
("frame_crank", "frame", "crank"), ("frame2_upper", "frame2", "upper"),
("crank_upper", "crank", "upper"),
("crank_foot", "crank", "foot"), ("upper_foot", "upper", "foot"),
]:
hg.add_edge(Edge(*edge))
dims = Dimensions(
node_positions={
"frame": (0, 0), "frame2": (2, 0),
"crank": (1, 0), "upper": (1, 2), "foot": (1, 3),
},
edge_distances={
"frame_crank": 1.0, "frame2_upper": 2.24,
"crank_upper": 2.0, "crank_foot": 3.16, "upper_foot": 1.0,
},
driver_angles={"crank": DriverAngle(angular_velocity=-tau / 12)},
)
walker = Walker(hg, dims, name="My Walker")
walker.add_opposite_leg(axis_x=1.0) # mirror for left/right pair
walker.add_legs(1) # add a phase-offset copy
ls.video(walker)
What you can do with it
| Capability | Entry points |
|---|---|
| Build mechanisms | Walker, HypergraphLinkage, Walker.from_strider/jansen/klann/chebyshev/watt/catalog |
| Kinematic fitness | leggedsnake.utility.stride, leggedsnake.utility.step |
| Physics simulation | World, video, all_linkages_video, video_debug |
| Dynamic fitness | DistanceFitness, EfficiencyFitness, StrideFitness, StabilityFitness, CompositeFitness |
| Single-objective GA | GeneticOptimization, genetic_algorithm_optimization |
| Multi-objective (NSGA) | nsga_walking_optimization, NsgaWalkingConfig |
| Topology co-design | topology_walking_optimization, optimize_walking_mechanism |
| Gait & stability | analyze_gait, StabilityTimeSeries, compute_tip_over_margin |
| Export | to_urdf (ROS), save_walker (JSON), save_walker_svg |
| Plotting | plot_pareto_front, plot_gait_diagram, plot_foot_trajectories, plot_optimization_dashboard |
Documentation
- Concepts guide
— orientation to the three core ideas: topology + dimensions = walker,
the
DynamicFitnessprotocol, and the optimizer landscape from fast-kinematic to dynamic-multi-objective. params→WorldConfigmigration guide — for code written against the legacy globalparamsdict.- Full API reference — modules grouped by capability (Mechanism, Physics, Evaluation, Optimization, I/O & Plotting).
Tutorials and deeper examples
Start with the numbered notebooks — they walk through the full pipeline end to end:
examples/01_walkers_gallery.ipynb— build and inspect the classical linkages.examples/02_physics_and_fitness.ipynb— physics simulation and fitness evaluation.examples/03_genetic_optimization.ipynb— evolve a walker with the genetic algorithm.examples/04_multi_objective_and_gait.ipynb— NSGA Pareto fronts plus gait / stability analysis.examples/05_topology_co_optimization.ipynb— topology + dimensions co-optimization, plus the Phase 8.3evolve_offsetschromosome that co-evolves gait pattern alongside structure and geometry.examples/06_export_and_share.ipynb— JSON serialization, URDF export for ROS / Gazebo, SVG snapshots, and interactive plotly visualization.
The examples/tools/ directory holds maintenance
scripts, not tutorials: verify_mechanisms.py
renders foot-trajectory plots for every classical builder
(Walker.from_jansen / from_klann / from_chebyshev) as a smoke
test, and generate_readme_gifs.py
regenerates the two animated GIFs above deterministically via
matplotlib PillowWriter (headless).
Tips for faster experiments
- Visualize early and often. Every optimizer will hand you a linkage with a better score; only the animation tells you whether it walks the way you wanted.
- Don't start from a hand-tuned optimum. A random starting population is more robust against collapsing into a nearby suboptimum.
- Exploit symmetry. A Strider half-leg has the same kinematic stride as
the full mechanism and evaluates an order of magnitude faster. Use
kinematic PSO on the reduced problem, then hand off the winner to a
dynamic GA on the full mechanism.
- Checkpoint long runs.
GeneticOptimization(..., startnstop="run.json")resumes automatically on the next launch. - Wrap optimization scripts in
if __name__ == "__main__":— the GA and NSGA optimizers spawn worker processes.
Contributing
Contributions, feature requests, and "look at this weird walker" submissions are all welcome. See CONTRIBUTING.md for the developer workflow, or drop by the GitHub discussions. A star or a link to your favourite walker also helps.
Quick links
- Documentation: hugofara.github.io/leggedsnake
- Concepts: concepts.html
- Migration guide: migration_world_config.html
- Source: HugoFara/leggedsnake
- PyPI: leggedsnake
- Changelog: CHANGELOG.md
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file leggedsnake-0.5.0.tar.gz.
File metadata
- Download URL: leggedsnake-0.5.0.tar.gz
- Upload date:
- Size: 171.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dad349eaedb53e0a4e866fd82579afbaf1c605d1b982728c04eb5c4b1ad817a4
|
|
| MD5 |
61aabb5cedd68ef98f1f547abba5f642
|
|
| BLAKE2b-256 |
0115ad3667711141f5abf8cbf967db9f3fe2b6e5a2159cd2753ef6cae74fc7c0
|
File details
Details for the file leggedsnake-0.5.0-py3-none-any.whl.
File metadata
- Download URL: leggedsnake-0.5.0-py3-none-any.whl
- Upload date:
- Size: 123.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2795acb96e850a0df855e7b0764cc42f8f9f9688d747d2cde8cf14f440d73c3e
|
|
| MD5 |
e0c099572624edab47f21431628c75c2
|
|
| BLAKE2b-256 |
16498dcf25d27fce2a0e64303f3e94409201a24700d9823fcf91737cf4cf7987
|