step.motion.characterize_drive ============================== .. py:module:: step.motion.characterize_drive .. autoapi-nested-parse:: Characterize drive limits by commanding raw motor power and measuring response. Bypasses ALL velocity control (library PID and firmware BEMF PID) to discover the robot's true physical limits: max velocity, acceleration, and deceleration for each axis. Motors are driven at 100% PWM via the kinematics layer's ``applyPowerCommand``, which uses inverse kinematics to compute per-wheel direction signs while commanding raw open-loop power. Results are persisted to raccoon.project.yml under robot.motion_pid and applied in-memory. Classes ------- .. autoapisummary:: step.motion.characterize_drive.AxisResult step.motion.characterize_drive.CharacterizeDrive Module Contents --------------- .. py:class:: AxisResult Measured limits for a single axis. .. py:attribute:: max_velocity :type: float :value: 0.0 .. py:attribute:: acceleration :type: float :value: 0.0 .. py:attribute:: deceleration :type: float :value: 0.0 .. py:class:: CharacterizeDrive(axes: list[str] = None, trials: int = 3, power_percent: int = 100, accel_timeout: float = 3.0, decel_timeout: float = 3.0, persist: bool = True) Bases: :py:obj:`step.Step` Characterize the robot's physical drive limits at full motor power. Drives each axis at 100 %% raw PWM (open-loop ``setSpeed``) via the kinematics layer, completely bypassing both the library velocity PID and the firmware BEMF PID. This reveals the true hardware ceiling for each degree of freedom. For each axis the step runs multiple independent trials consisting of two phases: 1. **Acceleration phase** -- commands 100 %% power and records odometry at ~100 Hz until a velocity plateau is detected or the timeout expires. Max velocity and acceleration are extracted with 10 %%--90 %% rise-time analysis. 2. **Deceleration phase** -- ramps back to full speed, then cuts power and records the coast-down. Deceleration is computed from 90 %%--10 %% fall-time analysis. The median of all valid trials for each metric is taken as the final result. Measured values are applied to the in-memory ``motion_pid_config`` immediately and, if ``persist`` is enabled, written to ``raccoon.project.yml`` under ``robot.motion_pid``. This step is intended for initial robot setup and should be run on a flat surface with enough room for the robot to accelerate to full speed. :param axes: Which axes to characterize. Options are ``"forward"``, ``"lateral"``, and ``"angular"``. Default ``["forward"]``. :param trials: Number of trials per axis. The median is used for robustness against outliers. Default 3. :param power_percent: Motor power percentage (1--100). Default 100 for true maximum characterization. :param accel_timeout: Maximum time in seconds to wait for the acceleration phase before giving up. Default 3.0. :param decel_timeout: Maximum time in seconds to record the deceleration (coast-down) phase. Default 3.0. :param persist: If ``True``, write the measured limits to ``raccoon.project.yml`` under ``robot.motion_pid``. Default ``True``. Example:: from raccoon.step.motion import characterize_drive # Characterize forward axis at full power characterize_drive() # Characterize forward and angular axes with 5 trials each characterize_drive( axes=["forward", "angular"], trials=5, ) # Characterize at 80% power without saving to disk (dry run) characterize_drive( axes=["forward", "lateral", "angular"], power_percent=80, persist=False, ) .. py:attribute:: axes :value: None .. py:attribute:: trials .. py:attribute:: power_percent .. py:attribute:: accel_timeout :value: 3.0 .. py:attribute:: decel_timeout :value: 3.0 .. py:attribute:: persist :value: True .. py:attribute:: results :type: dict[str, AxisResult]