Управление сервоприводом SG90 с помощью PCA9685 из Python на Raspberry Pi и Orange Pi

Сервопривод — это мотор-редуктор, способный поворачивать выходной вал в заданное положение (на заданный угол) и удерживать его в этом положении, вопреки сопротивлениям и возмущениям. Сервопривод Tower Pro 9g SG90 не имеет мощные характеристики (всего 1,2-1,6 кг*см), но имеет недорогую цену. Отлично подходит для управления небольшими легкими механизмами под управлением контроллеров Arduino, Raspberry Pi и т.п.. Рабочее напряжение Tower Pro 9g SG90 от 3В до 7.2В, угол поворота ограничен диапазоном от 0 до 180 градусов (в реальность — чуть больше).

В этой статье будем управлять сервоприводом SG90 с помощью PCA9685 из Python на Orange Pi PC. Можно использовать Raspberry Pi, Banana Pi, NanoPi или любой другой мини-компьютер под управлением ОС Linux имеющий I2C порт.



Библиотека на Python для PCA9685
Поскольку библиотека Adafruit_Python_PCA9685 для работы с PCA9685 из Python работает только на Raspberry Pi, она была переписана так, чтобы ее можно было использовать на Orange Pi и Banana Pi. Теперь используется SMBus в качестве I2C драйвера, как установить тут: SMBus: Работа с шиной I2C на Python в Raspberry Pi/Orange Pi/Banana Pi.

Файл PCA9685.py


import logging
import time
import math

# Based on Adafruit Lib:
# https://github.com/adafruit/Adafruit_Python_PCA9685/blob/master/Adafruit_PCA9685/PCA9685.py

# Default address:
PCA9685_ADDRESS = 0x40

# Registers/etc:
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

# Channels
CHANNEL00 = 0x00
CHANNEL01 = 0x01
CHANNEL02 = 0x02
CHANNEL03 = 0x03
CHANNEL04 = 0x04
CHANNEL05 = 0x05
CHANNEL06 = 0x06
CHANNEL07 = 0x07
CHANNEL08 = 0x08
CHANNEL09 = 0x09
CHANNEL10 = 0x0A
CHANNEL11 = 0x0B
CHANNEL12 = 0x0C
CHANNEL13 = 0x0D
CHANNEL14 = 0x0E
CHANNEL15 = 0x0F

class PCA9685(object):
def __init__(self, i2cBus, address=PCA9685_ADDRESS):
self.i2cBus = i2cBus
self.address = address
self.begin()

def begin(self):
"""Initialize device"""
self.set_all_pwm(0, 0)
self.i2cBus.write_byte_data(self.address, MODE2, OUTDRV) self.i2cBus.write_byte_data(self.address, MODE1, ALLCALL)
time.sleep(0.005) # wait for oscillator
mode1 = self.i2cBus.read_byte_data(self.address, MODE1)
mode1 = mode1 & ~SLEEP # wake up (reset sleep)
self.i2cBus.write_byte_data(self.address, MODE1, mode1)
time.sleep(0.005) # wait for oscillator

def reset(self):
self.i2cBus.write_byte_data(self.address, MODE1, RESTART)
time.sleep(0.01)

def set_address(self, address):
"""Sets device address."""
self.address = address

def set_i2c_bus(self, i2cBus):
"""Sets I2C Bus."""
self.i2cBus = i2cBus

def set_pwm(self, channel, on, off):
"""Sets a single PWM channel."""
self.i2cBus.write_byte_data(self.address, LED0_ON_L + 4 * channel, on & 0xFF)
self.i2cBus.write_byte_data(self.address, LED0_ON_H + 4 * channel, on >> 8)
self.i2cBus.write_byte_data(self.address, LED0_OFF_L + 4 * channel, off & 0xFF)
self.i2cBus.write_byte_data(self.address, LED0_OFF_H + 4 * channel, off >> 8)

def set_all_pwm(self, on, off):
"""Sets all PWM channels."""
self.i2cBus.write_byte_data(self.address, ALL_LED_ON_L, on & 0xFF)
self.i2cBus.write_byte_data(self.address, ALL_LED_ON_H, on >> 8)
self.i2cBus.write_byte_data(self.address, ALL_LED_OFF_L, off & 0xFF)
self.i2cBus.write_byte_data(self.address, ALL_LED_OFF_H, off >> 8)

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
prescale = int(math.floor(prescaleval + 0.5))
oldmode = self.i2cBus.read_byte_data(self.address, MODE1)
newmode = (oldmode & 0x7F) | 0x10 # sleep
self.i2cBus.write_byte_data(self.address, MODE1, newmode) # go to sleep
self.i2cBus.write_byte_data(self.address, PRESCALE, prescale)
self.i2cBus.write_byte_data(self.address, MODE1, oldmode)
time.sleep(0.005)
self.i2cBus.write_byte_data(self.address, MODE1, oldmode | 0x80)

