UltraBorg PBR BattBorg and pygame robot. Help request for Python code.

Forums:

Hello PiBorg community,

Firstly I'd like to thank everyone at PiBorg for creating such brilliant boards.

The PBR gives plenty of power to control motors and the BattBorg does an excellent job of powering the Pi from batteries. The UltraBorg is a great bit of kit, offering control over servos and distance sensors. I love them all and have brought them together to create a robot with an onboard pan/tilt camera.

However, I am having some trouble with some Python code that I have cobbled together. I am sure this is a classic case of "operator error" and hopefully, can be easily resolved by someone with more knowledge than myself.

The code is designed to take inputs from a keyboard (wireless) and either move motors controlled by the PBR forwards, backwards, left and right; or move two servos controlled by the UltraBorg up, down, left and right.

The motors work perfectly with the code, but whenever I press a key to operate the servos I receive the following error:-

Traceback (most recent call last):
  File "/home/pi/picoborgrev/1.2-servo_motorcontrol_PBR_pygame .py", line 118, in 
    servo_up()
  File "/home/pi/picoborgrev/1.2-servo_motorcontrol_PBR_pygame .py", line 47, in servo_up
    servo2 += rateServo2
UnboundLocalError: local variable 'servo2' referenced before assignment

I have moved bits of code around trying to resolve the issue, but to no avail.

I have tried to comment the code methodically to show what I am trying to achieve with each section of the program. Hopefully this will help diagnose the source of the problem.

Thank you in advance for your time and any assistance you are able to offer.

Kind regards,
TankMan

PS The code is as follows:-

#!/usr/bin/env python

import time
import PicoBorgRev
import pygame
import os
import UltraBorg
 
# Settings
servoMin = -1.0                 # Smallest servo position to use
servoMax = +1.0                 # Largest servo position to use
startupDelay = 0.5              # Delay before making the initial move
stepDelay = 0.1                 # Delay between steps
rateStart = 0.05                # Step distance for all servos during initial move
rateServo1 = 0.01               # Step distance for servo #1
rateServo2 = 0.01               # Step distance for servo #2
 
# Start the UltraBorg
UB = UltraBorg.UltraBorg()      # Create a new UltraBorg object
UB.Init()                       # Set the board up (checks the board is connected)

# Start the PicoBorg Reverse
PBR = PicoBorgRev.PicoBorgRev()
PBR.Init()
PBR.ResetEpo()

# Start pygame & define pygame screen size
pygame.init()
screen = pygame.display.set_mode ((480,480))

print 'Move to central'
# Initial settings
servo1 = 0.0
servo2 = 0.0
# Set our initial servo positions
UB.SetServoPosition1(servo1)
UB.SetServoPosition2(servo2)
# Wait a while to be sure the servos have caught up
time.sleep(startupDelay)

# Define servo variables
def servo_left():
    servo1 += rateServo1

def servo_right():
    servo1 -= rateServo1

def servo_up():
    servo2 += rateServo2

def servo_down():
    servo2 -= rateServo2

# Check if any servos exceed limits and constrain to limit
if servo1 > servoMax:
    servo1 = servoMax
if servo1 < servoMax:
    servo1 = servoMax
if servo2 > servoMax:
    servo2 = servoMax
if servo2 < servoMin:
    servo2 = servoMin

# Set our new servo positions
    UB.SetServoPosition1(servo1)
    UB.SetServoPosition2(servo2)

# Define motor variables
def forwards():
    PBR.SetMotor1(+0.8)
    PBR.SetMotor2(+0.7)

def back():
    PBR.SetMotor1(-0.8)
    PBR.SetMotor2(-0.7)

def left():
    PBR.SetMotor1(-0.6)
    PBR.SetMotor2(+0.6)
    
def right():
    PBR.SetMotor1(+0.6)
    PBR.SetMotor2(-0.6)
    
def stop():
    PBR.MotorsOff()

def shutdown():
    os.system("sudo shutdown -P now")
      
while True:
    pygame.display.flip()
    keystate=pygame.key.get_pressed()
    
    if keystate[pygame.K_DOWN]:
        print 'backwards'
        back()

    elif keystate[pygame.K_UP]:
        print 'forwards'
        forwards()

    elif keystate[pygame.K_RIGHT]:
        print 'right'
        right()

    elif keystate[pygame.K_LEFT]:
        print 'left'
        left()

    elif keystate[pygame.K_a]:
        print 'servo left'
        servo_left()

    elif keystate[pygame.K_d]:
        print 'servo right'
        servo_right()

    elif keystate[pygame.K_w]:
        print 'servo up'
        servo_up()

    elif keystate[pygame.K_x]:
        print 'servo down'
        servo_down()

    elif keystate[pygame.K_SPACE]:
        print 'shutdown'
        shutdown()

    else:
        stop()

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            stop()
            exit(0)
            
    time.sleep(0.01)
