step.motion.custom_velocity_dsl =============================== .. py:module:: step.motion.custom_velocity_dsl .. autoapi-nested-parse:: DSL builder and factory function for CustomVelocity. Classes ------- .. autoapisummary:: step.motion.custom_velocity_dsl.CustomVelocityBuilder Functions --------- .. autoapisummary:: step.motion.custom_velocity_dsl.custom_velocity Module Contents --------------- .. py:class:: CustomVelocityBuilder Bases: :py:obj:`raccoon.step.step_builder.StepBuilder` Builder for CustomVelocity. Provides the fluent ``.until()`` method. .. py:method:: until(value: raccoon.step.condition.StopCondition) -> CustomVelocityBuilder Set the stop condition. :raises TypeError: If *value* is not a ``StopCondition``. .. py:function:: custom_velocity(velocity_fn: step.motion.custom_velocity.VelocityFn, until: Optional[raccoon.step.condition.StopCondition] = None) -> CustomVelocityBuilder Run a user-defined velocity function each update cycle. Calls ``velocity_fn(robot, dt)`` at ~100 Hz and forwards the returned ``(vx, vy, omega)`` directly to the chassis velocity controller. This gives full control over all three axes simultaneously — forward/backward (``vx``), lateral (``vy``), and rotation (``omega``) — within the same closed-loop drive update that every other motion step uses. The velocity function receives the live robot handle so it can read sensors, odometry, and any other robot state to compute the next command. ``dt`` is the measured time since the last cycle in seconds. The step runs until the ``until`` condition fires (or until an external combinator such as ``do_until_checkpoint`` cancels it). On exit the drive is hard-stopped. :param velocity_fn: Callable with signature ``(robot: GenericRobot, dt: float) -> tuple[float, float, float]`` returning ``(vx, vy, omega)`` as fractions of each axis's configured max velocity. -1.0 = full reverse, 0.0 = stop, 1.0 = full forward. Values outside ±1.0 are not clamped but will saturate the underlying PID controller. :param until: Optional stop condition. Can also be set fluently via ``.until(condition)`` on the returned builder. :returns: A ``CustomVelocityBuilder`` (chainable via ``.until()``, ``.on_anomaly()``, ``.skip_timing()``). :raises TypeError: If ``velocity_fn`` is not callable, or ``until`` is not a ``StopCondition``. Example:: from libstp.step.motion import custom_velocity from libstp.step.condition import after_seconds, on_black # Drive forward at 40% while spinning at 20% custom_velocity(lambda robot, dt: (0.4, 0.0, 0.2)).until(after_seconds(3)) # Simple P heading controller — all values in -1.0..1.0 import math target_rad = math.pi / 2 def steer_to_heading(robot, dt): err = target_rad - robot.odometry.get_heading() omega = max(-1.0, min(1.0, err / math.pi)) # normalise to ±1 return 0.3, 0.0, omega custom_velocity(steer_to_heading).until(on_black(sensor))