«

»

Nov 18

Projet Bagheera – 08 – controle des servos moteur sur Raspberry Pi

Pour contrôler les 20 servomoteurs de l’hexapode, j’ai acheté deux cartes 16-Channel 12-bit PWM chez Adafruit. C’est la solution la moins couteuse tout en restant dans les performances. Et il est possible de mettre en série jusqu’à 62 cartes, soit 992 servomoteurs!

Installation du protocole I2C sous Debian

Pour contrôler les servomoteurs via la carte d’Adafruit 16 channels, il nous faut installer le protocole I2C:

$ sudo apt-get install i2c-tools

Puis charger les modules dans le noyau linux :

$ sudo modprobe i2c-dev

$ sudo modprobe i2c-bcm2708

et pour la partie programmation il nous faudra python-smbus:

$ sudo apt-get install python-smbus

Câblage de la carte Adafruit 16 channels avec les servomoteurs et le raspberry pi

Mieux vaut un bon schéma qu’un long discourt!

Adafruit utilise une breadboard dans son tutoriel, mais pour les non initiés, cela complique le montage, alors que 4 fils suffisent.

Faites attention à bien alimenter la carte Adafruit sur VCC en 3.3V et non 5V. Et alimentez directement la carte Adafruit, en son centre, avec une alimentation externe comprise en 5V-7V suivant vos servomoteurs.

Vérification de la communication i2C

Si vous avez bien tout connecté, il est possible de détecter la carte Adafruit depuis le raspberry pi avec la commande :

$ sudo i2cdetect -y 0

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: 70 -- -- -- -- -- -- --

La réponse indique que la carte Adafruit se trouve à l’adresse 0×40, ce qui correspond en binaire à 1000000, soit la première adresse.

Tout est prêt pour accéder à I2C et SMBus via python.

 

Contrôle d’un servomoteur via Python sur Raspberry Pi

J’ai repris l’exemple d’Adafruit qui se compose d’un script principal “Servo_Example.py” faisant appel à 2 scripts : Adafruit_PWM_Servo_Driver.py et Adafruit_I2C.py.

 

$ cd /home/pi/Desktop/

$ nano Servo_Example.py

#!/usr/bin/python
 
from Adafruit_PWM_Servo_Driver import PWM
import time
 
# ===========================================================================
# Example Code
# ===========================================================================
 
# Initialise the PWM device using the default address
# bmp = PWM(0×40, debug=True)
pwm = PWM(0×40, debug=True)
 
servoMin = 150 # Min pulse length out of 4096
servoMax = 600 # Max pulse length out of 4096
 
def setServoPulse(channel, pulse):
  pulseLength = 1000000 # 1,000,000 us per second
  pulseLength /= 60 # 60 Hz
  print “%d us per period” % pulseLength
  pulseLength /= 4096 # 12 bits of resolution
  print “%d us per bit” % pulseLength
  pulse *= 1000
  pulse /= pulseLength
  pwm.setPWM(channel, 0, pulse)
 
pwm.setPWMFreq(60) # Set frequency to 60 Hz
while (True):
  # Change speed of continuous servo on channel O
  pwm.setPWM(0, 0, servoMin)
  time.sleep(1)
  pwm.setPWM(0, 0, servoMax)
  time.sleep(1)

$ chmod +x Servo_Example.py

Puis

$ nano Adafruit_PWM_Servo_Driver.py

#!/usr/bin/python
 
import time
import math
from Adafruit_I2C import Adafruit_I2C
 
# ============================================================================
# Adafruit PCA9685 16-Channel PWM Servo Driver
# ============================================================================
 
class PWM :
  i2c = None
 
  # Registers/etc.
  __SUBADR1 = 0×02
  __SUBADR2 = 0×03
  __SUBADR3 = 0×04
  __MODE1 = 0×00
  __PRESCALE = 0xFE
  __LED0_ON_L = 0×06
  __LED0_ON_H = 0×07
  __LED0_OFF_L = 0×08
  __LED0_OFF_H = 0×09
  __ALLLED_ON_L = 0xFA
  __ALLLED_ON_H = 0xFB
  __ALLLED_OFF_L = 0xFC
  __ALLLED_OFF_H = 0xFD
 
  def __init__(self, address=0×40, debug=False):
    self.i2c = Adafruit_I2C(address)
    self.address = address
    self.debug = debug
    if (self.debug):
      print “Reseting PCA9685″
    self.i2c.write8(self.__MODE1, 0×00)
 
  def setPWMFreq(self, freq):
    ”Sets the PWM frequency”
    prescaleval = 25000000.0 # 25MHz
    prescaleval /= 4096.0 # 12-bit
    prescaleval /= float(freq)
    prescaleval -= 1.0
    if (self.debug):
      print “Setting PWM frequency to %d Hz” % freq
      print “Estimated pre-scale: %d” % prescaleval
    prescale = math.floor(prescaleval + 0.5)
    if (self.debug):
      print “Final pre-scale: %d” % prescale
 
    oldmode = self.i2c.readU8(self.__MODE1);
    newmode = (oldmode & 0x7F) | 0×10 # sleep
    self.i2c.write8(self.__MODE1, newmode) # go to sleep
    self.i2c.write8(self.__PRESCALE, int(math.floor(prescale)))
    self.i2c.write8(self.__MODE1, oldmode)
    time.sleep(0.005)
    self.i2c.write8(self.__MODE1, oldmode | 0×80)
 
  def setPWM(self, channel, on, off):
    ”Sets a single PWM channel”
    self.i2c.write8(self.__LED0_ON_L+4*channel, on & 0xFF)
    self.i2c.write8(self.__LED0_ON_H+4*channel, on >> 8)
    self.i2c.write8(self.__LED0_OFF_L+4*channel, off & 0xFF)
    self.i2c.write8(self.__LED0_OFF_H+4*channel, off >> 8)

