gait_analysis

Foot-contact event detection, gait cycles, and locomotion metrics.

Gait analysis for walking mechanism simulations.

Transforms raw joint trajectory data (loci) into biomechanics metrics: duty factor, stride frequency, phase offsets between feet, foot trajectory shape analysis, and gait cycle decomposition.

Example:

from leggedsnake.gait_analysis import analyze_gait

# After running a simulation with record_loci=True
result = fitness(topology, dimensions)
gait = analyze_gait(
    loci=result.loci,
    foot_ids=walker.get_feet(),
    dt=0.02,
)
print(gait.mean_duty_factor, gait.mean_stride_frequency)
print(gait.phase_offsets)
class leggedsnake.gait_analysis.FootEvent(foot_id: str, time: float, event_type: Literal['touchdown', 'liftoff'], position: tuple[float, float])

Bases: object

A single foot touchdown or liftoff event.

__init__(foot_id: str, time: float, event_type: Literal['touchdown', 'liftoff'], position: tuple[float, float]) None
event_type: Literal['touchdown', 'liftoff']
foot_id: str
position: tuple[float, float]
time: float
class leggedsnake.gait_analysis.GaitAnalysisResult(foot_events: list[FootEvent] = <factory>, gait_cycles: dict[str, list[GaitCycle]] = <factory>, foot_trajectories: dict[str, list[tuple[float, float]]] = <factory>, stability: StabilityTimeSeries | None = None, dt: float = 0.02)

Bases: object

Complete gait analysis for a simulation run.

Variables:
  • foot_events (list[FootEvent]) – All detected touchdown/liftoff events.

  • gait_cycles (dict[str, list[GaitCycle]]) – Stride cycles keyed by foot ID.

  • foot_trajectories (dict[str, list[tuple[float, float]]]) – Raw foot trajectories keyed by foot ID.

  • stability (StabilityTimeSeries | None) – Stability data if available.

  • dt (float) – Simulation timestep used.

__init__(foot_events: list[FootEvent] = <factory>, gait_cycles: dict[str, list[GaitCycle]] = <factory>, foot_trajectories: dict[str, list[tuple[float, float]]] = <factory>, stability: StabilityTimeSeries | None = None, dt: float = 0.02) None
dt: float = 0.02
energy_per_cycle(total_energy: float) float

Mean dissipated energy per stride cycle.

Parameters:

total_energy (float) – Energy accumulated over the full simulation (from _SimulationResult.total_energy or equivalent).

Returns:

total_energy / total_cycles averaged across feet. Returns 0 when no complete cycles were detected.

Return type:

float

foot_events: list[FootEvent]
foot_trajectories: dict[str, list[tuple[float, float]]]
property gait_asymmetry: float

Dispersion of duty factors across feet.

Population standard deviation of the per-foot mean duty factor. Zero when every foot spends the same fraction of its cycle in stance; larger when some legs drag and others rush.

gait_cycles: dict[str, list[GaitCycle]]
property mean_duty_factor: float

Average duty factor across all feet and cycles.

property mean_stride_frequency: float

Average stride frequency in Hz across all feet.

property mean_stride_length: float

Average horizontal distance between consecutive touchdowns.

property phase_offsets: dict[tuple[str, str], float]

Phase difference between each pair of feet.

Normalized to [0, 1) where 0 = in-phase, 0.5 = alternating.

stability: StabilityTimeSeries | None = None
summary_metrics() dict[str, float]

Flat dictionary of all scalar gait metrics.

property total_cycles: int

Total number of complete stride cycles across all feet.

class leggedsnake.gait_analysis.GaitCycle(foot_id: str, stance_start: float, stance_end: float, swing_start: float, swing_end: float)

Bases: object

Metrics for one stride cycle of a single foot.

__init__(foot_id: str, stance_start: float, stance_end: float, swing_start: float, swing_end: float) None
property duty_factor: float
foot_id: str
property stance_duration: float
stance_end: float
stance_start: float
property stride_period: float
property swing_duration: float
swing_end: float
swing_start: float
leggedsnake.gait_analysis.analyze_gait(loci: dict[str, list[tuple[float, float]]], foot_ids: list[str], stability: StabilityTimeSeries | None = None, dt: float = 0.02, contact_threshold: float = 0.1) GaitAnalysisResult

