step.motion.path.passes.resolve_heading

resolve_heading pass — stamp compile-time angles onto heading turns.

TurnToHeading lowers to an OPAQUE turn segment (angle_rad=None): its relative angle is only known at on_start, when the HeadingReferenceService computes the shortest-path delta from the live heading. That opacity blocks every geometry pass — most importantly corner_cut, which needs a known angle_rad to compute the arc radius — so a mission written in the recommended drift-robust style (turn_to_heading_* `` + ``heading= pins) silently gets ZERO benefit from optimize(...).cut_corners(...).

Path compilation happens inside Optimizer._execute_step — immediately before the path runs — so the robot’s CURRENT heading is the path-start heading. This pass integrates a running absolute heading through the node list (exactly like absolute_heading / to_absolute) and, wherever the running heading is provably known, computes the TurnToHeading delta at compile time and stamps it onto the segment as angle_rad.

The segment KEEPS its opaque_step: a stamped turn that no later pass consumes still executes as the original drift-corrected TurnToHeading (the executor prefers the opaque adapter). Only a pass that geometrically REPLACES the turn — corner_cut folding it into an arc — uses the stamp, which is exactly the trade corner cutting always makes (open-loop arc, downstream heading= pins re-correct).

Running-heading bookkeeping (absolute radians, CCW-positive):

  • start: live heading at compile time (known)

  • turn with known angle_rad — advance

  • TurnToHeading — after it, heading == its absolute target, so it RE-ESTABLISHES a known heading even when the current one is unknown (the turn itself is only stamped when the ENTRY heading is known)

  • arc with known arc_angle_rad — advance; crab_arc — unchanged

  • linear / diagonal with heading_deg pin — heading := pin (known)

  • linear / diagonal without pin — heading held, unchanged

  • follow_line without pin, spline — unknown

  • SideAction / deferred None — unknown (a side action may drive)

If no heading reference is marked (or no robot is available, e.g. in explain()), the pass is a no-op.

Classes

ResolveHeadingTurnsPass

Stamp known relative angles onto TurnToHeading segments.

Module Contents

class step.motion.path.passes.resolve_heading.ResolveHeadingTurnsPass(robot: GenericRobot | None = None)

Stamp known relative angles onto TurnToHeading segments.

name = 'resolve_heading'
run(nodes: list[step.motion.path.ir.PathNode | None]) list[step.motion.path.ir.PathNode | None]