Motors with Rotation Sensors

pupmotors

Figure 2 Powered Up motors with rotation sensors. The arrows indicate the default positive direction. See the hubs module for default directions of built-in motors.

class Motor(port, positive_direction=Direction.CLOCKWISE, gears=None, reset_angle=True)

Generic class to control motors with built-in rotation sensors.

Parameters
  • port (Port) – Port to which the motor is connected.

  • positive_direction (Direction) – Which direction the motor should turn when you give a positive speed value or angle.

  • gears (list) –

    List of gears linked to the motor.

    For example: [12, 36] represents a gear train with a 12-tooth and a 36-tooth gear. Use a list of lists for multiple gear trains, such as [[12, 36], [20, 16, 40]].

    When you specify a gear train, all motor commands and settings are automatically adjusted to account for the resulting gear ratio. The motor direction remains unchanged by this.

  • reset_angle (bool) – Choose True to reset the rotation sensor value to the absolute marker angle (between -180 and 179). Choose False to keep the current value, so your program knows where it left off last time.

Measuring

speed()

Gets the speed of the motor.

Returns

Motor speed.

Return type

rotational speed: deg/s

angle()

Gets the rotation angle of the motor.

Returns

Motor angle.

Return type

angle: deg

reset_angle(angle=None)

Sets the accumulated rotation angle of the motor to a desired value.

If you don’t specify an angle, the absolute angle will be used if your motor supports it.

Parameters

angle (angle: deg) – Value to which the angle should be reset.

Stopping

stop()

Stops the motor and lets it spin freely.

The motor gradually stops due to friction.

brake()

Passively brakes the motor.

The motor stops due to friction, plus the voltage that is generated while the motor is still moving.

hold()

Stops the motor and actively holds it at its current angle.

Action

run(speed)

Runs the motor at a constant speed.

The motor accelerates to the given speed and keeps running at this speed until you give a new command.

Parameters

speed (rotational speed: deg/s) – Speed of the motor.

run_time(speed, time, then=Stop.HOLD, wait=True)

Runs the motor at a constant speed for a given amount of time.

The motor accelerates to the given speed, keeps running at this speed, and then decelerates. The total maneuver lasts for exactly the given amount of time.

Parameters
  • speed (rotational speed: deg/s) – Speed of the motor.

  • time (time: ms) – Duration of the maneuver.

  • 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.

run_angle(speed, rotation_angle, then=Stop.HOLD, wait=True)

Runs the motor at a constant speed by a given angle.

Parameters
  • speed (rotational speed: deg/s) – Speed of the motor.

  • rotation_angle (angle: deg) – Angle by which the motor should rotate.

  • 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.

run_target(speed, target_angle, then=Stop.HOLD, wait=True)

Runs the motor at a constant speed towards a given target angle.

The direction of rotation is automatically selected based on the target angle. It does not matter if speed is positive or negative.

Parameters
  • speed (rotational speed: deg/s) – Speed of the motor.

  • target_angle (angle: deg) – Angle that the motor should rotate to.

  • then (Stop) – What to do after coming to a standstill.

  • wait (bool) – Wait for the motor to reach the target before continuing with the rest of the program.

track_target(target_angle)

Tracks a target angle. This is similar to run_target(), but the usual smooth acceleration is skipped: it will move to the target angle as fast as possible. This method is useful if you want to continuously change the target angle.

Parameters

target_angle (angle: deg) – Target angle that the motor should rotate to.

run_until_stalled(speed, then=Stop.COAST, duty_limit=None)

Runs the motor at a constant speed until it stalls.

Parameters
  • speed (rotational speed: deg/s) – Speed of the motor.

  • then (Stop) – What to do after coming to a standstill.

  • duty_limit (percentage: %) – Duty cycle limit during this command. This is useful to avoid applying the full motor torque to a geared or lever mechanism.

Returns

Angle at which the motor becomes stalled.

Return type

angle: deg

dc(duty)

Rotates the motor at a given duty cycle (also known as “power”).

This method lets you use a motor just like a simple DC motor.

Parameters

duty (percentage: %) – The duty cycle (-100.0 to 100).

Motor status

control.scale

Number of degrees that the motor turns to complete one degree at the output of the gear train. This is the gear ratio determined from the gears argument when initializing the motor.

control.done()

Checks if an ongoing command or maneuver is done.

Returns

True if the command is done, False if not.

Return type

bool

control.stalled()

Checks if the controller is currently stalled.

A controller is stalled when it cannot reach the target speed or position, even with the maximum actuation signal.

Returns

True if the controller is stalled, False if not.

Return type

bool

control.load()

Estimates the load based on the torque required to maintain the specified speed or angle.

When coasting, braking, or controlling the duty cycle manually, the load cannot be estimated in this way. Then this method returns zero.

Returns

The load torque. It returns 0 if control is not active.

Return type

torque: mNm

Motor settings

You can only change these settings while the controller is stopped. For example, you can change them at the start of your program. Alternatively, first call stop(), and then change the settings.

control.limits(speed, acceleration, duty, torque)

Configures the maximum speed, acceleration, duty, and torque.

