step.condition ============== .. py:module:: step.condition .. autoapi-nested-parse:: Composable stop conditions for motion steps. A StopCondition determines when a motion step should terminate. Conditions can be combined with ``|`` (any/or), ``&`` (all/and), ``>`` / ``+`` (then/sequence) operators. Example:: from raccoon.step.condition import on_black, on_white, after_seconds, after_cm, over_line # Stop when sensor sees black OR after 10 seconds drive_forward(speed=1.0).until(on_black(sensor) | after_seconds(10)) # Sequential: first wait for black, then travel 10 more cm # Use + instead of > to avoid Python's chained-comparison gotcha drive_forward(speed=1.0).until(on_black(sensor) + after_cm(10)) # Cross a line (black then white) three times drive_forward(speed=1.0).until(over_line(sensor) + over_line(sensor) + over_line(sensor)) Classes ------- .. autoapisummary:: step.condition.StopCondition step.condition.on_black step.condition.on_white step.condition.after_seconds step.condition.after_cm step.condition.after_forward_cm step.condition.after_lateral_cm step.condition.after_degrees step.condition.on_digital step.condition.on_analog_above step.condition.on_analog_below step.condition.stall_detected step.condition.custom Functions --------- .. autoapisummary:: step.condition.over_line Module Contents --------------- .. py:class:: StopCondition Base class for composable stop conditions. .. py:method:: start(robot: raccoon.robot.api.GenericRobot) -> None Called once when the motion step starts. Override to initialize state. .. py:method:: check(robot: raccoon.robot.api.GenericRobot) -> bool :abstractmethod: Return True to stop the motion. Called each update cycle. .. py:class:: on_black(sensor: raccoon.sensor_ir.IRSensor, threshold: float = 0.7) Bases: :py:obj:`StopCondition` Stop when an IR sensor detects a black surface. .. py:method:: check(robot: raccoon.robot.api.GenericRobot) -> bool Return True to stop the motion. Called each update cycle. .. py:class:: on_white(sensor: raccoon.sensor_ir.IRSensor, threshold: float = 0.7) Bases: :py:obj:`StopCondition` Stop when an IR sensor detects a white surface. .. py:method:: check(robot: raccoon.robot.api.GenericRobot) -> bool Return True to stop the motion. Called each update cycle. .. py:class:: after_seconds(seconds: float) Bases: :py:obj:`StopCondition` Stop after a fixed duration. .. py:method:: start(robot: raccoon.robot.api.GenericRobot) -> None Called once when the motion step starts. Override to initialize state. .. py:method:: check(robot: raccoon.robot.api.GenericRobot) -> bool Return True to stop the motion. Called each update cycle. .. py:class:: after_cm(cm: float, *, absolute: bool = False) Bases: :py:obj:`StopCondition` Stop after traveling a distance (uses odometry path length). By default operates in relative mode: the distance is measured from the robot's position when the condition starts. In absolute mode (``absolute=True``) the distance is measured from the odometry origin (last ``reset()``), so centimeters traveled *before* the condition started also count. Uses the cumulative path length (odometer) so it works correctly regardless of travel direction (forward, strafe, curved paths). .. py:method:: start(robot: raccoon.robot.api.GenericRobot) -> None Called once when the motion step starts. Override to initialize state. .. py:method:: check(robot: raccoon.robot.api.GenericRobot) -> bool Return True to stop the motion. Called each update cycle. .. py:class:: after_forward_cm(cm: float, *, absolute: bool = False) Bases: :py:obj:`_AxisDisplacementCondition` Stop after traveling a forward/backward displacement. Unlike :class:`after_cm` (which uses cumulative path length), this measures the *signed forward component* of displacement relative to the robot's heading at the moment this condition started. Any turning that happens *after* ``start()`` does not rotate the axis — it stays pinned to the original heading. A positive ``cm`` triggers after the robot has moved that far forward of its baseline pose; a negative ``cm`` triggers after moving that far backward. In absolute mode (``absolute=True``) the displacement is read directly from ``get_distance_from_origin().forward`` — i.e. measured from the odometry origin, projected onto the origin heading. .. py:class:: after_lateral_cm(cm: float, *, absolute: bool = False) Bases: :py:obj:`_AxisDisplacementCondition` Stop after traveling a lateral (sideways) displacement. Measures the *signed lateral component* of displacement relative to the robot's heading at the moment this condition started. Sign convention matches ``DistanceFromOrigin.lateral``: positive values point along the +lateral axis as defined there. Only meaningful on drivetrains that support lateral motion (e.g. mecanum / omni). On a differential drive this will stay near zero unless the robot rotates and then drives forward — in which case the world-frame displacement has a component sideways of the original heading. In absolute mode (``absolute=True``) the displacement is read directly from ``get_distance_from_origin().lateral``. .. py:class:: after_degrees(degrees: float) Bases: :py:obj:`StopCondition` Stop after turning a given angle (degrees). Uses the absolute IMU heading so it is unaffected by odometry resets. Tracks total unsigned rotation — works regardless of turn direction. .. py:method:: start(robot: raccoon.robot.api.GenericRobot) -> None Called once when the motion step starts. Override to initialize state. .. py:method:: check(robot: raccoon.robot.api.GenericRobot) -> bool Return True to stop the motion. Called each update cycle. .. py:class:: on_digital(sensor: raccoon.hal.DigitalSensor, pressed: bool = True) Bases: :py:obj:`StopCondition` Stop when a digital sensor reads a given state. By default stops when the sensor reads True (pressed). Set *pressed* to False to stop when the sensor is released instead. .. py:method:: check(robot: raccoon.robot.api.GenericRobot) -> bool Return True to stop the motion. Called each update cycle. .. py:class:: on_analog_above(sensor: raccoon.hal.AnalogSensor, threshold: int) Bases: :py:obj:`StopCondition` Stop when an analog sensor reading exceeds a threshold. .. py:method:: check(robot: raccoon.robot.api.GenericRobot) -> bool Return True to stop the motion. Called each update cycle. .. py:class:: on_analog_below(sensor: raccoon.hal.AnalogSensor, threshold: int) Bases: :py:obj:`StopCondition` Stop when an analog sensor reading drops below a threshold. .. py:method:: check(robot: raccoon.robot.api.GenericRobot) -> bool Return True to stop the motion. Called each update cycle. .. py:class:: stall_detected(motor: raccoon.hal.Motor, threshold_tps: int = 10, duration: float = 0.25) Bases: :py:obj:`StopCondition` Stop when a motor appears stalled. A motor is considered stalled when its position changes by fewer than *threshold_tps* ticks per second for at least *duration* seconds continuously. .. py:method:: start(robot: raccoon.robot.api.GenericRobot) -> None Called once when the motion step starts. Override to initialize state. .. py:method:: check(robot: raccoon.robot.api.GenericRobot) -> bool Return True to stop the motion. Called each update cycle. .. py:class:: custom(fn: Callable[[raccoon.robot.api.GenericRobot], bool]) Bases: :py:obj:`StopCondition` Stop based on a user-provided callable. The callable receives the robot and returns True to stop:: custom(lambda robot: robot.et_sensor.value() < 15) .. py:method:: check(robot: raccoon.robot.api.GenericRobot) -> bool Return True to stop the motion. Called each update cycle. .. py:function:: over_line(sensor: raccoon.sensor_ir.IRSensor, black_threshold: float = 0.7, white_threshold: float = 0.7) -> StopCondition Stop after crossing a line (black then white). Equivalent to ``on_black(sensor) > on_white(sensor)`` — waits for the sensor to see black, then waits for it to see white again. :param sensor: IR sensor to monitor. :param black_threshold: Probability threshold for the black phase. :param white_threshold: Probability threshold for the white phase.