piborg's picture

I think your problem is due to how Python uses variables.
Put simply if you are trying to read a value then Python will use anything it can find.
If you are trying to change a value it expects it to exist locally.

The simplest way to fix this would be to make the servo1 and servo2 variables global.

First change the initial settings to be global:

# Initial settings
global servo1
global servo2
servo1 = 0.0
servo2 = 0.0

Then any function which needs them should also make them global, for example:

# Define servo variables
def servo_left():
    global servo1
    servo1 += rateServo1

def servo_right():
    global servo1
    servo1 -= rateServo1

def servo_up():
    global servo2
    servo2 += rateServo2

def servo_down():
    global servo2
    servo2 -= rateServo2

now these functions will change the global version instead.

Hi piborg,

IT LIVES!

Thank you very much for your superbly swift and very helpful reply.

It has been a pretty busy week, so please accept my apology for not replying sooner.

I have managed to implement your code correctly and the pan/tilt mechanism is finally working.

I will tweak the code that limits the up/down and left/right limits and add a key-press that centres the mechanism, but, right now it works and I'm smiling from ear to ear.

I may be re-inventing the wheel a bit with this code, as I know that you have a web GUI. I will find time to explore it.

This code runs without wifi, controlled by a bluetooth keyboard and there are merits to that.

It has been a superb learning experience and your help has been the icing on the cake.

I wish you all the very best of fortune.

Kind regards,

TankMan

Hi all,

I am trying to revive the robot using a RaspberryPi 3 running Bullseye and Python3.

I think the problem I am experiencing is caused by Python3.

I have downloaded the PicoBorgRev3.py and UltraBorg3.py files and have saved them in the same folder as the code I want to run. I have amended the code to import PicoBorgRev3 as PicoBorgRev and import UltraBorg3 as UltraBorg.

The Demo GUI files for the PicoBorgReverse and UltraBorg are both working, so I am confident everything is wired-up correctly.

I am receiving an error when I run my code.

I run the code from terminal using this line:-
pi@raspberrypi:~/Python $ sudo python 1.4-servo_motorcontrol_PBR_pygame.py

and receive this error:-

  File "/home/pi/Python/./1.4-servo_motorcontrol_PBR_pygame.py", line 59
    global servo1
    ^
SyntaxError: name 'servo1' is used prior to global declaration

I am guessing this is caused by the new RaspberryPi using Python3.

The code is:-

#!/usr/bin/env python

import time
import PicoBorgRev3 as PicoBorgRev
import pygame
import os
import UltraBorg3 as Ultraborg
 
# Settings
servoMin = -1.0                 # Smallest servo position to use
servoMax = +1.0                 # Largest servo position to use
startupDelay = 0.5              # Delay before making the initial move
stepDelay = 0.1                 # Delay between steps
rateStart = 0.05                # Step distance for all servos during initial move
rateServo1 = 0.01               # Step distance for servo #1
rateServo2 = 0.01               # Step distance for servo #2
 
# Start the UltraBorg
UB = UltraBorg.UltraBorg()      # Create a new UltraBorg object
UB.Init()                       # Set the board up (checks the board is connected)

# Start the PicoBorg Reverse
PBR = PicoBorgRev.PicoBorgRev()
PBR.Init()
PBR.ResetEpo()

# Start pygame & define pygame screen size
pygame.init()
screen = pygame.display.set_mode ((480,480))

print('Move to central')
# Initial settings
global servo1
global servo2
servo1 = 0.0
servo2 = 0.0
# Set our initial servo positions
UB.SetServoPosition1(servo1)
UB.SetServoPosition2(servo2)
# Wait a while to be sure the servos have caught up
time.sleep(startupDelay)

# Define servo variables
def servo_left():
    global servo1
    servo1 += rateServo1
def servo_right():
    global servo1
    servo1 -= rateServo1
def servo_up():
    global servo2
    servo2 += rateServo2
def servo_down():
    global servo2
    servo2 -= rateServo2