If no arguments are given, this will return the current values.

Parameters
control.pid(kp, ki, kd, integral_range, integral_rate)

Gets or sets the PID values for position and speed control.

If no arguments are given, this will return the current values.

Parameters
  • kp (int) – Proportional position control constant. It is the feedback torque per degree of error: µNm/deg.

  • ki (int) – Integral position control constant. It is the feedback torque per accumulated degree of error: µNm/(deg s).

  • kd (int) – Derivative position (or proportional speed) control constant. It is the feedback torque per unit of speed: µNm/(deg/s).

  • integral_range (angle: deg or distance: mm) – Region around the target angle or distance, in which integral control errors are accumulated.

  • integral_rate (rotational speed: deg/s or speed: mm/s) – Maximum rate at which the error integral is allowed to grow.

control.target_tolerances(speed, position)

Gets or sets the tolerances that say when a maneuver is done.

If no arguments are given, this will return the current values.

Parameters
control.stall_tolerances(speed, time)

Gets or sets stalling tolerances.

If no arguments are given, this will return the current values.

Parameters
  • speed (rotational speed: deg/s or speed: mm/s) – If the controller cannot reach this speed for some time even with maximum actuation, it is stalled.

  • time (time: ms) – How long the controller has to be below this minimum speed before we say it is stalled.

Initialization Examples

Making the motor move back and forth

from pybricks.pupdevices import Motor
from pybricks.parameters import Port
from pybricks.tools import wait

# Initialize a motor on port A.
example_motor = Motor(Port.A)

# Make the motor run clockwise at 500 degrees per second.
example_motor.run(500)

# Wait for three seconds.
wait(3000)

# Make the motor run counterclockwise at 500 degrees per second.
example_motor.run(-500)

# Wait for three seconds.
wait(3000)

Initializing multiple motors

from pybricks.pupdevices import Motor
from pybricks.parameters import Port
from pybricks.tools import wait

# Initialize motors on port A and B.
track_motor = Motor(Port.A)
gripper_motor = Motor(Port.B)

# Make both motors run at 500 degrees per second.
track_motor.run(500)
gripper_motor.run(500)

# Wait for three seconds.
wait(3000)

Setting the positive direction as counterclockwise

from pybricks.pupdevices import Motor
from pybricks.parameters import Port, Direction
from pybricks.tools import wait

# Initialize a motor on port A with the positive direction as counterclockwise.
example_motor = Motor(Port.A, Direction.COUNTERCLOCKWISE)

# When we choose a positive speed value, the motor now goes counterclockwise.
example_motor.run(500)

# This is useful when your motor is mounted in reverse or upside down.
# By changing the positive direction, your script will be easier to read,
# because a positive value now makes your robot/mechanism go forward.

# Wait for three seconds.
wait(3000)

Using gears

from pybricks.pupdevices import Motor
from pybricks.parameters import Port, Direction
from pybricks.tools import wait

# Initialize a motor on port A with the positive direction as counterclockwise.
# Also specify one gear train with a 12-tooth and a 36-tooth gear. The 12-tooth
# gear is attached to the motor axle. The 36-tooth gear is at the output axle.
geared_motor = Motor(Port.A, Direction.COUNTERCLOCKWISE, [12, 36])

# Make the output axle run at 100 degrees per second. The motor speed
# is automatically increased to compensate for the gears.
geared_motor.run(100)

# Wait for three seconds.
wait(3000)

Measurement Examples

Measuring the angle and speed

from pybricks.pupdevices import Motor
from pybricks.parameters import Port
from pybricks.tools import wait

# Initialize a motor on port A.
example_motor = Motor(Port.A)

# Start moving at 300 degrees per second.
example_motor.run(300)

# Display the angle and speed 50 times.
for i in range(100):

    # Read the angle (degrees) and speed (degrees per second).
    angle = example_motor.angle()
    speed = example_motor.speed()

    # Print the values.
    print(angle, speed)

    # Wait some time so we can read what is displayed.
    wait(200)

Resetting the measured angle

from pybricks.pupdevices import Motor
from pybricks.parameters import Port

# Initialize a motor on port A.
example_motor = Motor(Port.A)

# Reset the angle to 0.
example_motor.reset_angle(0)

# Reset the angle to 1234.
example_motor.reset_angle(1234)

# Reset the angle to the absolute angle.
# This is only supported on motors that have
# an absolute encoder. For other motors, this
# will raise an error.
example_motor.reset_angle()

Movement Examples

Basic usage of all run methods

from pybricks.pupdevices import Motor
from pybricks.parameters import Port
from pybricks.tools import wait

# Initialize a motor on port A.
example_motor = Motor(Port.A)

# Run at 500 deg/s and then stop by coasting.
print("Demo of run")
example_motor.run(500)
wait(1500)
example_motor.stop()
wait(1500)

# Run at 70% duty cycle ("power") and then stop by coasting.
print("Demo of dc")
example_motor.dc(50)
wait(1500)
example_motor.stop()
wait(1500)

