Wikijunior:Raspberry Pi/Raspberry Pi Motor Control

A tutorial by Mark Cantrill @AstroDesignsLtd
24th June, 2017
cotswoldjam.org

In this tutorial we're going to look at what we need to do control a motor with the Raspberry PI, how a motor driver works and we're going to look at using the PiZ-Moto motor driver for the Raspberry Pi to control two motors. We'll also look at how we might program a small two-wheeled robot. This worksheet focuses on the use of the PiZ-Moto motor driver board.

Connecting the motor(s)

edit

There are two connectors on the PiZ-Moto board, one is for Motor "A" and one is for Motor "B". The connectors are screw terminals, simply push the wires of the motor into the terminals and do up the screw in the top of the connector 'finger-tight'. The connector on the left is for Motor "A", the connector on the right is for Motor "B".

For this tutorial we’re only using one motor but the second motor connector has a couple of LEDs wired into it to show changes in direction and also changes in motor speed through the brightness of the LED. The LEDs are connector to Motor "A" connector, the motor is connected to the Motor "B" connector.

Connecting the power supply

edit

The PiZ-Moto is designed to run off a 6V 4-cell AA battery pack. We’ve included one of these and the wires to connect it with the PiZ-Moto in this tutorial.

But for the purpose of this tutorial, to save us from buying lots of batteries, we've temporarily connected a wire from the Raspberry Pi's 5V pin to the VIN (+) connection on the PiZ-Moto which enables us to run this tutorial off of a 5V USB power supply plugged into the Raspberry Pi.

This is ok for this tutorial where we're only using one small motor like this but we highly recommend that you cut this wire off and power the PiZ-Moto from the battery pack.

Also please note that when powering the PiZ-Moto from the battery pack, the 5V regulator on the PiZ-Moto will provide the Raspberry Pi with 5V. There is no need to connect a USB power supply to your Raspberry Pi when using the PiZ-Moto, infact if you did do this then you could damage the power supply.

Writing the software (using Python)

edit

1. Import the PiZ-Moto package

edit
import piz_moto

2. Understand the functions that you'll need to use

edit
piz_moto.MotorA(speed,duration)
  • Where, speed is: -100 to +100
  • Max speed = 100 or -100
  • Use positive speeds for forwards (e.g. 100)
  • Use negative speeds for backwards (e.g. -100)
  • Use smaller numbers to slow the motor down (e.g. 50 for half speed)
  • Use 0 to make the motor stop
  • And duration is: the length of time in seconds that you want the motor to run for.
    • e.g. 20 will make the motor run for 20 seconds.
    • e.g. 0.5 will make the motor run for half a second
    • If time = 0 then the motor will run until you tell it to stop by setting the speed to 0.

3. Now let’s make the motors do something…

edit

Motor A forwards for 10 seconds

piz-moto.MotorA(100, 10)

Motor B backwards for 10 seconds

Piz_moto.MotorB(-100,10)

4. Task 1: Write a program to control a robot…

edit

Pretend you have two motors on a two wheeled robot. Write a program to make it go forwards for 5 seconds then backwards for 5 seconds.

5. Task 2: Make your robot turn…

edit

Add a routine to your last program so that your robot turns left (or right) for 1 second before it changes direction from forwards to backwards.

6. Task 3: Make your robot accelerate…

edit

Modify your program so that instead of going forwards at full speed, it increases the speed over a period of 5 seconds.

Other useful information on the PiZ-Moto

edit

Full instructions for the assembly of the PiZ-Moto (if you have a kit) are on GitHub, together with some examples. We've already cloned the GitHub piz_moto repository onto the SDCard supplied with the tutorial so you're ready to go! But please keep an eye out for updates.

How to clone piz-moto repository onto your Raspberry Pi if it’s not there. Or if you just want to put a fresh copy in a new folder.

To clone piz_moto into your home folder (/home/pi) run the following command. This will create a folder called piz-moto that contains the simple piz_moto package, instructions and examples.