# Check if any servos exceed limits and constrain to limit
if servo1 > servoMax:
    global servo1
    servo1 = servoMax
if servo1 < servoMax:
    global servo1
    servo1 = servoMax
if servo2 > servoMax:
    global servo2
    servo2 = servoMax
if servo2 < servoMin:
    global servo2
    servo2 = servoMin

# Set our new servo positions
    UB.SetServoPosition1(servo1)
    UB.SetServoPosition2(servo2)

# Define motor variables
def forwards():
    PBR.SetMotor1(+0.8)
    PBR.SetMotor2(+0.7)

def back():
    PBR.SetMotor1(-0.8)
    PBR.SetMotor2(-0.7)

def left():
    PBR.SetMotor1(-0.6)
    PBR.SetMotor2(+0.6)
    
def right():
    PBR.SetMotor1(+0.6)
    PBR.SetMotor2(-0.6)
    
def stop():
    PBR.MotorsOff()

def shutdown():
    os.system("sudo shutdown -P now")
      
while True:
    pygame.display.flip()
    keystate=pygame.key.get_pressed()
    
    if keystate[pygame.K_DOWN]:
        print('backwards')
        back()

    elif keystate[pygame.K_UP]:
        print('forwards')
        forwards()

    elif keystate[pygame.K_RIGHT]:
        print('right')
        right()

    elif keystate[pygame.K_LEFT]:
        print('left')
        left()

    elif keystate[pygame.K_a]:
        print('servo left')
        servo_left()

    elif keystate[pygame.K_d]:
        print('servo right')
        servo_right()

    elif keystate[pygame.K_w]:
        print('servo up')
        servo_up()

    elif keystate[pygame.K_x]:
        print('servo down')
        servo_down()

    elif keystate[pygame.K_SPACE]:
        print('shutdown')
        shutdown()

    else:
        stop()

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            stop()
            exit(0)
            
    time.sleep(0.01)

I have also attached the code as a .txt file.

I would greatly appreciate any help/advice you have, as I love PiBorg products and really want to get this back up and running.

Thank you in advance.

Images: 
Attachments: 
piborg's picture

You are correct, this is a difference between Python 2 and 3 :)

Basically Python 3 is a bit more strict about the use of global to help prevent accidental mix-ups between local and global values. In this case the error is that servo1 is used on the line above global servo1.

Normally you would fix this by moving global servo1 above the if block, but it this case we already have it up on line 33, so it can simply be removed. The same issue applies to global servo2.

In other words remove the global ... lines in the if block like so:

# Check if any servos exceed limits and constrain to limit
if servo1 > servoMax:
    servo1 = servoMax
if servo1 < servoMax:
    servo1 = servoMax
if servo2 > servoMax:
    servo2 = servoMax
if servo2 < servoMin:
    servo2 = servoMin

Don't remove the ones in the def servo_... functions above. Since they are functions they do need their own global ... lines.

Hi all,

Thank you very much for your swift response and for confirming that the error was due to Python3 handling global variables differently.

I made your recommended changes and that has moved me past the original error.

iI am getting a new error now, which appears to be regarding PyGame.

Hello from the pygame community. https://www.pygame.org/contribute.html
Traceback (most recent call last):
File "/home/pi/Python/./1.5-servo_motorcontrol_PBR_pygame.py", line 19, in
UB = UltraBorg.UltraBorg() # Create a new UltraBorg object
NameError: name 'UltraBorg' is not defined

I have seen elsewhere in the forum that PyGame can cause problems, and have seen that you created your own library -- GamePad.

I will download the code for GamePad, which is Python3 compatible, and will let you know how I get on.

I did anticipate that the migration to Python3 would require some updating of the code, but the only other option was to install Python2. My personal feeling is that, in the long term, the additional effort is worth while to give me up-to-date working code.

Once I have the robot working correctly over Bluetooth from a keypad, the next step will be to investigate your Web UI to stream video and offer control from the IP (which will mean my current code will be redundant, but I would like to get it working before I take the leap into Web Interfacing).

Thanks again for your help.

Ooops. After further investigation, I now see that GamePad is for using PlayStation-style controllers, not keyboards.

Is the error I am getting another global variable glitch from Python3? Are you able to offer a suggestion?

Thank you very much for your super-human patience.

Hi all,

Please ignore the previous two posts. I think I have the solution.

Line 7 reads:

import UltraBorg3 as Ultraborg

I think it should be:

import UltraBorg3 as UltraBorg

I will test and let you know the result.

