1.8 Development Steps this week. #11 #12

Development Step #11 – Controlling Pan/Tilt servos with Raspberry Pi

 

o Approach.

I will try to calculate the Duty Cycle of the servos since they might be different from what the manufacture has claimed. I found a nice instructables tutorial to take me through the process.

o Learning Experience.

I found the manufacturer’s data sheet for the servos:
Annotation 2019-08-25 203537.jpg

The Raspberry Pi has no analog output, but we can simulate this, using a PWM (Pulse Width Modulation) approach. What we will do is to generate a digital signal with a fixed frequency, where we will change the pulse train width, what will be “translated” as an “average” output voltage level.

Note that what matters here is not the frequency itself, but the “Duty Cycle”, that is the relation between the time that the puls is “high” divided by the wave period. For example, suppose that we will generating a 50Hz pulse frequency on one of our Raspberry Pi GPIO. The period (p) will the inverse of frequency or 20ms (1/f). If we want that our LED with a “half” bright, we must have a Duty Cycle of 50%, that means a “pulse” that will be “High” for 10ms.

This principle will be very important for us, to control our servo position, once the “Duty Cycle” will define the servo position.

I am connecting the pan/tilt servos to the following GPIO pins:

  • Tilt Servo – GPIO 17
  • Pan Servo – GPIO 27

Both of them are taking 5V power from Pin#2 and Pin#4, and they are also grounded to the Raspberry Pi.

In theory, the servo will be on its

  • Initial Position (0 degrees) when a pulse of 1ms is applied to its data terminal
  • Neutral Position (90 degrees) when a pulse of 1.5 ms is applied to its data terminal
  • Final Position (180 degrees) when a pulse of 2 ms is applied to its data terminal

To program a servo position using Python will be very important to know the correspondent “Duty Cycle” for the above positions, let’s do some calculation:

  • Initial Position ==> (0 degrees) Pulse width ==> 1ms ==> Duty Cycle = 1ms/20ms ==> 2.0%
  • Neutral Position (90 degrees) Pulse width of 1.5 ms ==> Duty Cycle = 1.5ms/20ms ==> 7.5%
  • Final Position (180 degrees) Pulse width of 2 ms ==> Duty Cycle = 2ms/20ms ==> 10%

So the Duty Cycle should vary on a range of 2 to 10 %.

Now lets find out that for each servo individually.

by typing the following commands in the Terminal

import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
tiltPin = 17

Now, we must specify that this pin will be an “output”

GPIO.setup(tiltPin, GPIO.OUT)

And, what will be the frequency generated on this pin, that for our servo will be 50Hz:

tilt = GPIO.PWM(tiltPin, 50)

Now, let’s start generating a PWM signal on the pin with an initial duty cycle (we will keep it “0”):

tilt.start(0)

Now, you can enter different duty cycle values, observing the movement of your servo. Let’s start with 2% and see what happens (we expect that the servo goes to “zero position”):

tilt.ChangeDutyCycle(2)

…..

  • Result:

Those are the results for both the Pan and the Tilt servos:

GGG02231.JPG

Annotation 2019-08-25 230225.jpg

Annotation 2019-08-25 231646.jpg

GGG02232.JPG

 

The PWM commands to be sent to our servo are in “duty cycles” as we saw on the last step. But usually, we must use “angle” in degrees as a parameter to control a servo. So, we must convert “angle” that is a more natural measurement to us in duty cycle as understandable by our Pi.

I know that duty cycle range goes from 2% to 12% and that this is equivalent to angles that will range from 0 to 180 degrees. Also, we know that those variations are linear.
GGG02233.JPG
So, given an angle, we can have a correspondent duty cycle:

dutycycle = angle/18 + 2

Now we can make a Python script using the formula

Servo5.py

from time import sleep
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

def setServoAngle(servo, angle):
	pwm = GPIO.PWM(servo, 50)
	pwm.start(8)
	dutyCycle = angle / 18. + 3.
	pwm.ChangeDutyCycle(dutyCycle)
	sleep(0.3)
	pwm.stop()

if __name__ == '__main__':
	import sys
	servo = int(sys.argv[1])
	GPIO.setup(servo, GPIO.OUT)
	setServoAngle(servo, int(sys.argv[2]))
	GPIO.cleanup()
sudo python3 angleServoCtrl.py 17 45

The above command will position the servo connected on GPIO 17 with 45 degrees in “elevation”. A similar command could be used for Pan Servo control (position to 45 degrees in “azimuth”):

sudo python servo5.py 27 45

Now lets use the script for controlling both of the servos at the same time:

Servo6.py

 

from time import sleep
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

pan = 27
tilt = 17