cd /home/pi
git clone https://github.com/astro-designs/piz-moto.git

If you simply want to check for updates, just navigate into the piz-moto folder and run git pull:

cd /home/pi/piz-moto
git pull

For up-to-date PiZ Moto code, from the Terminal, do:

mkdir -p ~/python/
cd ~/python
git clone https://github.com/astro-designs/piz-moto.git
mv piz-moto-master motors

Please note that for the purpose of running this tutorial, the SDCard has been prepared with the piz_moto files cloned to the following folder. You can continue to use it in this folder and run the git pull function to update the files as long as you are inside this folder: /home/pi/python/motors/piz-moto

The instructions found inside the piz-moto folder are worth a read as they explain all about the PiZ-Moto. For one thing, it's not just a motor driver… The PiZ-Moto includes simple interfaces to allow you to connect an ultrasonic range sensor to and infra-red photo-sensor.

It also includes a 5V regulator that you can use to power the Raspberry Pi. Plus there are two general purpose outputs that include an on-board series 100-Ohm resistor so you can connect a couple of LEDs direct to these pins.

Have fun with your PiZ-Moto motor driver board for the Raspberry Pi!

Files

edit

__init__.py

edit
import RPi.GPIO as GPIO # Import the GPIO Library
import time


from .motors import Motor1
from .motors import Motor2
from .motors import Stop
from .motors import Forwards
from .motors import Backwards
from .motors import SpinLeft
from .motors import SpinRight
from .motors import FLeft
from .motors import FRight
from .motors import BLeft
from .motors import BRight

__all__ = ['Stop', 'Forwards', 'Backwards', 'SpinLeft', 'SpinRight', 'FLeft', 'FRight', 'BLeft', 'BRight']

global pinMotorAForwards, pinMotorABackwards

# Set variables for the GPIO motor pins
pinMotorAForwards = 10
pinMotorABackwards = 9
pinMotorBForwards = 7
pinMotorBBackwards = 8

# Set variables for the line detector GPIO pin
pinLineFollower = 25

# Define GPIO pins to use on the Pi
pinTrigger = 17
pinEcho = 18

# Set variable for the LED pin
pinLED1 = 5
pinLED2 = 6

# How many times to turn the pin on and off each second
Frequency = 50

# How long the pin stays on each cycle, as a percent
DCA = 100
DCB = 100

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

GPIO.setup(pinMotorAForwards, GPIO.OUT)
GPIO.setup(pinMotorABackwards, GPIO.OUT)
GPIO.setup(pinMotorBForwards, GPIO.OUT)
GPIO.setup(pinMotorBBackwards, GPIO.OUT)

pwmMotorAForwards = GPIO.PWM(pinMotorAForwards, Frequency)
pwmMotorABackwards = GPIO.PWM(pinMotorABackwards, Frequency)
pwmMotorBForwards = GPIO.PWM(pinMotorBForwards, Frequency)
pwmMotorBBackwards = GPIO.PWM(pinMotorBBackwards, Frequency)

pwmMotorAForwards.start(0)
pwmMotorABackwards.start(0)
pwmMotorBForwards.start(0)
pwmMotorBBackwards.start(0)

def Motor1(SpeedAndDirection, duration=0):
	motors.Motor1(SpeedAndDirection, pwmMotorAForwards, pwmMotorABackwards)
	if duration > 0:
		time.sleep(duration)
		motors.Motor1(0, pwmMotorAForwards, pwmMotorABackwards)
	
def Motor2(SpeedAndDirection, duration=0):
	motors.Motor2(SpeedAndDirection, pwmMotorBForwards, pwmMotorBBackwards)
	if duration > 0:
		time.sleep(duration)
		motors.Motor2(0, pwmMotorBForwards, pwmMotorBBackwards)

def Stop():
	motors.Stop(pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards)

