testing.sim

High-level Python API for the libstp simulator.

This module wraps raccoon.sim (the pybind11 bindings) with ergonomic helpers so a team’s pytest can do:

from raccoon.testing.sim import use_scene

with use_scene("empty_table.ftmap", robot=my_robot_config, start=(50, 50, 0)):
    await drive_forward(cm=30).run_step(robot)
    assert pose().x == pytest.approx(80.0, abs=2.0)

The context manager configures the process-wide MockPlatform singleton to talk to a fresh SimWorld for the duration of the block, then detaches on exit so subsequent code paths see the stock mock HAL.

Only available when the wheel is built with DRIVER_BUNDLE=mock — the raccoon.sim.mock submodule must be present. Importing this module on a wombat-bundle wheel raises RuntimeError.

This module lives under raccoon.testing because it is exclusively a test-harness concern; the actual C++ sim bindings still live at raccoon.sim. Historically this API was at raccoon.step.sim — that path remains as a deprecation shim and will be removed in a future release.

Classes

LineSensorMount

DistanceSensorMount

SimRobotConfig

Sim-side counterpart to the team's GenericRobot geometry/kinematics.

Functions

configure(, auto_tick, auto_tick_max_step_sec)

Attach a fresh sim to the MockPlatform singleton.

detach(→ None)

Disconnect any attached sim. The mock HAL goes back to zero odometry.

pose(→ raccoon.sim.Pose2D)

Return the sim's current ground-truth pose (cm, cm, rad).

yaw_rate(→ float)

Return the sim's current yaw rate in rad/s.

tick(→ None)

Manually advance the sim. Use this for fully deterministic tests.

use_scene(, auto_tick, auto_tick_max_step_sec)

Context manager: attach the sim for the duration of a with block.

Module Contents

class testing.sim.LineSensorMount
analog_port: int
forward_cm: float
strafe_cm: float
name: str = ''
class testing.sim.DistanceSensorMount
analog_port: int
forward_cm: float
strafe_cm: float
mount_angle_rad: float = 0.0
max_range_cm: float = 100.0
name: str = ''
class testing.sim.SimRobotConfig

Sim-side counterpart to the team’s GenericRobot geometry/kinematics.

Defaults match a typical Botball wombat. Override fields that differ on your robot — they must agree with whatever the real Drive / DifferentialKinematics were constructed with so the sim physics matches what the motion controllers expect.

width_cm: float = 18.0
length_cm: float = 18.0
rotation_center_forward_cm: float = 0.0
rotation_center_strafe_cm: float = 0.0
wheel_radius_m: float = 0.03
track_width_m: float = 0.15
wheelbase_m: float = 0.15
drivetrain: Literal['diff', 'mecanum'] = 'diff'
left_motor_port: int = 0
right_motor_port: int = 1
left_motor_inverted: bool = False
right_motor_inverted: bool = False
fl_motor_port: int = 0
fr_motor_port: int = 1
bl_motor_port: int = 2
br_motor_port: int = 3
fl_motor_inverted: bool = False
fr_motor_inverted: bool = False
bl_motor_inverted: bool = False
br_motor_inverted: bool = False
max_wheel_velocity_rad_s: float = 30.0
motor_time_constant_sec: float = 0.05
ticks_to_rad: float = 0.0043633231299858195
motor_calibration_by_port: dict[int, tuple[float, float]]
viscous_drag_coeff: float = 0.0
coulomb_friction_rad_s2: float = 0.0
bemf_noise_stddev: float = 0.0
line_sensors: list[LineSensorMount] = []
distance_sensors: list[DistanceSensorMount] = []
testing.sim.configure(scene: str | pathlib.Path, *, robot: SimRobotConfig | None = None, start: PoseLike = (0.0, 0.0, 0.0), auto_tick: bool = True, auto_tick_max_step_sec: float = 0.05) → None

Attach a fresh sim to the MockPlatform singleton.

After this returns, motor commands written through any HAL Motor drive the simulated chassis, and OdometryBridge::readOdometry reports its pose. Auto-tick is enabled by default so a real motion loop reading odometry on its own schedule will see the sim advance with wall time.

testing.sim.detach() → None

Disconnect any attached sim. The mock HAL goes back to zero odometry.

testing.sim.pose() → raccoon.sim.Pose2D

Return the sim’s current ground-truth pose (cm, cm, rad).

testing.sim.yaw_rate() → float

Return the sim’s current yaw rate in rad/s.

testing.sim.tick(dt_seconds: float) → None

Manually advance the sim. Use this for fully deterministic tests.

testing.sim.use_scene(scene: str | pathlib.Path, *, robot: SimRobotConfig | None = None, start: PoseLike = (0.0, 0.0, 0.0), auto_tick: bool = True, auto_tick_max_step_sec: float = 0.05) → collections.abc.Iterator[None]

Context manager: attach the sim for the duration of a with block.

Example:

with use_scene("empty_table.ftmap", start=(20, 50, 0)):
    await drive_forward(cm=30).run_step(my_robot)
    x, y, _ = pose()
    assert x == pytest.approx(50, abs=2)