Hello again,

As I suspected, this time the error was created by myself with the lower-case "b" being the culprit.

The capital "B" in UltraBorg in line 7 was the solution.

The working code is as follows:-

#!/usr/bin/env python

import time
import PicoBorgRev3 as PicoBorgRev
import pygame
import os
import UltraBorg3 as UltraBorg
 
# Settings
servoMin = -1.0                 # Smallest servo position to use
servoMax = +1.0                 # Largest servo position to use
startupDelay = 0.5              # Delay before making the initial move
stepDelay = 0.1                 # Delay between steps
rateStart = 0.05                # Step distance for all servos during initial move
rateServo1 = 0.01               # Step distance for servo #1
rateServo2 = 0.01               # Step distance for servo #2
 
# Start the UltraBorg
UB = UltraBorg.UltraBorg()      # Create a new UltraBorg object
UB.Init()                       # Set the board up (checks the board is connected)

# Start the PicoBorg Reverse
PBR = PicoBorgRev.PicoBorgRev()
PBR.Init()
PBR.ResetEpo()

# Start pygame & define pygame screen size
pygame.init()
screen = pygame.display.set_mode ((480,480))

print('Move to central')
# Initial settings
global servo1
global servo2
servo1 = 0.0
servo2 = 0.0
# Set our initial servo positions
UB.SetServoPosition1(servo1)
UB.SetServoPosition2(servo2)
# Wait a while to be sure the servos have caught up
time.sleep(startupDelay)

# Define servo variables
def servo_left():
    global servo1
    servo1 += rateServo1
def servo_right():
    global servo1
    servo1 -= rateServo1
def servo_up():
    global servo2
    servo2 += rateServo2
def servo_down():
    global servo2
    servo2 -= rateServo2

# Check if any servos exceed limits and constrain to limit
if servo1 > servoMax:
    servo1 = servoMax
if servo1 < servoMax:
    servo1 = servoMax
if servo2 > servoMax:
    servo2 = servoMax
if servo2 < servoMin:
    servo2 = servoMin

# Set our new servo positions
    UB.SetServoPosition1(servo1)
    UB.SetServoPosition2(servo2)

# Define motor variables
def forwards():
    PBR.SetMotor1(+0.8)
    PBR.SetMotor2(+0.7)

def back():
    PBR.SetMotor1(-0.8)
    PBR.SetMotor2(-0.7)

def left():
    PBR.SetMotor1(-0.6)
    PBR.SetMotor2(+0.6)
    
def right():
    PBR.SetMotor1(+0.6)
    PBR.SetMotor2(-0.6)
    
def stop():
    PBR.MotorsOff()

def shutdown():
    os.system("sudo shutdown -P now")
      
while True:
    pygame.display.flip()
    keystate=pygame.key.get_pressed()
    
    if keystate[pygame.K_DOWN]:
        print('backwards')
        back()

    elif keystate[pygame.K_UP]:
        print('forwards')
        forwards()

    elif keystate[pygame.K_RIGHT]:
        print('right')
        right()

    elif keystate[pygame.K_LEFT]:
        print('left')
        left()

    elif keystate[pygame.K_a]:
        print('servo left')
        servo_left()

    elif keystate[pygame.K_d]:
        print('servo right')
        servo_right()

    elif keystate[pygame.K_w]:
        print('servo up')
        servo_up()

    elif keystate[pygame.K_x]:
        print('servo down')
        servo_down()

    elif keystate[pygame.K_SPACE]:
        print('shutdown')
        shutdown()

    else:
        stop()

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            stop()
            exit(0)
            
    time.sleep(0.01)

Thank you again for your help with the global variable discrepancy from Python3, and for making such excellent hardware, and most of all for your superb software support.

I wish you all the very best. :)

piborg's picture

Glad to hear you got it working.

Hi piborg,

I have been delving deeper into the convoluted intricacies of global variables in Python 3, and although I stated the last code I posted was working, it turns out that there was a glitch.

The keyboard inputs were being registered (and printed on-screen for feedback) for the motors and servos, but only the motors were actually responding.

I finally tracked the problem down to the lines of code that set the servo positions. I have now moved that code inside the functions that define the up, down, left and right movement of the servos.

I have also added a key-press that re-centres the servos and some code that drops the 12V input voltage to the PBR to a 6V output voltage that matches the requirement of the motors I'm currently using.