def Backwards(Speed, duration):
	if Speed > 1: Speed = 1
	elif Speed < 0: Speed = 0
	motors.Backwards(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, Speed)
	if duration > 0:
		time.sleep(duration)
		motors.Backwards(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, 0)

def Forwards(Speed, duration):
	if Speed > 1: Speed = 1
	elif Speed < 0: Speed = 0
	motors.Forwards(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, Speed)
	if duration > 0:
		time.sleep(duration)
		motors.Forwards(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, 0)

	
def SRight(Speed, duration):
	if Speed > 1: Speed = 1
	elif Speed < 0: Speed = 0
	motors.SpinRight(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, Speed)
	if duration > 0:
		time.sleep(duration)
		motors.SpinRight(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, 0)

def BLeft(Speed, duration):
	if Speed > 1: Speed = 1
	elif Speed < 0: Speed = 0
	motors.BLeft(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, Speed)
	if duration > 0:
		time.sleep(duration)
		motors.BLeft(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, 0)

def FLeft(Speed, duration):
	if Speed > 1: Speed = 1
	elif Speed < 0: Speed = 0
	motors.FLeft(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, Speed)
	if duration > 0:
		time.sleep(duration)
		motors.FLeft(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, 0)

def SLeft(Speed, duration):
	if Speed > 1: Speed = 1
	elif Speed < 0: Speed = 0
	motors.SpinLeft(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, Speed)
	if duration > 0:
		time.sleep(duration)
		motors.SpinLeft(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, 0)

def BRight(Speed, duration):
	if Speed > 1: Speed = 1
	elif Speed < 0: Speed = 0
	motors.BRight(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, Speed)
	if duration > 0:
		time.sleep(duration)
		motors.BRight(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, 0)

def FRight(Speed, duration):
	if Speed > 1: Speed = 1
	elif Speed < 0: Speed = 0
	motors.FRight(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, Speed)
	if duration > 0:
		time.sleep(duration)
		motors.FRight(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, 0)

motor.py

edit
#!/usr/bin/env python2.7
# PiZ-Moto Motor Control Functions

# First some basic motor control functions to control each motor separately
# SpeedAndDirection can be a number between +100 & -100
# +100 or any value greater than zero spins the motor forwards
# -100 or any value less than zero spins the motor backwards
# 100 or -100 (100% duty-cycle) spins the motor at full speed
# 50 or -50 (50% duty-cycle) spins the motor at half speed

#Motor 1
def Motor1(SpeedAndDirection, pwmMotorAForwards, pwmMotorABackwards):

	if SpeedAndDirection > 100:
		SpeedAndDirection = 100
		
	if SpeedAndDirection < -100:
		SpeedAndDirection = -100
		
	if(SpeedAndDirection < 0):
		SpeedAndDirection = SpeedAndDirection * -1
		pwmMotorAForwards.ChangeDutyCycle(0)
		pwmMotorABackwards.ChangeDutyCycle(SpeedAndDirection)
	else: # Forwards...
		pwmMotorAForwards.ChangeDutyCycle(SpeedAndDirection)
		pwmMotorABackwards.ChangeDutyCycle(0)
	
#Motor 2
def Motor2(SpeedAndDirection, pwmMotorBForwards, pwmMotorBBackwards):

	if SpeedAndDirection > 100:
		SpeedAndDirection = 100
		
	if SpeedAndDirection < -100:
		SpeedAndDirection = -100
		
	if(SpeedAndDirection < 0): # Backwards...
		SpeedAndDirection = SpeedAndDirection * -1
		pwmMotorBForwards.ChangeDutyCycle(0)
		pwmMotorBBackwards.ChangeDutyCycle(SpeedAndDirection)
	else: # Forwards...
		pwmMotorBForwards.ChangeDutyCycle(SpeedAndDirection)
		pwmMotorBBackwards.ChangeDutyCycle(0)



# Next, some functions to control both motors for driving a two motor, two-wheeled robot...
		
