Lesson 5 of 8Robotics with LEGO SPIKE
Robotics with LEGO SPIKE · Lesson 5 · 25 minutes
Buttons and gyro sensor
What you'll learn
- Wait for a button press on the hub
- Measure the heading with the gyro sensor
- Calculate the shortest turn angle with modulo
The code in this lesson comes from the book “Python for Robots”, chapter 2.3, and runs unchanged in the simulator. German edition on Amazon ↗
The hub is listening
The SPIKE hub has buttons, and your program can react to them. The centre button is normally the stop button. So your code may use it itself, you move the stop function to a combination of buttons:
from pybricks.hubs import PrimeHub
from pybricks.parameters import Button, Color
from pybricks.tools import wait
hub = PrimeHub()
# The center button is normally the STOP button for the entire program.
# We free it up here so we can check it ourselves in the code -
# from now on, stopping requires the Center AND Bluetooth buttons together.
hub.system.set_stop_button((Button.CENTER, Button.BLUETOOTH))
print("Press the center button to start ...")
# As long as the center button is NOT pressed, we wait
while Button.CENTER not in hub.buttons.pressed():
wait(50) # short pause so the loop doesn't hog the processor
hub.light.on(Color.GREEN)
print("Let's go!")Press Run and then the centre button on the virtual hub on the right.
The gyro sensor
The hub contains a gyro sensor. It notices how far the robot has turned. hub.imu.heading() returns the angle in degrees:
from pybricks.hubs import PrimeHub
from pybricks.tools import wait
hub = PrimeHub()
# Reset the heading to 0 - afterwards it keeps counting (even beyond 360)
hub.imu.reset_heading(0)
for _ in range(10):
angle = hub.imu.heading() # current heading in degrees
print(f"Current heading: {angle} degrees")
wait(500)The robot is standing still here, so the angle stays at 0. In the next example it turns – and then it gets interesting.
The gyro doesn’t know 360 degrees
The angle just keeps counting: after one and a half turns it says 540, not 180. With the modulo operator % you work out the shortest way back:
from pybricks.hubs import PrimeHub
from pybricks.pupdevices import Motor
from pybricks.parameters import Port, Direction
from pybricks.robotics import DriveBase
hub = PrimeHub()
left_motor = Motor(Port.A, positive_direction=Direction.COUNTERCLOCKWISE)
right_motor = Motor(Port.B, positive_direction=Direction.CLOCKWISE)
drive_base = DriveBase(left_motor, right_motor, wheel_diameter=56, axle_track=114)
def shortest_turn_angle(current, target):
difference = (target - current) % 360
if difference > 180:
difference = difference - 360
return difference
hub.imu.reset_heading(0) # starting direction = 0 degrees
drive_base.turn(400) # the robot turns more than a full circle
# ... here your program would do something else ...
# Back to the starting direction - by the shortest way:
back = shortest_turn_angle(hub.imu.heading(), 0)
drive_base.turn(back)(target - current) % 360 always gives a value between 0 and 359. If it’s bigger than 180, it’s shorter to turn the other way – that’s why we subtract 360.
Remember: Never compare angles with
==. The gyro often gives decimals and almost never hits a value exactly. Check a range instead, e.g.abs(difference) < 2.
Your task
Your turn
The robot turns away with drive_base.turn(250). Then bring it back to its starting direction by the shortest way. Use hub.imu.heading() and a function shortest_turn_angle(current, target). Tip: from 250 degrees back to 0 it's shorter to turn another 110 degrees than to turn 250 degrees back.
from pybricks.hubs import PrimeHub
from pybricks.pupdevices import Motor
from pybricks.parameters import Port, Direction
from pybricks.robotics import DriveBase
hub = PrimeHub()
left_motor = Motor(Port.A, positive_direction=Direction.COUNTERCLOCKWISE)
right_motor = Motor(Port.B, positive_direction=Direction.CLOCKWISE)
drive_base = DriveBase(left_motor, right_motor, wheel_diameter=56, axle_track=114)
def shortest_turn_angle(current, target):
# calculate the shortest angle (between -180 and 180)
return 0
hub.imu.reset_heading(0)
drive_base.turn(250)
# back to the starting direction:- Something to think about
At the end read
hub.imu.heading()and turn byshortest_turn_angle(hub.imu.heading(), 0). - Which command helps?
The way back should be +110 degrees, not -250. Calculate (target - current)
%360 and subtract 360 if the result is over 180. - Gap code – fill in the ___
from pybricks.hubs import PrimeHub from pybricks.pupdevices import Motor from pybricks.parameters import Port, Direction from pybricks.robotics import DriveBase hub = PrimeHub() left_motor = Motor(Port.A, positive_direction=Direction.COUNTERCLOCKWISE) right_motor = Motor(Port.B, positive_direction=Direction.CLOCKWISE) drive_base = DriveBase(left_motor, right_motor, wheel_diameter=56, axle_track=114) def shortest_turn_angle(current, target): difference = ___ if ___: difference = ___ return ___ hub.imu.reset_heading(0) drive_base.turn(250) # back to the starting direction: back = ___ drive_base.turn(___)
Solve it without hints to earn a ⭐ on your progress page.
Show solution🔒 Unlocks after all hints or 2 checks
Try it yourself first – mistakes teach you the most. Here is one possible solution:
from pybricks.hubs import PrimeHub
from pybricks.pupdevices import Motor
from pybricks.parameters import Port, Direction
from pybricks.robotics import DriveBase
hub = PrimeHub()
left_motor = Motor(Port.A, positive_direction=Direction.COUNTERCLOCKWISE)
right_motor = Motor(Port.B, positive_direction=Direction.CLOCKWISE)
drive_base = DriveBase(left_motor, right_motor, wheel_diameter=56, axle_track=114)
def shortest_turn_angle(current, target):
difference = (target - current) % 360
if difference > 180:
difference = difference - 360
return difference
hub.imu.reset_heading(0)
drive_base.turn(250)
# back to the starting direction:
back = shortest_turn_angle(hub.imu.heading(), 0)
drive_base.turn(back)