$ chmod +x Adafruit_PWM_Servo_Driver.py

Puis

$ nano Adafruit_I2C.py

#!/usr/bin/python
 
import smbus
 
# ===========================================================================
# Adafruit_I2C Base Class
# ===========================================================================
 
class Adafruit_I2C :
 
  def __init__(self, address, bus=smbus.SMBus(0), debug=False):
    self.address = address
    self.bus = bus
    self.debug = debug
 
  def reverseByteOrder(self, data):
    ”Reverses the byte order of an int (16-bit) or long (32-bit) value”
    # Courtesy Vishal Sapre
    dstr = hex(data)[2:].replace(‘L’,”)
    byteCount = len(dstr[::2])
    val = 0
    for i, n in enumerate(range(byteCount)):
      d = data & 0xFF
      val |= (d << (8 * (byteCount – i – 1)))
      data >>= 8
    return val
 
  def write8(self, reg, value):
    ”Writes an 8-bit value to the specified register/address”
    try:
      self.bus.write_byte_data(self.address, reg, value)
      if (self.debug):
        print(“I2C: Wrote 0x%02X to register 0x%02X” % (value, reg))
    except IOError, err:
      print “Error accessing 0x%02X: Check your I2C address” % self.address
      return -1
 
  def writeList(self, reg, list):
    ”Writes an array of bytes using I2C format”
    try:
      self.bus.write_i2c_block_data(self.address, reg, list)
    except IOError, err:
      print “Error accessing 0x%02X: Check your I2C address” % self.address
      return -1
 
  def readU8(self, reg):
    ”Read an unsigned byte from the I2C device”
    try:
      result = self.bus.read_byte_data(self.address, reg)
      if (self.debug):
        print “I2C: Device 0x%02X returned 0x%02X from reg 0x%02X” % (self.address, result & 0xFF, reg)
      return result
    except IOError, err:
      print “Error accessing 0x%02X: Check your I2C address” % self.address
      return -1
 
  def readS8(self, reg):
    ”Reads a signed byte from the I2C device”
    try:
      result = self.bus.read_byte_data(self.address, reg)
      if (self.debug):
        print “I2C: Device 0x%02X returned 0x%02X from reg 0x%02X” % (self.address, result & 0xFF, reg)
      if (result > 127):
        return result – 256
      else:
        return result
    except IOError, err:
      print “Error accessing 0x%02X: Check your I2C address” % self.address
      return -1
 
  def readU16(self, reg):
    ”Reads an unsigned 16-bit value from the I2C device”
    try:
      hibyte = self.bus.read_byte_data(self.address, reg)
      result = (hibyte << 8) + self.bus.read_byte_data(self.address, reg+1)
      if (self.debug):
        print “I2C: Device 0x%02X returned 0x%04X from reg 0x%02X” % (self.address, result & 0xFFFF, reg)
      return result
    except IOError, err:
      print “Error accessing 0x%02X: Check your I2C address” % self.address
      return -1
 
  def readS16(self, reg):
    ”Reads a signed 16-bit value from the I2C device”
    try:
      hibyte = self.bus.read_byte_data(self.address, reg)
      if (hibyte > 127):
        hibyte -= 256
      result = (hibyte << 8) + self.bus.read_byte_data(self.address, reg+1)
      if (self.debug):
        print “I2C: Device 0x%02X returned 0x%04X from reg 0x%02X” % (self.address, result & 0xFFFF, reg)
      return result
    except IOError, err:
      print “Error accessing 0x%02X: Check your I2C address” % self.address
      return -1

$ chmod +x Adafruit_I2C.py

Il ne reste plus qu’à lancer le programme en tant qu’administrateur :

$ sudo python Servo_Example.py


 

2 comments

  1. Yaug

    Salut,
    Pour information, quels servo-moteur as tu acheté et comptes tu utiliser ?
    Merci

  2. honeyshell

    Bonjour Yaug, j’ai acheté mes servos chez hobbyking, ce sera pour le fonctionnement des pattes et de la webcam de mon projet d’hexapod.

Leave a Reply

Your email address will not be published. Required fields are marked *


seven − = 6

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>