# Function to turn all motors off
def Stop(pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards):

	#print("Stop")
	pwmMotorAForwards.ChangeDutyCycle(0)
	pwmMotorABackwards.ChangeDutyCycle(0)
	pwmMotorBForwards.ChangeDutyCycle(0)
	pwmMotorBBackwards.ChangeDutyCycle(0)
	
# Turn both motors backwards
def Backwards(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, Speed):

	#print("Backwards")
	pwmMotorAForwards.ChangeDutyCycle(DCA * Speed)
	pwmMotorABackwards.ChangeDutyCycle(0)
	pwmMotorBForwards.ChangeDutyCycle(DCB * Speed)
	pwmMotorBBackwards.ChangeDutyCycle(0)

# Turn both motors forwards
def Forwards(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, Speed):
	
	#print("Forwards")
	pwmMotorAForwards.ChangeDutyCycle(0)
	pwmMotorABackwards.ChangeDutyCycle(DCA * Speed)
	pwmMotorBForwards.ChangeDutyCycle(0)
	pwmMotorBBackwards.ChangeDutyCycle(DCB * Speed)
	
# Spin Right
def SpinRight(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, Speed):
	
	#print("Spin Right")
	pwmMotorAForwards.ChangeDutyCycle(0)
	pwmMotorABackwards.ChangeDutyCycle(DCA * Speed)
	pwmMotorBForwards.ChangeDutyCycle(DCB * Speed)
	pwmMotorBBackwards.ChangeDutyCycle(0)
	
def BLeft(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, Speed):
	
	#print("Back Left")
	pwmMotorAForwards.ChangeDutyCycle(DCA * Speed * 0.5)
	pwmMotorABackwards.ChangeDutyCycle(0)
	pwmMotorBForwards.ChangeDutyCycle(DCB * Speed)
	pwmMotorBBackwards.ChangeDutyCycle(0)
	
def FLeft(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, Speed):
	
	#print("Forwards Left")
	pwmMotorAForwards.ChangeDutyCycle(0)
	pwmMotorABackwards.ChangeDutyCycle(DCA * Speed * 0.5)
	pwmMotorBForwards.ChangeDutyCycle(0)
	pwmMotorBBackwards.ChangeDutyCycle(DCB * Speed)
	
# Spin left
def SpinLeft(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, Speed):
	
	#print("Spin Left")
	pwmMotorAForwards.ChangeDutyCycle(DCA * Speed)
	pwmMotorABackwards.ChangeDutyCycle(0)
	pwmMotorBForwards.ChangeDutyCycle(0)
	pwmMotorBBackwards.ChangeDutyCycle(DCB * Speed)
	
def BRight(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, Speed):
	
	#print("Back Right")
	pwmMotorAForwards.ChangeDutyCycle(DCA * Speed)
	pwmMotorABackwards.ChangeDutyCycle(0)
	pwmMotorBForwards.ChangeDutyCycle(DCB * Speed * 0.5)
	pwmMotorBBackwards.ChangeDutyCycle(0)
	
def FRight(DCA, DCB, pwmMotorAForwards, pwmMotorABackwards, pwmMotorBForwards, pwmMotorBBackwards, Speed):
	
	#print("Forwards Right")
	pwmMotorAForwards.ChangeDutyCycle(0)
	pwmMotorABackwards.ChangeDutyCycle(DCA * Speed)
	pwmMotorBForwards.ChangeDutyCycle(0)
	pwmMotorBBackwards.ChangeDutyCycle(DCB * Speed * 0.5)

task1.py

edit
#!/usr/bin/env python2.7
# PiZ-Moto tutorial task #1
# A program to make a two wheeled robot travel forwards for 5 seconds
# and then travel backwards for 5 seconds

# Import the piz-moto package
import piz_moto
import time


