step.condition¶
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¶
Base class for composable stop conditions. |
|
Stop when an IR sensor detects a black surface. |
|
Stop when an IR sensor detects a white surface. |
|
Stop after a fixed duration. |
|
Stop after traveling a distance (uses odometry path length). |
|
Stop after a forward/backward displacement along the reference line. |
|
Stop after a lateral (sideways) displacement along the reference line. |
|
Stop after turning a given angle (degrees). |
|
Stop once the robot is tilted off horizontal by at least an angle. |
|
Stop once the robot is back to (near-)horizontal. |
|
Stop when a digital sensor reads a given state. |
|
Stop when an analog sensor reading exceeds a threshold. |
|
Stop when an analog sensor reading drops below a threshold. |
|
Stop when a motor appears stalled. |
|
Stop based on a user-provided callable. |
Functions¶
|
Stop after crossing a line (black then white). |
|
Stop after crossing a ramp (tilt up, then back to level). |
Module Contents¶
- class step.condition.StopCondition¶
Base class for composable stop conditions.
- start(robot: raccoon.robot.api.GenericRobot) None¶
Called once when the motion step starts. Override to initialize state.
- check(robot: raccoon.robot.api.GenericRobot) bool¶
Return True to stop the motion. Called each update cycle.
Public entry point. Delegates to the subclass
_evaluate()and emits a one-shotdebuglog the moment the condition first fires, so mission logs show which condition completed and its state at completion._state()(which may poll sensors/odometry) is only evaluated on the firing tick, so this adds no per-tick cost to the 100 Hz motion loop.
- class step.condition.on_black(sensor: raccoon.sensor_ir.IRSensor, threshold: float = 0.7)¶
Bases:
StopConditionStop when an IR sensor detects a black surface.
- class step.condition.on_white(sensor: raccoon.sensor_ir.IRSensor, threshold: float = 0.7)¶
Bases:
StopConditionStop when an IR sensor detects a white surface.
- class step.condition.after_seconds(seconds: float)¶
Bases:
StopConditionStop after a fixed duration.
- class step.condition.after_cm(cm: float, *, absolute: bool = False)¶
Bases:
StopConditionStop 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 (lastreset()), 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).
- class step.condition.after_forward_cm(cm: float, *, heading: float | None = None, absolute: bool = False)¶
Bases:
_AxisDisplacementConditionStop after a forward/backward displacement along the reference line.
Unlike
after_cm(which uses cumulative path length), this measures the signed forward component of displacement relative to the reference axis — the robot’s heading at the moment this condition started. Any turning that happens afterstart()does not rotate the axis: it stays pinned to the original heading, so “20 cm forward” means 20 cm along that line. A line-follow correcting around the reference therefore needs slightly more path to reach the target, and a robot that turns 90° off the reference can never reach it.A positive
cmtriggers after the robot has moved that far forward of its baseline along the reference; a negativecmtriggers after moving that far backward.By default the reference line is the robot’s heading at
start(). Passheading=<deg>to pin it to a targeted absolute heading instead — e.g.after_forward_cm(20, heading=0)means “20 cm along the heading-0 line”, robust to the robot being crooked when the condition starts.In absolute mode (
absolute=True) the displacement is read directly fromget_distance_from_origin().forward— i.e. measured from the odometry origin, projected onto the origin heading.
- class step.condition.after_lateral_cm(cm: float, *, heading: float | None = None, absolute: bool = False)¶
Bases:
_AxisDisplacementConditionStop after a lateral (sideways) displacement along the reference line.
Measures the signed lateral component of displacement relative to the reference axis — 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 fromget_distance_from_origin().lateral.
- class step.condition.after_degrees(degrees: float)¶
Bases:
StopConditionStop after turning a given angle (degrees).
Reads the heading from
robot.odometry.get_pose().heading— the same source the motion controllers regulate on — so the measured rotation and the executed feedback share one frame. Tracks total unsigned rotation, so it works regardless of turn direction.
- class step.condition.on_incline(min_deg: float = 8.0, *, up_axis: str = 'z', smoothing: float = 0.2)¶
Bases:
_InclinationConditionStop once the robot is tilted off horizontal by at least an angle.
Detects when the chassis pitches (or rolls) up onto a slope — e.g. the front wheels climbing onto a ramp. The inclination is derived from the IMU DMP orientation quaternion relative to the flat reference captured at step start (see
_InclinationCondition); the condition fires as soon as the smoothed tilt reachesmin_deg.Pair it with
on_level()to bracket a ramp:on_incline()marks entering the slope,on_level()marks cresting back onto flat ground.over_ramp()wires both together.- Prerequisites:
A working IMU. See
_InclinationConditionfor tuning notes onup_axisandsmoothing.
- Parameters:
min_deg – Tilt threshold in degrees (0–90). Fires when the measured inclination is at least this large. A 20° ramp reads ~20°, so a threshold around 8–12° detects entry robustly while ignoring floor roughness and driving jitter. Default 8.0.
up_axis – Deprecated / no effect (the quaternion tilt is yaw-invariant); accepted only for backward compatibility.
smoothing – EMA factor in (0, 1] applied to the tilt angle to reject any residual jitter (default 0.2).
- Returns:
A
StopConditionthat fires while tilted.
Example:
from raccoon.step.motion import drive_forward from raccoon.step.condition import on_incline # Drive until the robot noses up onto the ramp. drive_forward(speed=0.4).until(on_incline(10))
- class step.condition.on_level(max_deg: float = 4.0, *, up_axis: str = 'z', smoothing: float = 0.2)¶
Bases:
_InclinationConditionStop once the robot is back to (near-)horizontal.
The counterpart to
on_incline(): it fires when the smoothed tilt (from the IMU DMP orientation, relative to the flat start reference) drops to at mostmax_deg— i.e. the robot has crested a ramp and settled onto flat ground, or finished descending a slope.Because it triggers on a low tilt, using it on its own from a flat start would fire immediately. It is meant to run after the robot is already tilted — typically as the second stage of a chain, e.g.
on_incline(10) + on_level(4)(which is whatover_ramp()builds).- Prerequisites:
A working IMU. See
_InclinationConditionfor tuning notes.
- Parameters:
max_deg – Tilt threshold in degrees (0–90). Fires when the measured inclination is at most this large. Keep it a few degrees below the matching
on_inclinethreshold so transitional wobble does not fire it early (hysteresis). Default 4.0.up_axis – Deprecated / no effect (the quaternion tilt is yaw-invariant); accepted only for backward compatibility.
smoothing – EMA factor in (0, 1] applied to the tilt angle (default 0.2).
- Returns:
A
StopConditionthat fires while (near) level.
Example:
from raccoon.step.condition import on_incline, on_level # Climb onto the ramp, then keep going until back on the flat top. drive_forward(speed=0.4).until(on_incline(10) + on_level(4))
- class step.condition.on_digital(sensor: raccoon.hal.DigitalSensor, pressed: bool = True)¶
Bases:
StopConditionStop 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.
- class step.condition.on_analog_above(sensor: raccoon.hal.AnalogSensor, threshold: int)¶
Bases:
StopConditionStop when an analog sensor reading exceeds a threshold.
- class step.condition.on_analog_below(sensor: raccoon.hal.AnalogSensor, threshold: int)¶
Bases:
StopConditionStop when an analog sensor reading drops below a threshold.
- class step.condition.stall_detected(motor: raccoon.hal.Motor, threshold_tps: int = 10, duration: float = 0.25)¶
Bases:
StopConditionStop 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.
- class step.condition.custom(fn: collections.abc.Callable[[raccoon.robot.api.GenericRobot], bool])¶
Bases:
StopConditionStop based on a user-provided callable.
The callable receives the robot and returns True to stop:
custom(lambda robot: robot.et_sensor.value() < 15)
- step.condition.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.- Parameters:
sensor – IR sensor to monitor.
black_threshold – Probability threshold for the black phase.
white_threshold – Probability threshold for the white phase.
- step.condition.over_ramp(enter_deg: float = 8.0, exit_deg: float = 4.0, *, up_axis: str = 'z', smoothing: float = 0.2) StopCondition¶
Stop after crossing a ramp (tilt up, then back to level).
Equivalent to
on_incline(enter_deg) + on_level(exit_deg)— waits for the robot to tilt onto the slope, then waits for it to settle back onto flat ground once it crests. Both stages read the DMP orientation relative to the flat reference frommark_heading_reference()(which must have been called on flat ground beforehand), so no odometry or line sensor is needed. Use it to drive the whole way up and over a ramp with a single stop condition:drive_forward(speed=0.4).until(over_ramp())
To act on just one edge, use
on_incline()(entering) oron_level()(leaving) on their own.- Parameters:
enter_deg – Tilt at which the ramp is considered entered (default 8.0).
exit_deg – Tilt at or below which the robot is considered back on the flat (default 4.0). Keep it below
enter_degfor hysteresis.up_axis – Deprecated / no effect (kept for backward compatibility).
smoothing – EMA factor in (0, 1] for both stages (default 0.2).
- Returns:
A
StopConditionthat fires once the ramp has been crossed.