Concepts
LeggedSnake’s API is small once you see the three pieces that compose into everything else: how a mechanism is described, how a candidate is scored, and how the optimizer landscape lays itself out. This page is the orientation; the per-module API pages have the exhaustive detail.
The mechanism model: topology + dimensions = walker
A walker is two orthogonal pieces of state, plus a thin object that holds them together.
- Topology — what is connected to what.
HypergraphLinkageis a graph ofNode(joints, with aNodeRoleofGROUND,DRIVER, orDRIVEN) connected byEdge(binary links) andHyperedge(rigid triangles / N-ary clusters). The topology describes the graph; it has no metric information.- Dimensions — how big and where.
Dimensionscarries node positions, edge distances, and aDriverAngleper driver node (initial angle + angular velocity). Two walkers with the same topology but different dimensions are different designs; this is what an optimizer mutates.- Walker — the bag that holds them.
Walkerwraps(topology, dimensions)plus amotor_ratesoverride (floatfor all drivers, ordict[str, float]for per-driver rates — the multi-DOF path). It exposesadd_legs(n)for phase-offset copies,add_opposite_leg()for mirroring,to_mechanism()for kinematic stepping, and the classical-mechanism factories (from_jansen,from_klann,from_chebyshev,from_strider,from_watt,from_catalog, …).
Why the split? Optimization perturbs dimensions on a fixed topology — separating the two means the optimizer never has to think about graph mutation. Topology co-design (see below) is the exception, and it picks topologies from a finite catalog rather than mutating edges.
The fitness protocol
All physics-aware evaluators share one signature, captured by the
DynamicFitness Protocol:
def __call__(
self,
topology: HypergraphLinkage,
dimensions: Dimensions,
config: WorldConfig | None = None,
) -> FitnessResult: ...
A FitnessResult carries:
score— primary value, higher is better by convention.metrics— dict of secondary numbers ("distance","energy","buildable_fraction","froude_number","cost_of_transport", …). Multi-objective optimizers consume these; you also read them post-hoc.valid— did the run complete? Failed designs returnvalid=Falsewith a meaningfulbuildable_fractionso the optimizer still has a gradient.loci— per-joint trajectories whenrecord_loci=True.
Built-in implementations
Class |
Primary signal |
Use when |
|---|---|---|
|
Forward distance walked |
You only care if it walks far. |
|
Distance per unit motor energy |
Energy-aware single-objective. |
|
Mean tip-over margin |
Robustness under disturbance. |
|
Mean stride length + gait metrics |
You want gait shape, not just total distance. |
|
Multiple objectives, one sim |
NSGA front; avoids duplicate physics. |
|
Kinematic stride length, no physics |
Inner loop of fast PSO; use for prefilter. |
Adapters
Two glue functions bridge DynamicFitness to the
older optimizer contracts:
as_eval_func()— wraps a fitness into pylinkage’s(linkage, dimensions, positions) → floatminimizer contract (usenegate=Truefor any minimizer).as_ga_fitness()— wraps a fitness into the GA’s(dna) → (score, initial_positions)tuple contract.
The same fitness object can therefore drive
particle_swarm_optimization(),
differential_evolution_optimization(),
minimize_linkage(),
GeneticOptimization, and
nsga_walking_optimization() without rewriting it.
The optimizer landscape
Pick the cheapest tool that answers your question. In rough cost order from fastest-per-eval to slowest:
Function / class |
What it varies |
Use when |
|---|---|---|
|
Dimensions only (no physics) |
Quick kinematic search; pair with
|
|
Dimensions only |
Stage global → local pipelines (DE → dual annealing → Nelder-Mead). |
|
Dimensions; JSON-checkpointed GA with multiprocessing |
Single-objective dynamic GA; good for the final selection round. |
|
Dimensions; multi-objective |
Pareto front of distance vs. energy vs. stability vs. gait. |
|
Topology and dimensions (catalog index) |
Pick a mechanism family + tune it jointly. Mixed chromosome. |
|
End-to-end pipeline |
Topology discovery → kinematic prefilter → dynamic fitness in one call. |
|
Phase offsets only |
Tune trot / pace / canter on a finished walker. |
|
Number of legs |
Post-hoc “how many legs?” study. |
Recommended pipeline
The README phrases this as “exploit symmetry”; in concept terms it’s just use a coarser model in the inner loop:
Kinematic stage. PSO or DE on the half-mechanism with
StrideFitness. Hundreds to thousands of evals per second; finds promising regions of the design space.Dynamic stage. Hand the survivor to
GeneticOptimizationornsga_walking_optimization()withCompositeFitness(distance + efficiency + stability + gait in one run).Gait tuning (optional).
optimize_gait()on the winner if you want a non-classical gait.
Everything visible — video(),
plot_pareto_front(),
plot_gait_diagram(),
plot_optimization_dashboard() — is for inspecting
results, not driving them. The optimizer hands you a Walker; the
visualizer tells you whether it walks the way you wanted.
Where to go next
Migrating from params to WorldConfig if you have legacy code using the
paramsdict.The grouped API toctree on Welcome to LeggedSnake’s documentation! covers each module in detail.
The numbered tutorial notebooks (
examples/01_walkers_gallery.ipynbthroughexamples/04_multi_objective_and_gait.ipynb) walk through the full pipeline end-to-end.