Featured image of post R2D2 Arm Connected to RPI3

R2D2 Arm Connected to RPI3

Building R2D2 Arms using RPI3.

RPI3

You can use the RPI3 to use the servos. But the servo power will need to separte from the RPI3. The RPI3’s 5V is not enough to use servos long term. For initial testing they will work. But what you will find is the servos will glitch if connected to the RPI3’s header. The RPI3 causes the glitches. So eventually you need to find an alternative way to run the servos. There are two options I had on hand: a Maestro Mirco and PCA9685.

I included some basic code to just make the servo work. I also added the actual code I used to control the arms through the RPI3.

Read here on the Flask UI setup.

Function for Angle to Duty Cycle

The difficult part with controlling the servo with a RPI3 is the value you use to move the servo. The value is given as a duty cycle. I looked around how to convert an angle to duty cycle. I finally found one that worked with my servos. I am using Tower Pro MG995. This function should work for must people, but you can change it if needed.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    @staticmethod
    def calc_duty_cycle_from_angle(angle):
        """
        Calculate the duty cycle based off the angle given.  Then
        move the servo based off this angle.
        :param angle:
        :return: Duty cycle for servo based off angle.
        """
        #return ((angle / 180.0) + 1.0) * 5.0
        return angle / 18.0 + 3.0

Schematic

Connect the servos to the RPI3 directly.

Servo Color Code

1
2
3
Brown  = GND
Red    = Power 
Orange = Signal

Pinout

1
2
3
Orange -> Pin 7 (RPI3 GPIO 4)
Red    -> Pin 2 (RPI3 5V)
Brown  -> Pin 6 (RPI3 GND)

RPI3 Servo R2D2 Arms

Video

RPI3 Running the Servos

Code

https://github.com/artoo-ai/R2D2/blob/master/controls/rpi3_servo_control.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import RPi.GPIO as GPIO
import time
import sys


class Rpi3ServoControl:
    """
    Use the RPI3 to control the servos.
    """

    def __init__(self, servo_pin, init_angle=0.0):
        self.servo_pin = servo_pin
        self.gpio_pin = None
        self.last_angle = 0.0

        self.setup_pin()            # Setup GPIO pin
        self.set_angle(init_angle)

    def setup_pin(self):
        """
        Setup the GPIO pin.  Use the GPIO pin given in the constructor.
        :return:
        """
        GPIO.setmode(GPIO.BCM)                          # Setup GPIO pin to BCM
        GPIO.setup(self.servo_pin, GPIO.OUT)            # Set GPIO pin to output
        self.gpio_pin = GPIO.PWM(self.servo_pin, 50)    # GPIO PWM with 50Hz
        self.gpio_pin.start(2.5)                        # Initialization

    def shutdown(self):
        """
        Shutdown the object.  This will properly shutdown everything.
        :return:
        """
        self.gpio_pin.stop()
        GPIO.cleanup()

    def center(self):
        """
        Center the servo.
        :return:
        """
        self.set_angle(0.0)  # CENTER (7.5)

    def set_angle(self, angle):
        """
        Set the servo to the given angle.
        :param angle: Angle to move the servo
        :return:
        """
        if angle > 180:
            angle = 180
        if angle < 0:
            angle = 0

        # Cacluate the duty cycle to move to that angle
        dc = Rpi3ServoControl.calc_duty_cycle_from_angle(angle)
        print("Pin: " + str(self.servo_pin) + " Angle: " + str(angle) + " DC: " + str(dc))

        # Move the servo
        self.gpio_pin.ChangeDutyCycle(dc)

        time.sleep(0.1)

        # Record the last angle
        self.last_angle = angle

    @staticmethod
    def calc_duty_cycle_from_angle(angle):
        """
        Calculate the duty cycle based off the angle given.  Then
        move the servo based off this angle.
        :param angle:
        :return: Duty cycle for servo based off angle.
        """
        #return ((angle / 180.0) + 1.0) * 5.0
        return angle / 18.0 + 3.0


if __name__ == '__main__':
    if len(sys.argv) > 1:
        print("Using Servo Pin " + sys.argv[1])
        rpi3_servo = Rpi3ServoControl(int(sys.argv[1]))
    else:
        print("Using Servo Pin 4")
        rpi3_servo = Rpi3ServoControl(4)

    if len(sys.argv) > 2:
        angle_list = sys.argv[2:]
        print("Angles to try: " + str(sys.argv[2:]))
        for idx in range(len(angle_list)):
            rpi3_servo.set_angle(float(angle_list[idx]))
            time.sleep(0.5)
    rpi3_servo.shutdown()

Basic Code

In this example, I connected the servo signal pin to RPI3 GPIO pin 4. This will then open and close the servo.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import RPi.GPIO as GPIO
import time

servoPIN = 4
GPIO.setmode(GPIO.BCM)
GPIO.setup(servoPIN, GPIO.OUT)

p = GPIO.PWM(servoPIN, 50) # GPIO 17 for PWM with 50Hz
p.start(2.5) # Initialization
try:
    while True:
        p.ChangeDutyCycle(5)
        time.sleep(0.5)
        p.ChangeDutyCycle(7.5)      # CENTER
        time.sleep(0.5)
        p.ChangeDutyCycle(10)
        time.sleep(0.5)
        p.ChangeDutyCycle(12.5)     # 180 degrees
        time.sleep(0.5)
        p.ChangeDutyCycle(10)
        time.sleep(0.5)
        p.ChangeDutyCycle(7.5)      # CENTER
        time.sleep(0.5)
        p.ChangeDutyCycle(5)
        time.sleep(0.5)
        p.ChangeDutyCycle(2.5)      # 0 degrees
        time.sleep(0.5)
except KeyboardInterrupt:
    p.stop()
    GPIO.cleanup()

Parts List

The R2D2 arm holder can be found at http://astromech.com/

comments powered by Disqus