From 9818d2fa75852d3b219404a56cb1a6a4f826f591 Mon Sep 17 00:00:00 2001 From: Laurens Valk Date: Thu, 25 Jan 2024 20:24:29 +0100 Subject: [PATCH] pybricks.robotics: Add Car class. --- CHANGELOG.md | 4 + doc/common/extensions/blockimg.py | 1 - doc/common/extensions/requirements-static.py | 3 +- ...ricks_blockCarDrive_car_drive_at_power.svg | 989 ++++++++++++++++++ ...ricks_blockCarDrive_car_drive_at_speed.svg | 989 ++++++++++++++++++ doc/main/blockimg/pybricks_blockCarSteer.svg | 989 ++++++++++++++++++ .../blockimg/pybricks_variables_set_car.svg | 989 ++++++++++++++++++ doc/main/robotics.rst | 41 +- examples/pup/robotics/car_remote.py | 40 + jedi/tests/test_complete_import.py | 2 +- src/pybricks/robotics.py | 61 ++ 11 files changed, 4100 insertions(+), 8 deletions(-) create mode 100644 doc/main/blockimg/pybricks_blockCarDrive_car_drive_at_power.svg create mode 100644 doc/main/blockimg/pybricks_blockCarDrive_car_drive_at_speed.svg create mode 100644 doc/main/blockimg/pybricks_blockCarSteer.svg create mode 100644 doc/main/blockimg/pybricks_variables_set_car.svg create mode 100644 examples/pup/robotics/car_remote.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a2caef5..286da2d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ ## Unreleased +## Added + +- Added `pybricks.robotics.Car` class. + ### Changed - Changed `pybricks.robotics.DriveBase` icon to two wheels instead of steering diff --git a/doc/common/extensions/blockimg.py b/doc/common/extensions/blockimg.py index 9cd63f4f..02152d35 100644 --- a/doc/common/extensions/blockimg.py +++ b/doc/common/extensions/blockimg.py @@ -1,4 +1,3 @@ -import os import xml.etree.ElementTree as ET from docutils.parsers.rst import directives diff --git a/doc/common/extensions/requirements-static.py b/doc/common/extensions/requirements-static.py index 3162a3ea..2d058c25 100644 --- a/doc/common/extensions/requirements-static.py +++ b/doc/common/extensions/requirements-static.py @@ -14,6 +14,7 @@ "pybricks-iodevices", "stm32-extra", "stm32-float", + "pybricks-frozen", } # Large feature set. @@ -31,12 +32,10 @@ class PybricksRequirementsStaticDirective(Directive): - required_arguments = 0 optional_arguments = 10 def run(self): - # Copy required resources to static # CC BY-SA 4.0 via https://stackoverflow.com/a/63728208 env = self.state.document.settings.env diff --git a/doc/main/blockimg/pybricks_blockCarDrive_car_drive_at_power.svg b/doc/main/blockimg/pybricks_blockCarDrive_car_drive_at_power.svg new file mode 100644 index 00000000..03c00b4d --- /dev/null +++ b/doc/main/blockimg/pybricks_blockCarDrive_car_drive_at_power.svg @@ -0,0 +1,989 @@ + +50%cardrive atpower \ No newline at end of file diff --git a/doc/main/blockimg/pybricks_blockCarDrive_car_drive_at_speed.svg b/doc/main/blockimg/pybricks_blockCarDrive_car_drive_at_speed.svg new file mode 100644 index 00000000..861ef410 --- /dev/null +++ b/doc/main/blockimg/pybricks_blockCarDrive_car_drive_at_speed.svg @@ -0,0 +1,989 @@ + +500°/scardrive atspeed \ No newline at end of file diff --git a/doc/main/blockimg/pybricks_blockCarSteer.svg b/doc/main/blockimg/pybricks_blockCarSteer.svg new file mode 100644 index 00000000..958536e4 --- /dev/null +++ b/doc/main/blockimg/pybricks_blockCarSteer.svg @@ -0,0 +1,989 @@ + +car0%steer by \ No newline at end of file diff --git a/doc/main/blockimg/pybricks_variables_set_car.svg b/doc/main/blockimg/pybricks_variables_set_car.svg new file mode 100644 index 00000000..dea02178 --- /dev/null +++ b/doc/main/blockimg/pybricks_variables_set_car.svg @@ -0,0 +1,989 @@ + +motormotorcarwith steeringand drive \ No newline at end of file diff --git a/doc/main/robotics.rst b/doc/main/robotics.rst index a14d78d6..57d38a1c 100644 --- a/doc/main/robotics.rst +++ b/doc/main/robotics.rst @@ -85,6 +85,8 @@ .. automethod:: pybricks.robotics.DriveBase.stalled + .. pybricks-requirements:: gyro + .. rubric:: Driving with the gyro .. blockimg:: pybricks_blockDriveBaseUseGyro @@ -175,16 +177,47 @@ The :meth:`done` and :meth:`stalled` methods have been moved. +.. pybricks-requirements:: pybricks-frozen + +.. blockimg:: pybricks_variables_set_car + +.. autoclass:: pybricks.robotics.Car + :no-members: + + .. blockimg:: pybricks_blockCarSteer + + .. automethod:: pybricks.robotics.Car.steer + + .. blockimg:: pybricks_blockCarDrive_car_drive_at_power -.. pybricks-requirements:: gyro + .. automethod:: pybricks.robotics.Car.drive_power + + .. blockimg:: pybricks_blockCarDrive_car_drive_at_speed + + .. automethod:: pybricks.robotics.Car.drive_speed Examples ------------------- -Driving straight and turning in place -********************************************** +Driving straight and turning in place with a drive base +******************************************************** -The following program shows the basics of driving and turning. +This program shows the basics of driving and turning. .. literalinclude:: ../../examples/pup/robotics/drivebase_basics.py + +Remote controlling a car with front wheel steering +************************************************** + +This program shows how you can drive a car with front wheel steering +using the :class:`remote control `. + +In this program, the ports match those of the `LEGO Technic 42099 Off-Roader +`_, but you can +use any other car with front wheel steering. If your vehicle has only one +drive motor, you can use a single motor instead of a tuple of the motors used +below. + +.. literalinclude:: + ../../examples/pup/robotics/car_remote.py diff --git a/examples/pup/robotics/car_remote.py b/examples/pup/robotics/car_remote.py new file mode 100644 index 00000000..9cb615ce --- /dev/null +++ b/examples/pup/robotics/car_remote.py @@ -0,0 +1,40 @@ +from pybricks.parameters import Direction, Port, Button +from pybricks.pupdevices import Motor, Remote +from pybricks.robotics import Car +from pybricks.tools import wait + +# Set up motors. +front = Motor(Port.A, Direction.COUNTERCLOCKWISE) +rear = Motor(Port.B, Direction.COUNTERCLOCKWISE) +steer = Motor(Port.C, Direction.CLOCKWISE) + +# Connect to the remote. +remote = Remote() + +# Set up the car. +car = Car(steer, [front, rear]) + +# The main program starts here. +while True: + # Read remote state. + pressed = remote.buttons.pressed() + + # Steer using the left pad. Steering is the percentage + # of the angle determined while initializing. + steering = 0 + if Button.LEFT_PLUS in pressed: + steering += 100 + elif Button.LEFT_MINUS in pressed: + steering -= 100 + car.steer(steering) + + # Drive using the right pad. + power = 0 + if Button.RIGHT_PLUS in pressed: + power += 100 + elif Button.RIGHT_MINUS in pressed: + power -= 100 + car.drive_power(power) + + # Wait briefly. + wait(10) diff --git a/jedi/tests/test_complete_import.py b/jedi/tests/test_complete_import.py index 55eeab5c..c599478c 100644 --- a/jedi/tests/test_complete_import.py +++ b/jedi/tests/test_complete_import.py @@ -148,7 +148,7 @@ def test_from_pybricks_pupdevices_import(): def test_from_pybricks_robotics_import(): code = "from pybricks.robotics import " completions: list[CompletionItem] = json.loads(complete(code, 1, len(code) + 1)) - assert [c["insertText"] for c in completions] == ["DriveBase"] + assert [c["insertText"] for c in completions] == ["DriveBase", "Car"] def test_from_pybricks_tools_import(): diff --git a/src/pybricks/robotics.py b/src/pybricks/robotics.py index e2f7fc6d..30271acf 100644 --- a/src/pybricks/robotics.py +++ b/src/pybricks/robotics.py @@ -241,6 +241,67 @@ def use_gyro(self, use_gyro: bool) -> None: """ +class Car: + """A vehicle with one steering motor, and one or more motors for driving. + + When you use this class, the steering motor will automatically find the + center position. This also determines which angle corresponds to 100% + steering. + """ + + def __init__(self, steering_motor: Motor, drive_motors: Motor | Tuple[Motor, ...]): + """Car(steering_motor, drive_motors) + + Arguments: + steering_motor (Motor): + The motor that steers the front wheels. + drive_motors (Motor): The motor that drives the wheels. Use a tuple + for multiple motors. + """ + + def steer(self, percentage: Number) -> None: + """steer(percentage) + + Steers the front wheels by a given amount. For 100% steering, it + steers right by the angle that was determined on initialization. + For -100% steering, it steers left and 0% means straight. + + Arguments: + steering (Number, %): Amount to steer the front wheels. + """ + + def drive_power(self, power: Number) -> None: + """drive_power(power) + + Drives the car at a given "power" level, as a percentage of the + battery voltage. Positive values drive forward, negative values drive + backward. + + For ``power`` values below 30%, the car will coast the wheels in order + to roll out smoothly instead of braking abruptly. + + This command is useful for remote control applications where you want + instant response to button presses or joystick movements. + + Arguments: + speed (Number, %): Speed of the car. + """ + + def drive_speed(self, speed: Number) -> None: + """drive_speed(speed) + + Drives the car at a given motor speed. Positive values drive forward, + negative values drive backward. + + This command is useful for more precise driving with gentle + acceleration and deceleration. This automatically increases the power + to maintain speed as you drive across obstacles. + + Arguments: + speed (Number, deg/s): Angular velocity of the drive motors. + """ + + # HACK: hide from jedi if TYPE_CHECKING: del Motor