GPIO.setup(tilt, GPIO.OUT) # white => TILT
GPIO.setup(pan, GPIO.OUT) # gray ==> PAN

def setServoAngle(servo, angle):
	assert angle >=50 and angle <= 130
	pwm = GPIO.PWM(servo, 50)
	pwm.start(8)
	dutyCycle = angle / 18. + 2.
	pwm.ChangeDutyCycle(dutyCycle)
	sleep(0.3)
	pwm.stop()

if __name__ == '__main__':
	import sys
	if len(sys.argv) == 1:
		setServoAngle(pan, 110)
		setServoAngle(tilt, 50)
	else:
		setServoAngle(pan, int(sys.argv[1])) # 30 ==> 90 (middle point) ==> 150
		setServoAngle(tilt, int(sys.argv[2])) # 30 ==> 90 (middle point) ==> 150
	GPIO.cleanup()

When the script is executed, you must enter as parameters, Pan angle and Tilt angle. For example:

sudo python3 servo6.py 120 50

I found the proper angles for my Servos

Pan Servo:

  • Min – 50
  • Mid – 110
  • Max – 150

Tilt Servo:

  • Min – 50
  • Mid – 60
  • Max – 90

Annotation 2019-08-26 000207.jpg

Now the final test is a loop cycle on the pan and tilt axis:

servo7.py

from time import sleep
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

pan = 27
tilt = 17

GPIO.setup(tilt, GPIO.OUT) # white => TILT
GPIO.setup(pan, GPIO.OUT) # gray ==> PAN

def setServoAngle(servo, angle):
	assert angle >=50 and angle <= 130
	pwm = GPIO.PWM(servo, 50)
	pwm.start(8)
	dutyCycle = angle / 18. + 2.
	pwm.ChangeDutyCycle(dutyCycle)
	sleep(0.3)
	pwm.stop()

if __name__ == '__main__':  
    for i in range (50, 140, 15):
        setServoAngle(pan, i)
        setServoAngle(tilt, i)
    
    for i in range (130, 50, -15):
        setServoAngle(pan, i)
        setServoAngle(tilt, i)
        
    setServoAngle(pan, 110)
    setServoAngle(tilt, 50)    
    GPIO.cleanup()

o Result.

o Sources.

https://www.instructables.com/id/Pan-Tilt-Multi-Servo-Control/

Development Step #12 – Object Tracking with OpenCV and Raspberry Pi

o Approach.

First I will start with some examples using Object Tracking and OpenCV and after that I will proceed with live video and integrating the servo function.

o Learning Experience.

I already have installed the OpenCV library in a previous dev. step.

I will start by analyzing the code by Adrian posted on his page ( https://www.pyimagesearch.com/2015/09/14/ball-tracking-with-opencv/ )

I have to verify that the OpenCV library is installed correctly:
Annotation 2019-08-26 004329.jpg

I want to test if OpenCV is working as before, by running that test python script:

import numpy as np
import cv2
cap = cv2.VideoCapture(0)
while(True):
    ret, frame = cap.read()
    frame = cv2.flip(frame, -1) # Flip camera vertically
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
    cv2.imshow('frame', frame)
    cv2.imshow('gray', gray)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()
cv2.destroyAllWindows()

To execute the OpenCV code I need to get into the OpenCV’s virtual environment from the terminal with the following commands:

source ~/.profile
workon cv
python simpleCamTest.py

Unfortunately I bumped into an error “workon cv is not valid”

I started backtracking the issue and reached the conclusion that there could be something wrong with the OpenCV version I am using 3.2.0 and maybe I have missed some packages during my installation, so I decided to look for them.
I headed to this installation guide : https://www.pyimagesearch.com/2017/09/04/raspbian-stretch-install-opencv-3-python-on-your-raspberry-pi/

I started installing all the mentioned libraries, but nothing fixed the issue. So after many tries I decided to completely reinstall the OpenCV library, which is a long process that I wanted to avoid.

Annotation 2019-08-26 033548.jpg

Everything was going smooth until I started getting multiple error messages and the process stopped.

Annotation 2019-08-26 033331.jpg

Annotation 2019-08-26 033400.jpg

Annotation 2019-08-26 033435.jpg

Annotation 2019-08-26 033526.jpg

I tried again, but no effect:

Annotation 2019-08-26 033246.jpg

This time I receive a “free space” error, even though that I use a 32GB sd card.

Going through the comments the only solution to the issue is to make a clean install of Raspbian and start installing OpenCV, VNC and all the other libraries that are needed.

Sadly because of the time scope of this project, I am unable to continue the development.

o Sources.

Ball Tracking with OpenCV

https://www.hackster.io/mjrobot/automatic-vision-object-tracking-5575c4#_=

Color Detection in Python with OpenCV

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s