One-call gait analysis from simulation loci.

Parameters:
  • loci (dict[str, list[tuple[float, float]]]) – Joint trajectories keyed by node ID (from FitnessResult.loci).

  • foot_ids (list[str]) – Node IDs of feet (from Walker.get_feet()).

  • stability (StabilityTimeSeries | None) – Optional stability data to attach.

  • dt (float) – Simulation timestep.

  • contact_threshold (float) – Maximum y-coordinate for ground contact detection.

Return type:

GaitAnalysisResult

leggedsnake.gait_analysis.compute_cost_of_transport(energy: float, mass: float, distance: float) float

Mechanical cost of transport COT = E / (m · d).

Standard locomotion-efficiency metric — lower is better. The energy E should be the total mechanical work done by the motors over the run; m the walker mass; d the forward distance travelled. Dimensionless when E is in Joules, m in kg, d in metres (units cancel through g implicitly when divided by gravity, but the unitful form J/(kg·m) is what most biomechanics papers report).

Parameters:
  • energy (float) – Total energy spent (Joules).

  • mass (float) – Walker mass (kg).

  • distance (float) – Forward distance travelled (m).

Returns:

E / (m · d) — or 0.0 when mass or distance is non-positive.

Return type:

float

leggedsnake.gait_analysis.compute_foot_trajectory_metrics(trajectory: list[tuple[float, float]]) dict[str, float]

Per-foot trajectory shape metrics.

Parameters:

trajectory (list[tuple[float, float]]) – A single foot’s (x, y) trajectory.

Returns:

max_height, horizontal_range, path_length, smoothness (inverse mean curvature).

Return type:

dict[str, float]

leggedsnake.gait_analysis.compute_froude_number(speed: float, gravity: float, leg_length: float) float

Walking Froude number Fr = / (g · L).

Dimensionless gait metric introduced for ship hulls (William Froude) and brought to locomotion by R. McNeill Alexander. Lets walkers of very different sizes be compared directly: the walk-to-run gait transition occurs near Fr 0.5 across the animal kingdom.

Parameters:
  • speed (float) – Mean forward speed (m/s).

  • gravity (float) – Magnitude of gravitational acceleration (m/s², positive).

  • leg_length (float) – Characteristic length — typically hip height or, for arbitrary planar topologies, the linkage’s vertical extent.

Returns:

/ (g · L) — or 0.0 when gravity or leg length is non-positive.

Return type:

float

leggedsnake.gait_analysis.compute_phase_offsets(cycles: dict[str, list[GaitCycle]]) dict[tuple[str, str], float]

Phase difference between each pair of feet.

Compares the mean stance_start times, normalized by the mean stride period. Result is in [0, 1) where 0 = in-phase, 0.5 = alternating.

Parameters:

cycles (dict[str, list[GaitCycle]]) – Gait cycles keyed by foot ID.

Returns:

Phase offset for each foot pair.

Return type:

dict[tuple[str, str], float]

leggedsnake.gait_analysis.detect_foot_events(foot_trajectories: dict[str, list[tuple[float, float]]], contact_threshold: float = 0.1, dt: float = 0.02) list[FootEvent]

Detect touchdown/liftoff events from foot y-position threshold crossings.

A touchdown occurs when the foot y-coordinate drops below contact_threshold. A liftoff occurs when it rises back above.

Parameters:
  • foot_trajectories (dict[str, list[tuple[float, float]]]) – Foot trajectories keyed by foot ID, each a list of (x, y) points.

  • contact_threshold (float) – Maximum y-coordinate for ground contact.

  • dt (float) – Timestep between trajectory points.

Returns:

Chronologically sorted events across all feet.

Return type:

list[FootEvent]

leggedsnake.gait_analysis.extract_gait_cycles(events: list[FootEvent]) dict[str, list[GaitCycle]]

Group foot events into stride cycles per foot.

A cycle is: touchdown → liftoff → next touchdown.

Parameters:

events (list[FootEvent]) – Sorted foot events from detect_foot_events.

Returns:

Gait cycles keyed by foot ID.

Return type:

dict[str, list[GaitCycle]]