mirror of
https://github.com/pybricks/pybricks-api.git
synced 2026-09-12 09:37:25 +00:00
414 lines
15 KiB
Python
414 lines
15 KiB
Python
# SPDX-License-Identifier: MIT
|
|
# Copyright (c) 2018-2023 The Pybricks Authors
|
|
|
|
"""Robotics module for the Pybricks API."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Tuple, Union, Optional, overload, TYPE_CHECKING
|
|
|
|
from . import _common
|
|
from .parameters import Stop
|
|
|
|
if TYPE_CHECKING:
|
|
from ._common import Motor, MaybeAwaitable
|
|
from .parameters import Number
|
|
|
|
|
|
class DriveBase:
|
|
"""A robotic vehicle with two powered wheels and an optional support
|
|
wheel or caster.
|
|
|
|
By specifying the dimensions of your robot, this class
|
|
makes it easy to drive a given distance in millimeters or turn by a given
|
|
number of degrees.
|
|
|
|
**Positive** distances, radii, or drive speeds mean
|
|
driving **forward**. **Negative** means **backward**.
|
|
|
|
**Positive** angles and turn rates mean turning **right**.
|
|
**Negative** means **left**. So when viewed from the top,
|
|
positive means clockwise and negative means counterclockwise.
|
|
|
|
See the `measuring`_ section for tips to measure and adjust the diameter
|
|
and axle track values.
|
|
"""
|
|
|
|
distance_control = _common.Control()
|
|
"""The traveled distance and drive speed are controlled by a PID
|
|
controller. You can use this attribute to change its settings.
|
|
See the :ref:`motor control <settings>` attribute for an overview of
|
|
available methods. The ``distance_control`` attribute has the same
|
|
functionality, but the settings apply to every millimeter driven by the
|
|
drive base, instead of degrees turned by one motor."""
|
|
|
|
heading_control = _common.Control()
|
|
"""The robot turn angle and turn rate are controlled by a PID
|
|
controller. You can use this attribute to change its settings.
|
|
See the :ref:`motor control <settings>` attribute for an overview of
|
|
available methods. The ``heading_control`` attribute has the same
|
|
functionality, but the settings apply to every degree of rotation of the
|
|
whole drive base (viewed from the top) instead of degrees turned by one
|
|
motor."""
|
|
|
|
def __init__(
|
|
self,
|
|
left_motor: Motor,
|
|
right_motor: Motor,
|
|
wheel_diameter: Number,
|
|
axle_track: Number,
|
|
):
|
|
"""DriveBase(left_motor, right_motor, wheel_diameter, axle_track)
|
|
|
|
Arguments:
|
|
left_motor (Motor):
|
|
The motor that drives the left wheel.
|
|
right_motor (Motor):
|
|
The motor that drives the right wheel.
|
|
wheel_diameter (Number, mm): Diameter of the wheels.
|
|
axle_track (Number, mm): Distance between the points where
|
|
both wheels touch the ground.
|
|
"""
|
|
|
|
def drive(self, speed: Number, turn_rate: Number) -> None:
|
|
"""drive(speed, turn_rate)
|
|
|
|
Starts driving at the specified speed and turn rate. Both values are
|
|
measured at the center point between the wheels of the robot.
|
|
|
|
Arguments:
|
|
speed (Number, mm/s): Speed of the robot.
|
|
turn_rate (Number, deg/s): Turn rate of the robot.
|
|
"""
|
|
|
|
def stop(self) -> None:
|
|
"""stop()
|
|
|
|
Stops the robot by letting the motors spin freely."""
|
|
|
|
def brake(self) -> None:
|
|
"""brake()
|
|
|
|
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
|
|
|
|
Gets the estimated driven distance.
|
|
|
|
Returns:
|
|
Driven distance since last reset.
|
|
"""
|
|
|
|
def angle(self) -> float:
|
|
"""angle() -> float: deg
|
|
|
|
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.
|
|
"""
|
|
|
|
def state(self) -> Tuple[int, int, int, int]:
|
|
"""state() -> Tuple[int, int, int, int]
|
|
|
|
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.
|
|
"""
|
|
|
|
def reset(self, distance: Number = 0, angle: Number = 0) -> None:
|
|
"""reset(distance=0, angle=0)
|
|
|
|
Resets the estimated driven distance and heading angle.
|
|
|
|
This also calls :meth:`.stop` to stop ongoing movements.
|
|
If your robot is controlled with :meth:`.use_gyro` set to ``True``,
|
|
calling this method will `also` set the gyro to the given angle.
|
|
|
|
Arguments:
|
|
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[Union[Number, Tuple[Number, Number]]] = None,
|
|
turn_rate: Optional[Number] = None,
|
|
turn_acceleration: Optional[Union[Number, Tuple[Number, Number]]] = None,
|
|
) -> None: ...
|
|
|
|
@overload
|
|
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.
|
|
|
|
If you give no arguments, this returns the current values as a tuple.
|
|
|
|
The initial values are automatically configured based on your wheel
|
|
diameter and axle track. They are selected such that your robot
|
|
drives at about 40% of its maximum speed.
|
|
|
|
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 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 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(
|
|
self, distance: Number, then: Stop = Stop.HOLD, wait: bool = True
|
|
) -> MaybeAwaitable:
|
|
"""straight(distance, then=Stop.HOLD, wait=True)
|
|
|
|
Drives straight for a given distance and then stops.
|
|
|
|
Arguments:
|
|
distance (Number, mm): Distance to travel
|
|
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.
|
|
"""
|
|
|
|
def turn(
|
|
self,
|
|
angle: Number,
|
|
then: Stop = Stop.HOLD,
|
|
wait: bool = True,
|
|
absolute: bool = False,
|
|
) -> MaybeAwaitable:
|
|
"""turn(angle, then=Stop.HOLD, wait=True, absolute=False)
|
|
|
|
Turns in place by a given angle and then stops.
|
|
|
|
Arguments:
|
|
angle (Number, deg): Angle of the turn.
|
|
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(
|
|
self,
|
|
radius: Number,
|
|
angle: Number = None,
|
|
distance: Number = None,
|
|
then: Stop = Stop.HOLD,
|
|
wait: bool = True,
|
|
) -> MaybeAwaitable:
|
|
"""arc(radius, angle=None, distance=None, then=Stop.HOLD, wait=True)
|
|
|
|
Drives an arc (a partial circle) with a given radius. You can specify
|
|
how far to drive using either an angle or a distance.
|
|
|
|
With a positive radius, the robot drives along a circle to its right.
|
|
With a negative radius, the robot drives along a circle to its left.
|
|
|
|
You can specify how far to travel along that circle as an angle
|
|
(degrees) or distance (mm). A positive value means driving forward
|
|
along the circle. Negative means driving in reverse.
|
|
|
|
Arguments:
|
|
radius (Number, mm): Radius of the circle.
|
|
angle (Number, deg): Angle to drive along the circle.
|
|
distance (Number, mm): Distance to drive along the circle,
|
|
measured at the center of the robot.
|
|
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.
|
|
Raises:
|
|
ValueError:
|
|
You must specify ``angle`` or ``distance``, but not both. The
|
|
radius cannot be zero. Use :meth:`.turn` for in-place turns.
|
|
"""
|
|
|
|
def curve(
|
|
self, radius: Number, angle: Number, then: Stop = Stop.HOLD, wait: bool = True
|
|
) -> MaybeAwaitable:
|
|
"""curve(radius, angle, then=Stop.HOLD, wait=True)
|
|
|
|
Drives an arc along a circle of a given radius, by a given angle.
|
|
|
|
Arguments:
|
|
radius (Number, mm): Radius of the circle.
|
|
angle (Number, deg): Angle along the circle.
|
|
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.
|
|
"""
|
|
|
|
def done(self) -> bool:
|
|
"""done() -> bool
|
|
|
|
Checks if an ongoing command or maneuver is done.
|
|
|
|
Returns:
|
|
``True`` if the command is done, ``False`` if not.
|
|
"""
|
|
|
|
def stalled(self) -> bool:
|
|
"""stalled() -> bool
|
|
|
|
Checks if the drive base is currently stalled.
|
|
|
|
It is stalled when it cannot reach the target speed or position, even
|
|
with the maximum actuation signal.
|
|
|
|
Returns:
|
|
``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)
|
|
|
|
Choose ``True`` to use the gyro sensor for turning and driving
|
|
straight. Choose ``False`` to rely only on the motor's built-in
|
|
rotation sensors.
|
|
|
|
This method will automatically call :meth:`.stop` to stop ongoing
|
|
movements.
|
|
|
|
Arguments:
|
|
use_gyro (bool): ``True`` to enable, ``False`` to disable.
|
|
"""
|
|
|
|
|
|
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,
|
|
steer_motor: Motor,
|
|
drive_motors: Motor | Tuple[Motor, ...],
|
|
torque_limit: Number = 100,
|
|
):
|
|
"""Car(steer_motor, drive_motors, torque_limit=100)
|
|
|
|
Arguments:
|
|
steer_motor (Motor):
|
|
The motor that steers the front wheels.
|
|
drive_motors (Motor): The motor that drives the wheels. Use a tuple
|
|
for multiple motors.
|
|
torque_limit (Number, %): The maximum torque limit used to find the
|
|
endpoints for the steering mechanism, as a percentage of the
|
|
maximum torque of the steering motor.
|
|
"""
|
|
|
|
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. Positive values drive forward,
|
|
negative values drive backward.
|
|
|
|
The ``power`` value is used to set the motor voltage as a percentage of
|
|
the battery voltage. Below 10%, 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
|
|
del Number
|
|
del MaybeAwaitable
|
|
del Stop
|