I'm so glad to have revived this project. It has been on the shelf for eight years and really deserved the effort to update it. I'm now excited to explore your web GUI and the additional connection opportunities that will offer.

Thanks again for your help, you are great and I wish you all the very best for the future.

The new code is as follows:-

#!/usr/bin/env python3
# coding: utf-8

import time
import PicoBorgRev3 as PicoBorgRev
import pygame
import os
import UltraBorg3 as UltraBorg
 
# Settings
servoMin = -1.0                 # Smallest servo position to use
servoMax = +1.0                 # Largest servo position to use
startupDelay = 0.5              # Delay before making the initial move
stepDelay = 0.1                 # Delay between steps
rateStart = 0.05                # Step distance for all servos during initial move
rateServo1 = 0.01               # Step distance for servo #1
rateServo2 = 0.01               # Step distance for servo #2
 
# Start the UltraBorg
# global UB
UB = UltraBorg.UltraBorg()      # Create a new UltraBorg object
UB.Init()                       # Set the board up (checks the board is connected)

# Start the PicoBorg Reverse
# global PBR
PBR = PicoBorgRev.PicoBorgRev()
PBR.Init()
PBR.ResetEpo()

# Power settings
voltageIn = 12.0
voltageOut = 6.0

# Setup the power limits
if voltageOut > voltageIn:
    maxPower = 1.0
else:
    maxPower = voltageOut / float(voltageIn)

# Start pygame & define pygame screen size
pygame.init()
screen = pygame.display.set_mode ((480,480))

# Initial settings
print('Move to central')
central = 0.0

# Set our initial servo positions
UB.SetServoPosition1(central)
UB.SetServoPosition2(central)

# Read the servo positions
servo1 = UB.GetServoPosition1()
servo2 = UB.GetServoPosition2()

# Wait a while to be sure the servos have caught up
time.sleep(startupDelay)

# Define servo variables
def servo_left():
    global servo1
    servo1 += rateServo1
    UB.SetServoPosition1(servo1)    

def servo_right():
    global servo1
    servo1 -= rateServo1
    UB.SetServoPosition1(servo1)    

def servo_up():
    global servo2
    servo2 += rateServo2
    UB.SetServoPosition2(servo2)    

def servo_down():
    global servo2
    servo2 -= rateServo2
    UB.SetServoPosition2(servo2)    

def servo_central():
    global servo1
    global servo2
    UB.SetServoPosition1(central)
    UB.SetServoPosition2(central)
    
# Check if any servos exceed limits and constrain to limit
if servo1 > servoMax:
    servo1 = servoMax
if servo1 < servoMin:
    servo1 = servoMin
if servo2 > servoMax:
    servo2 = servoMax
if servo2 < servoMin:
    servo2 = servoMin

# Define motor variables
def forwards():
    PBR.SetMotor1(+0.8 * maxPower)
    PBR.SetMotor2(+0.7 * maxPower)

def back():
    PBR.SetMotor1(-0.8 * maxPower)
    PBR.SetMotor2(-0.7 * maxPower)

def left():
    PBR.SetMotor1(-0.6 * maxPower)
    PBR.SetMotor2(+0.6 * maxPower)
    
def right():
    PBR.SetMotor1(+0.6 * maxPower)
    PBR.SetMotor2(-0.6 * maxPower)
    
def stop():
    PBR.MotorsOff()

def shutdown():
    os.system("sudo shutdown -P now")
      
while True:
    pygame.display.flip()
    keystate=pygame.key.get_pressed()
    
    if keystate[pygame.K_DOWN]:
        print('backwards')
        back()

    elif keystate[pygame.K_UP]:
        print('forwards')
        forwards()

    elif keystate[pygame.K_RIGHT]:
        print('right')
        right()

    elif keystate[pygame.K_LEFT]:
        print('left')
        left()

    elif keystate[pygame.K_a]:
        print('servo left')
        servo_left()

    elif keystate[pygame.K_d]:
        print('servo right')
        servo_right()

    elif keystate[pygame.K_w]:
        print('servo up')
        servo_up()

    elif keystate[pygame.K_x]:
        print('servo down')
        servo_down()

    elif keystate[pygame.K_s]:
        print('servo central')
        servo_central()

    elif keystate[pygame.K_SPACE]:
        print('shutdown')
        shutdown()

    else:
        stop()

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            stop()
            exit(0)
            
    time.sleep(0.01)
Subscribe to Comments for &quot;UltraBorg PBR BattBorg and pygame robot. Help request for Python code.&quot;