step.motion.spline_path ======================= .. py:module:: step.motion.spline_path .. autoapi-nested-parse:: Spline path motion step — smooth curved path through waypoints. Classes ------- .. autoapisummary:: step.motion.spline_path.SplinePath Functions --------- .. autoapisummary:: step.motion.spline_path.spline Module Contents --------------- .. py:class:: SplinePath(waypoints: list[tuple[float, Ellipsis]], speed: float, absolute_heading: bool = False, final_heading_deg: float | None = None) Bases: :py:obj:`step.motion.motion_step.MotionStep` Drive along a centripetal Catmull-Rom spline through waypoints. Supports two waypoint formats: - 2-tuples (forward_cm, left_cm): heading follows spline tangent. - 3-tuples (forward_cm, left_cm, heading_deg): explicit heading at each waypoint, linearly interpolated vs arc-length. Omni drivetrains only. .. py:method:: on_start(robot: raccoon.robot.api.GenericRobot) -> None .. py:method:: on_update(robot: raccoon.robot.api.GenericRobot, dt: float) -> bool .. py:function:: spline(*waypoints: tuple[float, Ellipsis], speed: float = 1.0, absolute_heading: bool = False, final_heading: float | None = None) -> SplinePath Drive along a smooth curved path through a series of waypoints. The robot follows a centripetal Catmull-Rom spline that passes through every waypoint with continuous velocity. A trapezoidal velocity profile on arc-length provides smooth acceleration and deceleration along the path, while a heading PID keeps the robot oriented along the curve. Waypoints are specified relative to the robot's pose when the step starts. Two formats are supported: - **2-tuples** ``(forward_cm, left_cm)``: heading automatically follows the spline tangent. Works on both differential and omni drivetrains. - **3-tuples** ``(forward_cm, left_cm, heading_deg)``: explicit heading at each waypoint, linearly interpolated along the path. Omni drivetrains only — raises ``ValueError`` on differential bots. **Absolute heading mode** (``absolute_heading=True``): the heading PID computes its error against the IMU's absolute heading (``get_absolute_heading()``) which is never reset by odometry resets. The absolute heading at the start of the step is captured and used as an offset so that waypoint headings are still expressed relative to the robot's facing direction at the start — the convention is unchanged. Use this when the spline is chained after other motions that reset odometry and you need heading tracking to remain stable. **Final heading** (``final_heading``): the heading the robot should be facing when it arrives at the last waypoint, in degrees. The robot smoothly rotates from its start heading to this target heading as it follows the path (linear interpolation by arc-length). Ignored when 3-tuple waypoints are used (per-waypoint headings take precedence). When ``absolute_heading=True``, ``final_heading`` is interpreted as an absolute heading relative to the heading reference (same convention as ``drive_forward(heading=…)``). When ``absolute_heading=False``, ``final_heading`` is relative to the robot's start orientation (0 = keep facing forward, 90 = face 90° CCW at the end). Prerequisites: Drive characterization (``auto_tune`` or ``characterize_drive``) must have been run so that axis constraints are populated. ``mark_heading_reference()`` required when using ``absolute_heading=True`` with ``final_heading``. :param \*waypoints: Two or more waypoints as tuples. ``forward_cm`` is centimeters ahead of the start position (positive = forward). ``left_cm`` is centimeters to the left (positive = left). Optional ``heading_deg`` is the desired heading in degrees (0 = initial heading, positive = CCW). :param speed: Fraction of maximum speed, 0.0 to 1.0 (default 1.0). :param absolute_heading: If True, compute heading error against the absolute IMU heading instead of the reset-relative heading (default False). :param final_heading: Optional desired heading in degrees at the end of the path. Relative to start orientation when ``absolute_heading=False``; absolute (heading-reference-based) when ``absolute_heading=True``. Default None (no heading target). :returns: A SplinePath step configured for the described curved path. :raises ValueError: If fewer than 2 waypoints or inconsistent tuple sizes. :raises ValueError: If explicit headings (3-tuples) are used on a non-omni drivetrain. Example:: from raccoon.step.motion import spline # S-curve around an obstacle (heading follows tangent) spline((30, 0), (50, 15), (50, 30), (30, 30)) # Gentle curve at half speed spline((40, 0), (60, 20), speed=0.5) # Arrive facing 90° left of start orientation spline((30, 0), (50, 20), final_heading=90) # Omni: curve while rotating to face 90° left at the end spline((30, 0, 0), (50, 15, 45), (50, 30, 90)) # After an odometry reset, keep heading stable against the IMU spline((30, 0), (50, 20), absolute_heading=True) # Absolute heading + arrive facing north (heading reference = north) spline((30, 0), (50, 20), absolute_heading=True, final_heading=0)