Close

From soft(ware) PWM to hard(ware) PWM

A project log for Balambér

a robotic handpuppet

borazsloborazslo 11/03/2016 at 15:250 Comments

In the beginning I had no external PWM module and my Raspberry Pi3 has only 2 native PWM output. So I have used PigPio module as a software solution. It was a good temporary solution even though the PWM had an interference with my room's lights.

So I've ordered a PCA9685 16 Channel PWM Servo motor Driver. The Raspberry Pi can communicate with this cheap module via I2C protocol. So now I have 16 channels. :D

The new module needs new wiring and new code. I followed this post: Adafruit 16 Channel Servo Driver with Raspberry Pi. It was fine and easy. So I moved on to write the new code with extra features like clapping and bowing. (Do not forget to install the Adafruit_PCA9685 as it was written in that adafruit post.) So here my new code:

import time, sys
import Adafruit_PCA9685

class Servo:
 
   min = 100
   max = 480

   pwm = Adafruit_PCA9685.PCA9685()
   pwm = Adafruit_PCA9685.PCA9685(address=0x70, busnum=1)
   pwm.set_pwm_freq(50)

   def __init__(self, channel):
      self.channel = channel
      pass

   def setLimits(min, max):
        self.min = min
        self.max = min
        pass

   def set_pwm(self, on, off):
       self.pwm.set_pwm(self.channel, on, off)
       pass

   def setDegree(self, degree):  
        oneDegree =  (self.max - self.min) / 180 
        self.set_pwm(0, self.min + int(oneDegree * degree))
        pass

   def move(self, degreeFrom, degreeTo, speed = 0.01):  
        if degreeFrom < degreeTo:
            step = 1
        else:
            step = -1
        for degree in range(degreeFrom, degreeTo, step):
            self.setDegree(degree)
            time.sleep(speed)
        pass


class Arm(Servo):
   
   def __init__(self, channel, limits):
      self.channel = channel
      self.limits = limits
      pass

   def wave(self):
      self.move(self.limits[1],self.limits[3], 0.002)
      self.move(self.limits[3],self.limits[1], 0.004)
      pass

   def reset(self):
      self.setDegree(self.limits[1])
      pass

class Arms():
    
    def __init__(self):
      self.rightLimits = [20, 50, 155, 160] # -, normal, clap, full
      self.leftLimits = [140, 110, 35, 10]

      self.Left = Arm(3,self.leftLimits)
      self.Right = Arm(1,self.rightLimits)
      pass

    def clap(self, speed = 0):
        leftDistance = self.leftLimits[1] - self.leftLimits[2] 
        rightDistance = self.rightLimits[2] - self.rightLimits[1]
        if leftDistance > rightDistance:
            distance = leftDistance
        else:
            distance = rightDistance
        
        for count in range(0, distance):
            self.Right.setDegree(self.rightLimits[1] + count)
            self.Left.setDegree(self.leftLimits[1] - count)
            time.sleep(speed)
        for count in range(distance, 0, -1):
            self.Right.setDegree(self.rightLimits[1] + count)
            self.Left.setDegree(self.leftLimits[1] - count)
            time.sleep(speed)
        pass

    def claps(self, quantity):
        for c in range(0,quantity):
            self.clap()
        pass


class Head():

   def __init__(self):
      self.Horizontal = Servo(2)
      self.Vertical = Servo(0)
      
      self.horizontalBase = 90
      self.verticalBase = 160
      pass

   def shake(self):
      limits = [40,self.horizontalBase,140]
      speed = 0.002
      self.Horizontal.move(limits[0],limits[2],speed)
      self.Horizontal.move(limits[2],limits[0],speed)
      self.Horizontal.move(limits[0],limits[1],speed)
      pass

   def nod(self):
      limits = [self.verticalBase,130]
      self.Vertical.move(limits[0],limits[1], 0.01)
      self.Vertical.move(limits[1],limits[0], 0.01)
      pass

   def bow(self):
      limits = [self.verticalBase,100]
      self.Vertical.move(limits[0],limits[1],0.05)
      self.Vertical.move(limits[1],limits[0],0.01)
      pass    

   def reset(self):
      self.Horizontal.setDegree(self.horizontalBase)
      self.Vertical.setDegree(self.verticalBase)
      pass   

Arms = Arms()
Head = Head()


if len(sys.argv) > 1:
    command = sys.argv[1]
    if command == "no":
        Head.shake()
    elif command == "reset":
        Head.reset()
        Arms.Left.reset()
        Arms.Right.reset()
    elif command == "yes":
        Head.nod()
        Head.nod()
    elif command == "bow":
        Head.bow()
    elif command == "right":
        Arms.Left.wave()        
    elif command == "left":
        Arms.Right.wave()        
    elif command == "clap":
        Arms.claps(3)        
    else:
        print "Invalid command"
else:
    print "Arguments needed"
You can give commands to Balambér through arguments of the command. So he is happy again. :D


Discussions