def __enter__(self):
return self

def __exit__(self, exception_type, exception_value, traceback):
self.reset()


Описание методов (функций)
__init__()
Конструктор класса.

__init__(self, i2cBus, address=PCA9685_ADDRESS)
Параметры
i2cBus — Объект типа PCA9685.
address — I2C адрес устройства. По умолчанию PCA9685_ADDRESS = 0x40.

begin()
Инициализация устройства.

begin(self)
set_address()
Установка адреса устройства.

set_address(self, address)
Параметры
address — I2C адрес устройства.

set_i2c_bus()
Установка I2C шины.

set_i2c_bus(self, i2cBus)
Параметры
i2cBus — Объект типа PCA9685.

set_pwm()
Устанавливает ШИМ одного из выводов PCA9685.

set_pwm(self, channel, on, off)
Параметры
channel — Один из выводов PWM от 0 до 15.
on — В какой момент цикла из 4096 частей включить ШИМ.
off — В какой момент цикла из 4096 частей выключить ШИМ.

set_all_pwm()
Устанавливает ШИМ на все выводы PCA9685.

set_all_pwm(self, on, off)
Параметры
on — В какой момент цикла из 4096 частей включить ШИМ.
off — В какой момент цикла из 4096 частей выключить ШИМ.

set_pwm_freq()
Устанавливает частоту ШИМ для всего чипа, до ~ 1,6 кГц.

set_pwm_freq(self, freq_hz)
Параметры
freq_hz — Частота в Герцах.

Библиотека на Python для сервоприводов
Для более удобного управления сервоприводом, основные функции были собраны в одном классе — ServoPCA9685. Тут можно найти минимальную (servo_min = 130) и максимальную (servo_max = 510) длину импульса для безопасного управления сервоприводом SG90.

# Configure min and max servo pulse lengths
servo_min = 130
servo_max = 510

Если ваш сервопривод работает с другими значениями, тогда вы можете редактировать их.

Файл ServoPCA9685.py

import time
# Servo with PCA9685 implementation

# Configure min and max servo pulse lengths
servo_min = 130 # Min pulse length out of 4096 / 150/112
servo_max = 510 # Max pulse length out of 4096 / 600/492

def map(x, in_min, in_max, out_min, out_max):
return (x - in_min) * (out_max - out_min + 1) / (in_max - in_min + 1) + out_min

class ServoPCA9685(object):
def __init__(self, pca9685, channel):
self.pca9685 = pca9685
self.channel = channel
self.set_pwm_freq(50)
self.set_pulse(300)

def set_pwm_freq(self, freq=50):
self.pca9685.set_pwm_freq(freq)
time.sleep(0.005)

def set_angle(self, angle):
self.set_pulse(map(angle, 0, 180, servo_min, servo_max))

def set_pulse(self, pulse):
if pulse >= servo_min and pulse <= servo_max:
self.pca9685.set_pwm(self.channel, 0, pulse)
time.sleep(0.005)

def disable(self):
self.pca9685.set_pwm(self.channel, 0, 0)
time.sleep(0.005)

Описание методов (функций)
__init__()
Конструктор класса.

__init__(self, pca9685, channel)
pca9685 — Объект типа PCA9685.
channel — Один из ШИМ выводов PCA9685 от 0 до 15.

set_pwm_freq()
Установка частоты ШИМ для вашего сервопривода.

set_pwm_freq(self, freq=50)
freq — Частота в Герцах. По умолчанию freq=50.

set_angle()
Установка примерного угла сервопривода.

set_angle(self, angle)
angle — Угол от 0 до 180 градусов.

set_pulse()
Установка длины импульса.

set_pulse(self, pulse)
pulse — Длина ШИМ импульса.

disable()
Отключение сервопривода (установка длины импульса в ноль «0»).

disable(self)
Примеры программ
Схема подключения сервопривода SG90 к PCA9685




Управление одним сервоприводом SG90
Чтобы управлять сервоприводом посредством PCA9685 нужно соблюдать следующие шаги:

Нужно открыть шину I2C «0» (или «1»);
i2cBus = smbus.SMBus(0)
Создаём объект класса PCA9685, а в качестве параметра конструктора используем выше созданный объект: i2cBus;
pca9685 = PCA9685.PCA9685(i2cBus)
Создаём объект класса ServoPCA9685 для управления одного сервопривода, в качестве первого параметра используем выше созданный объект, pca9685, а второй параметр — это номер канала PCA9685, можно выбрать следующие значения: PCA9685.CHANNEL00, PCA9685.CHANNEL01, PCA9685.CHANNEL02, …, PCA9685.CHANNEL15 или номера от 0 до 15;
servo00 = ServoPCA9685.ServoPCA9685(pca9685, PCA9685.CHANNEL00)
Для управления сервоприводом можно использовать два метода, а именно: set_pulse(pulse), где pulse — это длина ШИМ импульса от servo_min = 130 до servo_max = 510; и set_angle(angle), где angle — это угол поворота от 0 до 180 градусов, метод (функция) пропорционально переносит значение из текущего диапазона значений в градусах (от 0 до 180) в новый диапазон (от 130 до 510) в импульсах.
Нижеприведённый пример кода поварачивает сервопривод в одну сторону,

