From acb5206349e39b6a8b92c3992e25a8806e140b47 Mon Sep 17 00:00:00 2001 From: Laurens Valk Date: Fri, 29 May 2026 20:06:56 +0200 Subject: [PATCH] pybricks.robotics: Update DriveBase. --- doc/main/robotics.rst | 19 +++----- jedi/tests/test_get_signature.py | 16 +++++-- src/pybricks/robotics.py | 79 ++++++++++++++++++++++++++------ 3 files changed, 83 insertions(+), 31 deletions(-) diff --git a/doc/main/robotics.rst b/doc/main/robotics.rst index 35af6cc..db1a0cf 100644 --- a/doc/main/robotics.rst +++ b/doc/main/robotics.rst @@ -27,25 +27,16 @@ .. automethod:: pybricks.robotics.DriveBase.turn - .. versionchanged:: 3.6 - - The ``curve()`` Python method will be replaced by the :meth:`.arc` - method. It can still make curves, but it uses different definitions - for drive and turn direction. Existing code with ``curve()`` continues - to work the same, but you should use :meth:`.arc` for new code. - If you use block code, you can pick a new block from the palette to - update your code. The old block will still work, but it displays a - warning icon to remind you to upgrade. The updated `curve` option uses - the direction definitions given below. The new `veer` option lets - you drive along a circle by a given distance, which is useful for - veering slightly in one direction. - .. blockimg:: pybricks_blockDriveBaseDrive2_drivebase_drive_arc_angle .. blockimg:: pybricks_blockDriveBaseDrive2_drivebase_drive_arc_distance .. automethod:: pybricks.robotics.DriveBase.arc + .. pybricks-requirements:: stm32-float + + .. automethod:: pybricks.robotics.DriveBase.move_by + .. blockimg:: pybricks_blockDriveBaseConfigure_drivebase_straight_speed .. blockimg:: pybricks_blockDriveBaseConfigure_drivebase_straight_acceleration @@ -80,6 +71,8 @@ .. blockimg:: pybricks_blockDriveBaseStop_hold + .. automethod:: pybricks.robotics.DriveBase.hold + .. rubric:: Measuring .. blockimg:: pybricks_blockDriveBaseMeasure_drivebase_get_distance diff --git a/jedi/tests/test_get_signature.py b/jedi/tests/test_get_signature.py index 994c66d..a3c3991 100644 --- a/jedi/tests/test_get_signature.py +++ b/jedi/tests/test_get_signature.py @@ -971,7 +971,12 @@ METHOD_PARAMS = [ "turn", [ ( - ["angle: Number", "then: Stop=Stop.HOLD", "wait: bool=True"], + [ + "angle: Number", + "then: Stop=Stop.HOLD", + "wait: bool=True", + "absolute: bool=False", + ], "MaybeAwaitable", ) ], @@ -1000,13 +1005,16 @@ METHOD_PARAMS = [ ( [ "straight_speed: Optional[Number]=None", - "straight_acceleration: Optional[Number]=None", + "straight_acceleration: Optional[Union[Number, Tuple[Number, Number]]]=None", "turn_rate: Optional[Number]=None", - "turn_acceleration: Optional[Number]=None", + "turn_acceleration: Optional[Union[Number, Tuple[Number, Number]]]=None", ], "None", ), - ([], "Tuple[int, int, int, int]"), + ( + [], + "Tuple[int, Union[int, Tuple[int, int]], int, Union[int, Tuple[int, int]]]", + ), ], ), pytest.param( diff --git a/src/pybricks/robotics.py b/src/pybricks/robotics.py index d18004b..22eada6 100644 --- a/src/pybricks/robotics.py +++ b/src/pybricks/robotics.py @@ -5,7 +5,7 @@ from __future__ import annotations -from typing import Tuple, Optional, overload, TYPE_CHECKING +from typing import Tuple, Union, Optional, overload, TYPE_CHECKING from . import _common from .parameters import Stop @@ -92,6 +92,12 @@ class DriveBase: Stops the robot by passively braking the motors. """ + def hold(self) -> None: + """hold() + + Stops the robot and actively holds it in place. + """ + def distance(self) -> int: """distance() -> int: mm @@ -106,6 +112,10 @@ class DriveBase: Gets the estimated rotation angle of the drive base. + When the gyro is used for this drive base, this gives the gyro angle. + Otherwise, it gives the estimated angle estimated from the motor + displacement. + Returns: Accumulated angle since last reset. """ @@ -115,6 +125,10 @@ class DriveBase: Gets the state of the robot. + As with the :meth:`.angle` methods, the reported angle and turn rate + are those of the gyro if the gyro is used. Otherwise they are + estimated from the motor displacement. + Returns: Tuple of distance, drive speed, angle, and turn rate of the robot. """ @@ -129,26 +143,29 @@ class DriveBase: calling this method will `also` set the gyro to the given angle. Arguments: - distance (Number, mm): Speed of the robot. - angle (Number, deg): Heading angle of the robot. + distance (Number, mm): New value of the driven distance. + angle (Number, deg): New heading angle of the robot. """ @overload def settings( self, straight_speed: Optional[Number] = None, - straight_acceleration: Optional[Number] = None, + straight_acceleration: Optional[Union[Number, Tuple[Number, Number]]] = None, turn_rate: Optional[Number] = None, - turn_acceleration: Optional[Number] = None, + turn_acceleration: Optional[Union[Number, Tuple[Number, Number]]] = None, ) -> None: ... @overload - def settings(self) -> Tuple[int, int, int, int]: ... + def settings( + self, + ) -> Tuple[int, Union[int, Tuple[int, int]], int, Union[int, Tuple[int, int]]]: ... def settings(self, *args): """ settings(straight_speed, straight_acceleration, turn_rate, turn_acceleration) settings() -> Tuple[int, int, int, int] + settings() -> Tuple[int, Tuple[int, int], int, Tuple[int, int]] Configures the drive base speed and acceleration. @@ -161,15 +178,22 @@ class DriveBase: The speed values given here do not apply to the :meth:`.drive` method, since you provide your own speed values as arguments in that method. + Speed and rate values are treated as absolute; negative values are + converted to positive automatically. + Arguments: straight_speed (Number, mm/s): Straight-line speed of the robot. - straight_acceleration (Number, mm/s²): Straight-line - acceleration and deceleration of the robot. Provide a tuple with - two values to set acceleration and deceleration separately. + straight_acceleration (Number or Tuple[Number, Number], mm/s²): + Straight-line acceleration and deceleration of the robot. + Provide a single value to use the same acceleration and + deceleration. Provide a tuple with two values to set them + separately. turn_rate (Number, deg/s): Turn rate of the robot. - turn_acceleration (Number, deg/s²): Angular acceleration and - deceleration of the robot. Provide a tuple with - two values to set acceleration and deceleration separately. + turn_acceleration (Number or Tuple[Number, Number], deg/s²): + Angular acceleration and deceleration of the robot. + Provide a single value to use the same acceleration and + deceleration. Provide a tuple with two values to set them + separately. """ def straight( @@ -187,9 +211,13 @@ class DriveBase: """ def turn( - self, angle: Number, then: Stop = Stop.HOLD, wait: bool = True + self, + angle: Number, + then: Stop = Stop.HOLD, + wait: bool = True, + absolute: bool = False, ) -> MaybeAwaitable: - """turn(angle, then=Stop.HOLD, wait=True) + """turn(angle, then=Stop.HOLD, wait=True, absolute=False) Turns in place by a given angle and then stops. @@ -198,6 +226,9 @@ class DriveBase: then (Stop): What to do after coming to a standstill. wait (bool): Wait for the maneuver to complete before continuing with the rest of the program. + absolute (bool): If ``False`` (default), the robot turns _by_ the + given angle relative to its current heading. If ``True``, + the robot turns to the given absolute heading angle. """ def arc( @@ -270,6 +301,26 @@ class DriveBase: ``True`` if the drive base is stalled, ``False`` if not. """ + def move_by(self, dx: Number, dy: Number, then: Stop = Stop.HOLD) -> MaybeAwaitable: + """move_by(dx, dy, then=Stop.HOLD) + + Moves the robot by an amount given as X-and-Y coordinates on the robot + drive area. The X-axis is what was forward when the program started. + The Y-axis is 90° left of that. You can reset this by resetting the heading. + + The robot first turns to the required heading and then drives the + straight-line distance. Because the heading target is absolute, the + result is independent of the robot's current heading. + + Arguments: + dx (Number, mm): X-distance on the drive area. + dy (Number, mm): Y-distance on the drive area. + then (Stop): What to do after coming to a standstill. + + Raises: + ValueError: If one of the distances is more than 30 m. + """ + def use_gyro(self, use_gyro: bool) -> None: """use_gyro(use_gyro)