# Run at 500 deg/s for two seconds.
print("Demo of run_time")
example_motor.run_time(500, 2000)
wait(1500)

# Run at 500 deg/s for 90 degrees.
print("Demo of run_angle")
example_motor.run_angle(500, 90)
wait(1500)

# Run at 500 deg/s back to the 0 angle
print("Demo of run_target to 0")
example_motor.run_target(500, 0)
wait(1500)

# Run at 500 deg/s back to the -90 angle
print("Demo of run_target to -90")
example_motor.run_target(500, -90)
wait(1500)

# Run at 500 deg/s until the motor stalls
print("Demo of run_until_stalled")
example_motor.run_until_stalled(500)
print("Done")
wait(1500)

Stopping ongoing movements in different ways

from pybricks.pupdevices import Motor
from pybricks.parameters import Port
from pybricks.tools import wait

# Initialize a motor on port A.
example_motor = Motor(Port.A)

# Run at 500 deg/s and then stop by coasting.
example_motor.run(500)
wait(1500)
example_motor.stop()
wait(1500)

# Run at 500 deg/s and then stop by braking.
example_motor.run(500)
wait(1500)
example_motor.brake()
wait(1500)

# Run at 500 deg/s and then stop by holding.
example_motor.run(500)
wait(1500)
example_motor.hold()
wait(1500)

# Run at 500 deg/s and then stop by running at 0 speed.
example_motor.run(500)
wait(1500)
example_motor.run(0)
wait(1500)

Using the then argument to change how a run command stops

from pybricks.pupdevices import Motor
from pybricks.parameters import Port, Stop
from pybricks.tools import wait

# Initialize a motor on port A.
example_motor = Motor(Port.A)

# By default, the motor holds the position. It keeps
# correcting the angle if you move it.
example_motor.run_angle(500, 360)
wait(1000)

# This does exactly the same as above.
example_motor.run_angle(500, 360, then=Stop.HOLD)
wait(1000)

# You can also brake. This applies some resistance
# but the motor does not move back if you move it.
example_motor.run_angle(500, 360, then=Stop.BRAKE)
wait(1000)

# This makes the motor coast freely after it stops.
example_motor.run_angle(500, 360, then=Stop.COAST)
wait(1000)

Stall Examples

Running a motor until a mechanical endpoint

from pybricks.pupdevices import Motor
from pybricks.parameters import Port

# Initialize a motor on port A.
example_motor = Motor(Port.A)

# We'll use a speed of 200 deg/s in all our commands.
speed = 200

# Run the motor in reverse until it hits a mechanical stop.
# The duty_limit=30 setting means that it will apply only 30%
# of the maximum torque against the mechanical stop. This way,
# you don't push against it with too much force.
example_motor.run_until_stalled(-speed, duty_limit=30)

# Reset the angle to 0. Now whenever the angle is 0, you know
# that it has reached the mechanical endpoint.
example_motor.reset_angle(0)

# Now make the motor go back and forth in a loop.
# This will now work the same regardless of the
# initial motor angle, because we always start
# from the mechanical endpoint.
for count in range(10):
    example_motor.run_target(speed, 180)
    example_motor.run_target(speed, 90)

Centering a steering mechanism

from pybricks.pupdevices import Motor
from pybricks.parameters import Port

# Initialize a motor on port A.
example_motor = Motor(Port.A)

# We'll use a speed of 200 deg/s in all our commands.
speed = 200

# Run the motor in reverse until it hits a mechanical stop.
# The duty_limit=30 setting means that it will apply only 30%
# of the maximum torque against the mechanical stop. This way,
# you don't push against it with too much force.
example_motor.run_until_stalled(-speed, duty_limit=30)

# Reset the angle to 0. Now whenever the angle is 0, you know
# that it has reached the mechanical endpoint.
example_motor.reset_angle(0)

# Now make the motor go back and forth in a loop.
# This will now work the same regardless of the
# initial motor angle, because we always start
# from the mechanical endpoint.
for count in range(10):
    example_motor.run_target(speed, 180)
    example_motor.run_target(speed, 90)

Parallel Movement Examples

Using the wait argument to run motors in parallel

from pybricks.pupdevices import Motor
from pybricks.parameters import Port

# Initialize motors on port A and B.
track_motor = Motor(Port.A)
gripper_motor = Motor(Port.B)

# Make the track motor start moving,
# but don't wait for it to finish.
track_motor.run_angle(500, 360, wait=False)

# Now make the gripper motor rotate. This
# means they move at the same time.
gripper_motor.run_angle(200, 720)

Waiting for two parallel actions to complete

from pybricks.pupdevices import Motor
from pybricks.parameters import Port
from pybricks.tools import wait

# Initialize motors on port A and B.
track_motor = Motor(Port.A)
gripper_motor = Motor(Port.B)

# Make both motors perform an action with wait=False
track_motor.run_angle(500, 360, wait=False)
gripper_motor.run_angle(200, 720, wait=False)

# While one or both of the motors are not done yet,
# do something else. In this example, just wait.
while not track_motor.control.done() or not gripper_motor.control.done():
    wait(10)

print("Both motors are done!")