# Move forwards...
# Note that one motor must spin in the opposite direction to the other
# because it's on the opposite side of the robot
print("First we're moving forwards...")
piz_moto.Motor1(100)
piz_moto.Motor2(-100)

# wait 5 seconds
time.sleep(5)

# Move backwards...
# Note that one motor must spin in the opposite direction to the other
# because it's on the opposite side of the robot
print("Now we're moving backwards...")
piz_moto.Motor1(-100)
piz_moto.Motor2(100)

# wait 5 seconds
time.sleep(5)

# This is the really important bit - we need to stop the motors when finished...
piz_moto.Motor1(0)
piz_moto.Motor2(0)

# Important delay - for some reason if this delay isn't here
# the motors don't stop.
# I suspect the program needs a cleaner exit
time.sleep(1)

task2.py

edit
#!/usr/bin/env python2.7
# PiZ-Moto tutorial task #1
# A program to make a two wheeled robot travel forwards for 5 seconds
# and then travel backwards for 5 seconds

# Import the piz-moto package
import piz_moto
import time


# Move forwards...
# Note that one motor must spin in the opposite direction to the other
# because it's on the opposite side of the robot
print("First we're moving forwards...")
piz_moto.Motor1(100)
piz_moto.Motor2(-100)

# wait 5 seconds
time.sleep(5)

# Turn left...
# Here we make one wheel turn one way as if the robot was moving forwards and 
# make the other wheel turn the opposite way as if the robot was moving backwards.
# Make the left wheel run backwards and the right wheel run forwards should
# make the robot spin to the left (or anti-clockwise)
# But don't forget that the motors are on opposite sides...
print("Now we're turning left...")
piz_moto.Motor1(100)
piz_moto.Motor2(100)

# wait 1 seconds
time.sleep(1)

# Move backwards...
# Note that one motor must spin in the opposite direction to the other
# because it's on the opposite side of the robot
print("Now we're moving backwards...")
piz_moto.Motor1(-100)
piz_moto.Motor2(100)

# wait 5 seconds
time.sleep(5)

# This is the really important bit - we need to stop the motors when finished...
piz_moto.Motor1(0)
piz_moto.Motor2(0)

# Important delay - for some reason if this delay isn't here
# the motors don't stop.
# I suspect the program needs a cleaner exit
time.sleep(1)

task3.py

edit
#!/usr/bin/env python2.7
# PiZ-Moto tutorial task #1
# A program to make a two wheeled robot travel forwards for 5 seconds
# and then travel backwards for 5 seconds

# Import the piz-moto package
import piz_moto
import time

# Initialise the speed, setting it to zero for the acceleration function
speed = 0

# Move forwards...
# Note that one motor must spin in the opposite direction to the other
# because it's on the opposite side of the robot
print("First we're accelerating forwards over 5 seconds...")
while speed <100:
	piz_moto.Motor1(speed)
	piz_moto.Motor2(speed * -1)
	speed = speed + 20
	time.sleep(1)

# wait 5 seconds
time.sleep(5)

# Turn left...
# Here we make one wheel turn one way as if the robot was moving forwards and 
# make the other wheel turn the opposite way as if the robot was moving backwards.
# Make the left wheel run backwards and the right wheel run forwards should
# make the robot spin to the left (or anti-clockwise)
# But don't forget that the motors are on opposite sides...
print("Now we're turning left...")
piz_moto.Motor1(100)
piz_moto.Motor2(100)

# wait 1 seconds
time.sleep(1)

# Move backwards...
# Note that one motor must spin in the opposite direction to the other
# because it's on the opposite side of the robot
print("Now we're moving backwards...")
piz_moto.Motor1(-100)
piz_moto.Motor2(100)

# wait 5 seconds
time.sleep(5)

# This is the really important bit - we need to stop the motors when finished...
piz_moto.Motor1(0)
piz_moto.Motor2(0)

# Important delay - for some reason if this delay isn't here
# the motors don't stop.
# I suspect the program needs a cleaner exit
time.sleep(1)