I wanted to control the robot using Python, so my first stop was the ever useful google, unfortunately other than one simple python example in 4tronix's PiRoCon github repository, the cupboard was bare. I did however know that Cymplecy (SimpleSi)'s ScratchGPIO code supported the PiRoCon so this code also gave me some inspiration (!). I have since found out that 4tronix have some downloadable examples on their site, check out the very bottom of this page - doh!
I wired the motor's up as per the instructions on 4tronix's website and the speed sensors as per the instructions on Cymplecy's blog and I set about creating a motor control class, which would allow me to control the speed of the motors and also get data from the speed sensors (encoders).
This is my starting point for more advanced features and automation, but as always start small, grow big.
There is a more 'complete' example in the code below, but you use the motor controller class like this:
import time import motorControl #setup gpio GPIO.setmode(GPIO.BOARD) #create motor control motors = motorControl.MotorController() #go forward - both motors at 100% motors.start(100) time.sleep(2) #go forward in a curve # - motor A at 100% # - motor B at 50% motors.start(100,50) time.sleep(2) #go backward at 100% # - using negative power motors.start(-100) time.sleep(2) #stop motors.stop() #cleanup gpio GPIO.cleanup()
The full Motor Controller code is below and in my initio repository github.com/martinohanlon/initio:
#PiRoCon - 4Tronix Initio - Motor Controller
#Martin O'Hanlon
#www.stuffaboutcode.com
import sys
import time
import RPi.GPIO as GPIO
#motor pins
MOTORAFWRDPIN = 19
MOTORABWRDPIN = 21
MOTORBFWRDPIN = 24
MOTORBBWRDPIN = 26
#encoder pins
MOTORAENCODERPIN = 7
MOTORBENCODERPIN = 11
#motor states
STATEFORWARD = 1
STATESTOPPED = 0
STATEBACKWARD = -1
class MotorController:
def __init__(self,
motorAForwardPin = MOTORAFWRDPIN,
motorABackwardPin = MOTORABWRDPIN,
motorBForwardPin = MOTORBFWRDPIN,
motorBBackwardPin = MOTORBBWRDPIN,
motorAEncoderPin = MOTORAENCODERPIN,
motorBEncoderPin = MOTORBENCODERPIN,):
#setup motor classes
self.motorA = Motor(motorAForwardPin, motorABackwardPin, motorAEncoderPin)
self.motorB = Motor(motorBForwardPin, motorBBackwardPin, motorBEncoderPin)
#motor properties
@property
def motorA(self):
return self.motorA
@property
def motorB(self):
return self.motorB
#start
def start(self, powerA, powerB = None):
#if a second power isnt passed in, both motors are set the same
if powerB == None: powerB = powerA
self.motorA.start(powerA)
self.motorB.start(powerB)
#stop
def stop(self):
self.motorA.stop()
self.motorB.stop()
#rotate left
def rotateLeft(self, power):
self.motorA.start(power * -1)
self.motorB.start(power)
#rotate right
def rotateRight(self, power):
self.motorA.start(power)
self.motorB.start(power * -1)
#class for controlling a motor
class Motor:
def __init__(self, forwardPin, backwardPin, encoderPin):
#persist values
self.forwardPin = forwardPin
self.backwardPin = backwardPin
self.encoderPin = encoderPin
#setup GPIO pins
GPIO.setup(forwardPin, GPIO.OUT)
GPIO.setup(backwardPin, GPIO.OUT)
GPIO.setup(encoderPin,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)
#add encoder pin event
GPIO.add_event_detect(encoderPin, GPIO.RISING, callback=self._encoderCallback,bouncetime=2)
#setup pwm
self.forwardPWM = GPIO.PWM(forwardPin,50)
self.backwardPWM = GPIO.PWM(backwardPin,50)
#setup encoder/speed ticks
self.totalTicks = 0
self.currentTicks = 0
self.state = STATESTOPPED
#motor state property
@property
def state(self):
return self.state
#start
def start(self, power):
#forward or backward?
# backward
if power < 0:
if self.state != STATEBACKWARD: self.stop()
self._backward(power)
self.state = STATEBACKWARD
# forward
if power > 0:
if self.state != STATEFORWARD: self.stop()
self._forward(power)
self.state = STATEFORWARD
# stop
if power == 0:
self.stop()
#stop
def stop(self):
#stop motor
self.forwardPWM.stop()
self.backwardPWM.stop()
self.state = STATESTOPPED
#reset ticks
self.currentTicks = 0
#private function to calculate the freq for the PWM
def _calcPowerAndFreq(self, power):
# make power between 0 and 100
power = max(0,min(100,abs(power)))
# make half of freq, minimum of 11
freq = max(11,abs(power/2))
return power, freq
#forward
def _forward(self, power):
#start forward motor
power, freq = self._calcPowerAndFreq(power)
self.forwardPWM.ChangeFrequency(freq)
self.forwardPWM.start(power)
#backward
def _backward(self, power):
#start backward motor
power, freq = self._calcPowerAndFreq(power)
self.backwardPWM.ChangeFrequency(freq)
self.backwardPWM.start(power)
#encoder callback
def _encoderCallback(self, pin):
self.totalTicks += 1
self.currentTicks += 1
#tests
if __name__ == '__main__':
try:
#setup gpio
GPIO.setmode(GPIO.BOARD)
#create motor control
motors = MotorController()
#forward
print("forward")
motors.start(100)
time.sleep(2)
print("encoder ticks")
print(motors.motorA.totalTicks)
print(motors.motorB.totalTicks)
#backward
print("backward")
motors.start(-50)
time.sleep(2)
#forward curve
print("forward curve")
motors.start(100,50)
time.sleep(2)
#rotate left
print("rotate left")
motors.rotateLeft(50)
time.sleep(2)
#rotate right
print("rotate right")
motors.rotateRight(50)
time.sleep(2)
#stop
print("stop")
motors.stop()
#Ctrl C
except KeyboardInterrupt:
print "User cancelled"
#Error
except:
print "Unexpected error:", sys.exc_info()[0]
raise
finally:
print ("cleanup")
#cleanup gpio
GPIO.cleanup()
Its my first time doing any robotics but so far I have been impressed with the Initio, the build quality and support seem really good - let the robot roll!

It is like the Bigtrak project with python http://thingswatihavedonewithmyraspberrypi.blogspot.co.uk/2012/10/controlling-bigtrack-motors-with-my.html
ReplyDeleteHi, I've just found your web, and I'll check your example. Thanks for sharing this, it's quite important to know how to control the robot with more precision.
ReplyDeleteThanks!
Hey, sorry for the question. Does anyone know how I could hard-code a way to change speed on the motors with the Robohat addon
ReplyDeleteSorry I have never used the Robohat addon.
Delete