step.motion.path.passes.velocity_profile

Time-optimal velocity profiling pass.

Generalises the executor’s local warm-start (carry velocity only across the single adjacent same-type seam) into a GLOBAL forward/backward sweep over the whole compiled path. It computes, for every motion segment, the fastest feasible ENTRY and EXIT speed (m/s) such that the robot:

  • starts and ends at rest,

  • never exceeds a segment’s cruise cap (max_speed * speed_scale) or, for arcs, the curvature cap v <= sqrt(lat_accel * R),

  • can always accelerate up to / decelerate down to the next constraint within a segment’s length (the two-sweep feasibility), and

  • drops to a full stop at every REAL barrier — a turn (rotation in place), a direction reversal, a blocking inline side action, a deferred node, and the path’s last segment.

A sensor-bounded .until() leg’s end is NOT an unconditional stop: when the next leg continues in the SAME direction the robot FLOWS THROUGH the sensor boundary (capped by sensor_carry_mps) instead of braking to zero — “time- optimal on steroids” for the sensor-driven style. See _seam_speed_cap().

The speeds are stamped onto Segment.entry_speed_mps / exit_speed_mps. The pass is PATH-PRESERVING — it changes only speed, never geometry — and is opt-in via optimize(steps).time_optimal(). With the pass off the fields stay None and the executor’s behaviour is byte-identical to before.

Attributes

DEFAULT_MAX_SPEED_MPS

DEFAULT_ACCEL_MPS2

DEFAULT_LATERAL_ACCEL_MPS2

DEFAULT_SENSOR_CARRY_MPS

Classes

VelocityProfilePass

Compiler pass: stamp a global time-optimal velocity profile (entry/exit

Functions

run_velocity_profile(...)

Stamp feasible entry/exit speeds onto every motion Segment via a global

Module Contents

step.motion.path.passes.velocity_profile.DEFAULT_MAX_SPEED_MPS = 1.0
step.motion.path.passes.velocity_profile.DEFAULT_ACCEL_MPS2 = 0.6
step.motion.path.passes.velocity_profile.DEFAULT_LATERAL_ACCEL_MPS2 = 0.5
step.motion.path.passes.velocity_profile.DEFAULT_SENSOR_CARRY_MPS = inf
step.motion.path.passes.velocity_profile.run_velocity_profile(nodes: list[step.motion.path.ir.PathNode | None], *, max_speed_mps: float = DEFAULT_MAX_SPEED_MPS, accel_mps2: float = DEFAULT_ACCEL_MPS2, lateral_accel_mps2: float = DEFAULT_LATERAL_ACCEL_MPS2, sensor_carry_mps: float = DEFAULT_SENSOR_CARRY_MPS) list[step.motion.path.ir.PathNode | None]

Stamp feasible entry/exit speeds onto every motion Segment via a global two-sweep. Returns a NEW node list (segments replaced); non-motion nodes are preserved in place.

class step.motion.path.passes.velocity_profile.VelocityProfilePass(max_speed_mps: float = DEFAULT_MAX_SPEED_MPS, accel_mps2: float = DEFAULT_ACCEL_MPS2, lateral_accel_mps2: float = DEFAULT_LATERAL_ACCEL_MPS2, sensor_carry_mps: float = DEFAULT_SENSOR_CARRY_MPS)

Compiler pass: stamp a global time-optimal velocity profile (entry/exit speeds) onto the path. Path-preserving; opt-in via .time_optimal().

name = 'time_optimal'
max_speed_mps = 1.0
accel_mps2 = 0.6
lateral_accel_mps2 = 0.5
sensor_carry_mps = inf
run(nodes: list[step.motion.path.ir.PathNode | None]) list[step.motion.path.ir.PathNode | None]