step.motion.smooth_path¶
Smooth multi-segment motion path with zero-stop transitions.
Supports bare motion steps, nested seq(), parallel() with a motion
spine, background() steps, and Run actions. Composite steps are
flattened into a linear path of motion segments and side actions at
construction time, with deferred steps resolved at runtime.
Classes¶
Execute a sequence of motion segments with smooth velocity transitions. |
Functions¶
|
Execute motion steps with smooth velocity transitions between them. |
Module Contents¶
- class step.motion.smooth_path.SmoothPath(steps: list, correct: bool = True, optimize: bool = False, corner_cut_m: float = 0.0, spline: bool = False)¶
Bases:
step.StepExecute a sequence of motion segments with smooth velocity transitions.
Instead of decelerating to zero between each step, carries velocity across segment boundaries using warm-started motion controllers.
Supports composite steps: nested
seq()is flattened,parallel()with a motion spine runs side-effect branches concurrently,background()steps are launched without blocking, and non-drive steps (servos, Run actions, etc.) execute at transition points.- required_resources() frozenset[str]¶
Return the hardware resources this step requires exclusive access to.
For leaf steps (drive, motor, servo), return the resources this step directly uses. Composite steps override
collected_resourcesinstead to include children —required_resourcesstays empty for composites because they don’t touch hardware themselves.
- collected_resources() frozenset[str]¶
Return all resources this step and its children require.
Used by
validate_no_overlapfor static conflict detection at construction time. Leaf steps don’t need to override this — the default delegates torequired_resources. Composite steps override to union their children’s collected resources.
- step.motion.smooth_path.smooth_path(*steps, correct: bool = True, optimize: bool = False, corner_cut_cm: float | None = None, spline: bool = False) SmoothPath¶
Execute motion steps with smooth velocity transitions between them.
Eliminates the velocity drops that occur when steps are chained via
seq(). Instead of decelerating to zero and hard-stopping between each step,smooth_pathcarries velocity across segment boundaries, producing continuous fluid motion.For same-type transitions (e.g., drive to drive), the velocity is carried seamlessly via warm-started motion controllers with zero discontinuity. For cross-type transitions (e.g., drive to turn), a soft stop is used which smoothly decelerates without resetting PID state.
When
correctis enabled (default), the path tracks the robot’s world-frame pose and corrects accumulated position and heading errors at each segment transition. Along-track errors adjust the next linear segment’s distance, cross-track errors bias the heading hold, and heading errors adjust turn/arc angles. All corrections are clamped to safe limits (3 cm distance, 5 deg heading).Supports composite steps:
Nested seq(): Automatically flattened into the motion spine.
parallel(): The branch containing drive steps becomes the motion spine; other branches run concurrently as side effects.
background(): Launched without blocking the motion flow.
Non-drive steps (servos,
Runactions, etc.): Execute at transition points between motion segments.
Steps with
.until()conditions are fully supported. When a condition fires, the current velocity is carried into the next segment immediately.Supports
Defersteps (e.g.,turn_to_heading_right), which are resolved lazily at the actual transition point so that heading-dependent steps see the robot’s current heading, not the heading at the start of the path.Optimization (all opt-in):
optimize=Trueapplies algebraic simplification passes at construction time, before execution:Merge: adjacent same-type, same-direction, same-axis segments with no conditions are collapsed into one.
drive(30) + drive(20)becomesdrive(50);turn(30°) + turn(20°)becomesturn(50°). Only same-direction merges are performed (drive(30) + drive(-20)is not merged — intent may differ from net displacement)._SideActionand deferred nodes act as merge barriers.
corner_cut_cm=N(implies algebraic merge as well) replaceslinear + turn + lineartriples withlinear + arc + linearby cutting N cm from each straight leg and inserting a circular arc of radiusR = N / tan(|θ| / 2)at the corner. The robot never slows for the turn — it follows the arc at cruise speed. Only segments with known endpoints and no conditions are eligible.Note on
_SideActionbarriers: abackground()or non-drive step between two segments pins that step to the transition point. Optimization never reorders across side actions, preserving the execution contract.- Prerequisites:
calibrate_distance()if any segment uses distance-based mode.mark_heading_reference()if any segment uses heading hold.
- Parameters:
*steps – Motion steps to execute smoothly. Accepts
drive_forward,drive_backward,strafe_left,strafe_right,turn_left,turn_right,turn_to_heading_left,turn_to_heading_right,drive_arc_left,drive_arc_right,follow_line,follow_line_single,spline, nestedseq(),parallel()with a motion spine,background(), and their builder variants with.until()and.speed().follow_line/follow_line_singlesteps warm-start from a preceding forward drive segment; the subsequent segment cold-starts from the line-follow exit velocity.splinesteps always cold-start and exit at zero velocity.correct – Enable world-frame error correction across segment transitions. Default
True. Set toFalsefor the legacy uncorrected behavior.optimize – Merge adjacent same-type segments algebraically. Default
False(opt-in).corner_cut_cm – If set, replace right-angle drive+turn+drive corners with arcs of radius
corner_cut_cm / tan(|θ|/2), cutting corner_cut_cm cm from each straight leg. Also enables the merge pass. Mutually exclusive withspline. DefaultNone(disabled).spline – If True, convert the drive/turn sequence into waypoints and follow them with a Catmull-Rom spline (
SplinePath). The entire path executes as a single continuous curve — no segment transitions, no velocity drops. Requires all segments to have known endpoints and no conditions or side actions. Mutually exclusive withcorner_cut_cm. DefaultFalse(disabled).
- Returns:
A SmoothPath step that executes the segments as one fluid motion.
Example:
from raccoon.step.motion import smooth_path, drive_forward, turn_to_heading_right from raccoon.step.condition import on_black # Three drives with no stops between them smooth_path( drive_forward(50), drive_forward(30), drive_forward(20), ) # Drive, turn, drive — soft transition at type boundaries smooth_path( drive_forward(50), turn_to_heading_right(90), drive_forward(30), ) # Merge adjacent drives and cut corners with 5 cm arcs smooth_path( drive_forward(30), drive_forward(20), turn_left(90), drive_forward(40), optimize=True, corner_cut_cm=5.0, ) # → drive(50) + arc(R=5cm, 90°) + drive(35) # Condition-based segment carries velocity into the next smooth_path( drive_forward(speed=0.8).until(on_black(sensor)), drive_forward(20), ) # Parallel: servo moves while driving continuously smooth_path( drive_forward(30), parallel( [drive_forward(20), drive_forward(10)], [servo_move(0, 1500)], ), ) # Background: non-blocking step alongside motion smooth_path( drive_forward(30), background(servo_move(0, 1500)), drive_forward(20), ) # Disable correction for legacy behavior smooth_path( drive_forward(50), turn_to_heading_right(90), drive_forward(30), correct=False, ) # Spline mode: drive+turn+drive → smooth curved path via Catmull-Rom. # The robot follows the spline tangent; the drive and turn steps provide # the waypoints and heading changes. Requires at least 2 linear # segments; raises ValueError for condition-based steps, side actions, # arc segments, or fewer than 2 waypoints. smooth_path( drive_forward(50), turn_right(90), drive_forward(30), spline=True, )