很久之前整过PWM舵机,刚好最近师弟需要,并且网上现有教程不是很完整,就整理一下。方便交流以及后面回顾。
首先要明确,在这个控制方式中需要用到哪些方面:
1、树莓派与PCA9685之间使用I2C通信
2、PCA9685通讯协议
3、FT2331M舵机性能参数
一、树莓派与PCA9685 I2C通信
接线
接线方式可如下图所示,参考树莓派4B使用PCA9685控制舵机:
其中VCC供电为6V.
开启I2C:
在终端执行:
sudo raspi-config
Interfacing Options-->I2C-->YES-->Finish
保险起见 重启树莓派 终端输入 :
sudo reboot
安装相关库文件:
sudo apt-get update
sudo apt-get install python-pip
sudo pip install RPi.GPIO
sudo apt-get install python-smbus
开启I2C并且完成树莓派与PCA9685接线之后,可以通过如下命令查看设备是否正常搭载:
ls /dev/*i2c*
查看I2C搭载设备地址:
pi@raspberrypi:~ $ sudo i2cdetect -y 1 # 默认为I2C1,如果是I2C0.则只需要将1-->0即可
关于I2C设备的操作,详细可看之前的文章关于树莓派4B两个I2C通道(I2C0、I2C1)的总结 。
二、PCA9685通讯协议
相关代码我放在文中,并且有具体注释。
from __future__ import division
import logging
import time
import math
# Registers/etc:
PCA9685_ADDRESS = 0x40
MODE1 = 0x00
MODE2 = 0x01
SUBADR1 = 0x02
SUBADR2 = 0x03
SUBADR3 = 0x04
PRESCALE = 0xFE
LED0_ON_L = 0x06
LED0_ON_H = 0x07
LED0_OFF_L = 0x08
LED0_OFF_H = 0x09
ALL_LED_ON_L = 0xFA
ALL_LED_ON_H = 0xFB
ALL_LED_OFF_L = 0xFC
ALL_LED_OFF_H = 0xFD
# Bits:
RESTART = 0x80
SLEEP = 0x10
ALLCALL = 0x01
INVRT = 0x10
OUTDRV = 0x04
logger = logging.getLogger(__name__)
def software_reset(i2c=None, **kwargs):
"""Sends a software reset (SWRST) command to all servo drivers on the bus."""
# Setup I2C interface for device 0x00 to talk to all of them.
if i2c is None:
import Adafruit_GPIO.I2C as I2C
i2c = I2C
self._device = i2c.get_i2c_device(0x00, **kwargs)
self._device.writeRaw8(0x06) # SWRST
class PCA9685(object):
"""PCA9685 PWM LED/servo controller."""
def __init__(self, address=PCA9685_ADDRESS, i2c=None, **kwargs):
"""Initialize the PCA9685."""
# Setup I2C interface for the device.
if i2c is None:
import Adafruit_GPIO.I2C as I2C
i2c = I2C
self._device = i2c.get_i2c_device(address, **kwargs)
self.set_all_pwm(0, 0)
self._device.write8(MODE2, OUTDRV)
self._device.write8(MODE1, ALLCALL)
time.sleep(0.005) # wait for oscillator
mode1 = self._device.readU8(MODE1)
mode1 = mode1 & ~SLEEP # wake up (reset sleep)
self._device.write8(MODE1, mode1)
time.sleep(0.005) # wait for oscillator
def set_pwm_freq(self, freq_hz):
"""Set the PWM frequency to the provided value in hertz."""
prescaleval = 25000000.0 # 25MHz
prescaleval /= 4096.0 # 12-bit
prescaleval /= float(freq_hz)
prescaleval -= 1.0
logger.debug('Setting PWM frequency to {0} Hz'.format(freq_hz))
logger.debug('Estimated pre-scale: {0}'.format(prescaleval))
prescale = int(math.floor(prescaleval + 0.5))
logger.debug('Final pre-scale: {0}'.format(prescale))
oldmode = self._device.readU8(MODE1);
newmode = (oldmode & 0x7F) | 0x10 # sleep
self._device.write8(MODE1, newmode) # go to sleep
self._device.write8(PRESCALE, prescale)
self._device.write8(MODE1, oldmode)
time.sleep(0.005)
self._device.write8(MODE1, oldmode | 0x80)
def set_pwm(self, channel, on, off):
"""Sets a single PWM channel."""
self._device.write8(LED0_ON_L+4*channel, on & 0xFF)
self._device.write8(LED0_ON_H+4*channel, on >> 8)
self._device.write8(LED0_OFF_L+4*channel, off & 0xFF)
self._device.write8(LED0_OFF_H+4*channel, off >> 8)
def set_all_pwm(self, on, off):
"""Sets all PWM channels."""
self._device.write8(ALL_LED_ON_L, on & 0xFF)
self._device.write8(ALL_LED_ON_H, on >> 8)
self._device.write8(ALL_LED_OFF_L, off & 0xFF)
self._device.write8(ALL_LED_OFF_H, off >> 8)
def cmd(self, channel, Position):
'''
已经进行数据转换,position范围0-210度
'''
'''
PCA9685是一种PWM控制器,可以产生从0到4096之间的脉宽值,对应于PWM信号的高电平时间
PWM频率被设置为60Hz,周期的时间为 1 / 60 秒,即约 16.67 毫秒(16666 微秒)
servo_min = 150 # Min pulse length out of 4096
servo_max = 600 # Max pulse length out of 4096
servo_min 的实际脉宽时间为 (150 / 4096) * 16666 微秒 ≈ 610 微秒
servo_max 的实际脉宽时间为 (600 / 4096) * 16666 微秒 ≈ 2440 微秒
Ft2331舵机参数为0~240°对应500~2500us
'''
servo_min = int(500 * 60 / 1000000 * 4096) # Min pulse length out of 4096
servo_max = int(2500 * 60 / 1000000 * 4096) # Max pulse length out of 4096
Position = int(Position / (240 - 0) * (servo_max - servo_min) + servo_min) # 240这个数值需对每个舵机进行标定,有可能需要增大,有可能需要减小
self.set_pwm(channel, 0, Position)
pwm = PCA9685()
pwm.set_pwm_freq(60)
channel = 0
while True:
pwm.cmd(channel, 0)
time.sleep(1)
pwm.cmd(channel, 90)
time.sleep(1)
pwm.cmd(channel, 180)
time.sleep(1)
pwm.cmd(channel, 90)
time.sleep(1)
三、FT233M1舵机参数
舵机控制参数如下所示:
从旋转角度那一栏可知,舵机由0-240°,对应周期时间为500-2500us。
所以在第二节中的协议中,控制函数cmd(self, channel, Position)中存在(同样和通信频率有关),如下代码:
'''
PCA9685是一种PWM控制器,可以产生从0到4096之间的脉宽值,对应于PWM信号的高电平时间
PWM频率被设置为60Hz,周期的时间为 1 / 60 秒,即约 16.67 毫秒(16666 微秒)
servo_min = 150 # Min pulse length out of 4096
servo_max = 600 # Max pulse length out of 4096
servo_min 的实际脉宽时间为 (150 / 4096) * 16666 微秒 ≈ 610 微秒
servo_max 的实际脉宽时间为 (600 / 4096) * 16666 微秒 ≈ 2440 微秒
Ft2331舵机参数为0~240°对应500~2500us
'''
servo_min = int(500 * 60 / 1000000 * 4096) # Min pulse length out of 4096
servo_max = int(2500 * 60 / 1000000 * 4096) # Max pulse length out of 4096
Position = int(Position / (240 - 0) * (servo_max - servo_min) + servo_min) # 240这个数值需对每个舵机进行标定,有可能需要增大,有可能需要减小