# 130 -> 510
for pulse in range(ServoPCA9685.servo_min, ServoPCA9685.servo_max + 1):
servo00.set_pulse(pulse)
time.sleep(0.01)
потом в другую

# 510 -> 130
for pulse in reversed(range(ServoPCA9685.servo_min, ServoPCA9685.servo_max + 1)):
servo00.set_pulse(pulse)
time.sleep(0.01)

с использованием метода set_pulse(pulse), а в конце отключает подаваемый на сервопривод ШИМ.

servo00.disable()
Файл servo_1x_pulse.py
Пример управления сервоприводом используя метод set_pulse(pulse).

import time
import smbus
import PCA9685
import ServoPCA9685

i2cBus = smbus.SMBus(0)
pca9685 = PCA9685.PCA9685(i2cBus)
servo00 = ServoPCA9685.ServoPCA9685(pca9685, PCA9685.CHANNEL00)

# 130 -> 510
for pulse in range(ServoPCA9685.servo_min, ServoPCA9685.servo_max + 1):
servo00.set_pulse(pulse)
time.sleep(0.01)

# 510 -> 130
for pulse in reversed(range(ServoPCA9685.servo_min, ServoPCA9685.servo_max + 1)):
servo00.set_pulse(pulse)
time.sleep(0.01)

servo00.disable()


Файл servo_1x_angle.py
Пример управления сервоприводом используя метод set_angle(angle).

import time
import smbus
import PCA9685
import ServoPCA9685

i2cBus = smbus.SMBus(0)
pca9685 = PCA9685.PCA9685(i2cBus)
servo00 = ServoPCA9685.ServoPCA9685(pca9685, PCA9685.CHANNEL00)

# 0 - > 180
for angle in range(0, 180 + 1):
servo00.set_angle(angle)
time.sleep(0.01)

# 180 -> 0
for angle in reversed(range(0, 180 + 1)):
servo00.set_angle(angle)
time.sleep(0.01)

servo00.disable()

Управление несколькими сервоприводами SG90
Управлять несколькими сервоприводами можно аналогичным способом, как и одним. Единственное отличие в том, что нужно создать для каждого сервопривода отдельный экземпляр класса ServoPCA9685. К примеру:

servo00 = ServoPCA9685.ServoPCA9685(pca9685, PCA9685.CHANNEL00)
servo01 = ServoPCA9685.ServoPCA9685(pca9685, PCA9685.CHANNEL01)
servo02 = ServoPCA9685.ServoPCA9685(pca9685, PCA9685.CHANNEL02)
servo03 = ServoPCA9685.ServoPCA9685(pca9685, PCA9685.CHANNEL03)

каждый объект должен иметь отличное от других имя и свой собственный канал (от 0 до 15).

servo_Nx_pulse.py
Пример управления несколькими (четырьмя) сервоприводами используя метод set_pulse(pulse).

import time
import smbus
import PCA9685
import ServoPCA9685

i2cBus = smbus.SMBus(0)
pca9685 = PCA9685.PCA9685(i2cBus)
servo00 = ServoPCA9685.ServoPCA9685(pca9685, PCA9685.CHANNEL00)
servo01 = ServoPCA9685.ServoPCA9685(pca9685, PCA9685.CHANNEL01)
servo02 = ServoPCA9685.ServoPCA9685(pca9685, PCA9685.CHANNEL02)
servo03 = ServoPCA9685.ServoPCA9685(pca9685, PCA9685.CHANNEL03)

# 130 -> 510
for pulse in range(ServoPCA9685.servo_min, ServoPCA9685.servo_max + 1):
servo00.set_pulse(pulse)
servo01.set_pulse(pulse)
servo02.set_pulse(pulse)
servo03.set_pulse(pulse)
time.sleep(0.01)

# 510 -> 130
for pulse in reversed(range(ServoPCA9685.servo_min, ServoPCA9685.servo_max + 1)):
servo00.set_pulse(pulse)
servo01.set_pulse(pulse)
servo02.set_pulse(pulse)
servo03.set_pulse(pulse)
time.sleep(0.01)

servo00.disable()
servo01.disable()
servo02.disable()
servo03.disable()

Источник

27.02.2023 в 19:47, Просмотров: 1505
Опубликовал: ak167






Предложить пост

Поиск



По всему сайту
По тэгам
По тэгам и заголовкам

Боковое меню

  • Карта сайта
  • RSS-каналы
  • Обратная связь
  • Пользователи



    Рейтинг@Mail.ru