Сервопривод SG90 на Raspberry Pi 5: управление из Python без дрожания

Сервопривод SG90 поворачивает вал на заданный угол и удерживает его. Он нужен для поворотных камер, замков, роботов-манипуляторов и «умных» кормушек. Подключить его к Raspberry Pi проще простого: три провода, но на Raspberry Pi 5 у такого подключения есть нюанс. Обычный программный ШИМ из gpiozero даёт заметное дрожание вала, а привычная библиотека pigpio на этой плате не работает.

В статье подключим SG90 к Raspberry Pi 5, управим им через gpiozero и посмотрим, как избавиться от дрожания с помощью аппаратного ШИМ на GPIO12. Про управление сразу несколькими сервоприводами через модуль PCA9685 есть отдельная статья: SG90 и PCA9685 на Python, а сам модуль описан здесь.


Как управляется сервопривод

Угол вала задаётся шириной импульса на сигнальном проводе. Импульсы идут с частотой 50 Гц (период 20 мс):

Ширина импульсаУгол вала (SG90)
около 0,5 мс−90° (крайнее положение)
около 1,5 мс0° (середина)
около 2,5 мс+90° (крайнее положение)

Это типичные значения для SG90. По документации производителя номинальный диапазон 1–2 мс, а реальные экземпляры допускают чуть больше, и у каждого немного свои границы. Если вал упирается в ограничитель и «гудит», сузьте диапазон (например, до 0,6–2,4 мс).

В процентах заполнения ШИМ на частоте 50 Гц это примерно 2,5 % (−90°), 7,5 % (0°) и 12,5 % (+90°).


Что понадобится

  • Raspberry Pi 5 с системой (установка Raspberry Pi OS);
  • сервопривод SG90 (или подобный маленький: MG90S);
  • три провода «мама–папа» или «мама–мама» (на проводах SG90 гнёзда).

Схема подключения

У SG90 три провода (цвета могут отличаться у разных производителей, но чаще всего такие):

ПроводНазначениеКонтакт Raspberry Pi 5
Коричневый (или чёрный)GNDGND, контакт 6
КрасныйПитание 5 В5V, контакт 2
Оранжевый (или жёлтый)СигналGPIO12, контакт 32
Схема подключения сервопривода SG90 к Raspberry Pi 5

Сигнал берём с GPIO12: это один из контактов, на которых у Raspberry Pi 5 доступен аппаратный ШИМ (его мы включим ниже). Для первых опытов с gpiozero подойдёт любой GPIO, но удобнее сразу использовать этот.

Питание сервопривода. Один SG90 без нагрузки потребляет немного, но в момент трогания и при упоре вала ток растёт до сотен миллиампер. Для одного-двух маленьких сервоприводов хватает 5 В платы (если блок питания качественный, см. питание и охлаждение). Для нескольких сервоприводов или более мощных (MG996R и т. п.) нужен отдельный источник 5 В, у которого минус соединён с GND Raspberry Pi. Если плата перезагружается при движении сервопривода, это признак просадки питания.

Сервопривод SG90 с белой качалкой подключён тремя проводами к контактам GPIO Raspberry Pi 5, сверху на плате стоит M.2 HAT+

Что на снимке. Так выглядит собранная схема: сервопривод SG90 (синий корпус, белая качалка) и три провода от штыревого разъёма GPIO платы. На плате стоит M.2 HAT+, поэтому провода воткнуты в удлинённые штыри сверху. По фото трудно разобрать, какой провод в каком гнезде: ориентируйтесь на схему выше (питание 5 В, земля GND и сигнал GPIO12). Цвета проводов сервопривода обычно такие: коричневый или чёрный — земля, красный — питание, оранжевый или жёлтый — сигнал.


Способ 1. gpiozero (программный ШИМ)

Самый простой способ: класс AngularServo из gpiozero (библиотека установлена в Raspberry Pi OS, подробнее о ней в статье про GPIO и gpiozero). Создайте файл servo_gpiozero.py:

from time import sleep

from gpiozero import AngularServo

servo = AngularServo(
    12,
    min_angle=-90,
    max_angle=90,
    min_pulse_width=0.0005,   # 0,5 мс
    max_pulse_width=0.0025,   # 2,5 мс
)

for angle in (0, 90, 0, -90, 0):
    print("Угол:", angle)
    servo.angle = angle
    sleep(1)

servo.detach()  # перестаём подавать импульсы, вал не удерживается

Запуск:

python3 servo_gpiozero.py
Терминал Raspberry Pi: python3 servo_gpiozero.py выводит предупреждение PWMSoftwareFallback про pigpio и строки Угол: 0, 90, 0, -90, 0

Что на снимке. Сервопривод проходит пять углов: в терминале строки Угол: 0, 90, 0, -90, 0 с паузой в секунду, и вал поворачивается следом. Перед ними выведено предупреждение PWMSoftwareFallback: To reduce servo jitter, use the pigpio pin factory. По-русски: «Чтобы уменьшить дрожание сервопривода, используйте фабрику выводов pigpio». Это не ошибка: gpiozero сообщает, что на этой плате формирует ШИМ программно. Совет про pigpio для Raspberry Pi 5 не подходит (эта библиотека на плате не работает, подробнее ниже), а дрожание убирает аппаратный ШИМ из способа 2. Предупреждение можно оставить как есть. Если оно мешает, добавьте в начало программы две строки: import warnings и warnings.simplefilter("ignore").

Запускайте программу обычным python3, без виртуального окружения: gpiozero и драйвер lgpio установлены в системе, и так они видны сразу. Если вы всё-таки работаете в окружении, созданном командой python3 -m venv env, программа остановится с ошибкой:

ModuleNotFoundError: No module named 'gpiozero'

По-русски: «Модуль gpiozero не найден». Обычное окружение изолировано от системных пакетов и не видит gpiozero. Выйдите из него (deactivate) и создайте заново с доступом к системным библиотекам:

deactivate
rm -rf ~/servo/env
python3 -m venv --system-site-packages ~/servo/env
source ~/servo/env/bin/activate
python3 servo_gpiozero.py

Параметр --system-site-packages открывает окружению пакеты python3-gpiozero и python3-lgpio из системы (проверить, что они установлены, можно командой dpkg -l | grep gpiozero). Если их нет, установите: sudo apt install python3-gpiozero python3-lgpio.

Вал должен повернуться на 0°, +90°, снова на 0°, затем на −90° и вернуться в середину. Если крайние положения не доходят до упора, расширьте диапазон min_pulse_width и max_pulse_width; если вал гудит в крайних положениях, сузьте (например, 0,0006 и 0,0024).

Метод detach() отключает сигнал: сервопривод больше не удерживает положение и не греется. Для постоянного удержания (например, поворот камеры) сигнал нужен всегда.

Когда нужно поворачивать от 0 до 180° без «отрицательных» углов, можно задать min_angle=0, max_angle=180: пересчёт диапазона gpiozero сделает сам.

Почему вал дрожит

На Raspberry Pi 5 библиотека gpiozero по умолчанию формирует ШИМ программно: через драйвер lgpio, которым управляет процессор. Импульсы получаются с небольшим разбросом по времени, и SG90 реагирует на это едва заметными подёргиваниями в точке удержания. Для поворота «туда-обратно» это незаметно, а для камеры или точной механики может мешать.

Известная библиотека pigpio с её аппаратно-таймированным ШИМ на Raspberry Pi 5 не работает: плата устроена по-другому (GPIO обслуживает микросхема RP1). Поэтому для точного сигнала используют аппаратный ШИМ.


Способ 2. Аппаратный ШИМ на GPIO12

У Raspberry Pi 5 четыре канала аппаратного ШИМ. Каналы 0 и 1 выведены на GPIO12 и GPIO13, каналы 2 и 3 на GPIO18 и GPIO19. Импульсы формирует сама микросхема, поэтому сигнал получается ровным и без нагрузки на процессор.

Шаг 1. Включаем ШИМ в конфигурации

Откройте файл конфигурации:

sudo nano /boot/firmware/config.txt

В конец добавьте строку (она включает два канала на GPIO12 и GPIO13):

dtoverlay=pwm-2chan,pin=12,func=4,pin2=13,func2=4

Сохраните файл и перезагрузите плату:

sudo reboot

Шаг 2. Определяем номер ШИМ-контроллера

После перезагрузки посмотрите, какие ШИМ-контроллеры есть в системе:

ls /sys/class/pwm
cat /sys/class/pwm/pwmchip*/npwm
Терминал Raspberry Pi: ls /sys/class/pwm показывает pwmchip0 и pwmchip1, cat /sys/class/pwm/pwmchip*/npwm выводит 4 и 4

Что на снимке. В системе два ШИМ-контроллера, pwmchip0 и pwmchip1, и у каждого по 4 канала (4 и 4). Количество каналов у них одинаковое, поэтому по нему контроллеры не различить. Чтобы понять, какой из них нужен, выведите подробный список:

ls -l /sys/class/pwm

Вывод покажет ссылки вида pwmchip0 -> …/1f00098000.pwm/pwm/pwmchip0. На Raspberry Pi 5 ШИМ создаёт микросхема RP1, и у неё два контроллера по четыре канала: 1f00098000.pwm (PWM0) и 1f0009c000.pwm (PWM1). Контакты GPIO12 и GPIO13 подключены к PWM0, поэтому нам нужен тот pwmchipN, в чьей ссылке есть 1f00098000.pwm. Его номер понадобится в программе.

На Raspberry Pi OS на Debian 13 (ядро 6.18) это pwmchip0, на более старых версиях ядра номер бывает другим (например, pwmchip2). Не подставляйте номер из примера, не проверив его: если ошиться, библиотека выдаст ошибку, которая указывает совсем на другое (см. раздел «Если что-то не так»). Убедиться, что оверлей сработал, можно командой pinctrl get 12,13: в строках должно быть GPIO12 = PWM0_CHAN0 и GPIO13 = PWM0_CHAN1.

Шаг 3. Устанавливаем библиотеку

Библиотеки ставьте в виртуальное окружение (о venv подробнее):

python3 -m venv ~/servo-env
source ~/servo-env/bin/activate
pip install rpi-hardware-pwm
Терминал Raspberry Pi: python3 -m venv ~/servo-env, source ~/servo-env/bin/activate и pip install rpi-hardware-pwm с сообщением Successfully installed rpi-hardware-pwm-0.3.1

Что на снимке. Первая команда создаёт окружение ~/servo-env, вторая включает его (в начале строки появляется (servo-env)), а pip install скачивает маленькую библиотеку rpi-hardware-pwm с зеркала piwheels.org и сообщает Successfully installed rpi-hardware-pwm-0.3.1. Для этой библиотеки системные пакеты не нужны, поэтому окружение здесь создаётся обычным способом, без --system-site-packages (в отличие от способа 1, где нужен gpiozero).

Шаг 4. Программа

Создайте файл servo_hw.py. Номер контроллера замените на тот, что нашли на втором шаге:

from time import sleep

from rpi_hardware_pwm import HardwarePWM

CHIP = 0       # номер pwmchipN контроллера 1f00098000.pwm (PWM0), см. шаг 2
CHANNEL = 0    # канал 0 = GPIO12
FREQ_HZ = 50   # частота сервопривода


def angle_to_duty(angle):
    """Угол -90..+90 градусов в заполнение ШИМ, %: 2,5 % ... 12,5 %."""
    return 7.5 + angle / 90 * 5.0


pwm = HardwarePWM(pwm_channel=CHANNEL, hz=FREQ_HZ, chip=CHIP)
pwm.start(angle_to_duty(0))

try:
    for angle in (0, 90, 0, -90, 0):
        print("Угол:", angle)
        pwm.change_duty_cycle(angle_to_duty(angle))
        sleep(1)
finally:
    pwm.stop()

Запуск:

python3 servo_hw.py
Терминал Raspberry Pi в окружении servo-env: python3 servo_hw.py выводит Угол: 0, 90, 0, -90, 0 без ошибок

Что на снимке. В окружении servo-env программа печатает те же пять углов (0, 90, 0, -90, 0) и завершается без ошибок. Предупреждения PWMSoftwareFallback здесь нет: ШИМ формирует сама микросхема, а не gpiozero.

Вал будет поворачиваться так же, как и в первом варианте, но без подёргиваний.

Если крайние положения не достигаются или вал упирается, подстройте формулу: 2,5 и 12,5 % соответствуют импульсам 0,5 и 2,5 мс.

Два сервопривода. Оверлей из примера включает канал 0 на GPIO12 и канал 1 на GPIO13. Второй сервопривод можно подключить к GPIO13 и управлять им отдельным объектом HardwarePWM(pwm_channel=1, hz=50, chip=CHIP). Эти контакты при включённом оверлее заняты под ШИМ и для других целей использовать их не стоит.


Несколько сервоприводов

Для 3–16 сервоприводов удобнее использовать отдельный контроллер на I2C — PCA9685: он берёт на себя формирование импульсов, а Raspberry Pi только отправляет ему углы по шине I2C. Подключение и пример кода в статье SG90 и PCA9685 на Python. Включение I2C описано в статье про I2C, SPI и UART.


Если что-то не так

СимптомЧто проверить
Сервопривод не двигаетсяПитание на красном проводе (5 В), общая земля, правильность номера GPIO
ModuleNotFoundError: No module named 'gpiozero'Вы запустили программу из обычного виртуального окружения. Запускайте вне окружения или пересоздайте его с python3 -m venv --system-site-packages env
Вал дрожитВключите аппаратный ШИМ (способ 2) или питайте сервопривод от отдельного источника
Вал гудит в крайних положенияхСузьте диапазон импульсов: вал упирается в ограничитель
Плата перезагружается при движенииПросадка питания: подключите отдельный источник 5 В, объединив земли
HardwarePWMException: Need to add 'dtoverlay=pwm-2chan' to /boot/config.txt and rebootСообщение вводит в заблуждение: библиотека выдаёт его и тогда, когда папки /sys/class/pwm/pwmchip<CHIP> просто нет. Проверьте номер CHIP: он должен совпадать с pwmchipN, у которого в ls -l /sys/class/pwm есть 1f00098000.pwm (на новых версиях ядра это pwmchip0). Только потом проверяйте строку dtoverlay в /boot/firmware/config.txt (не /boot/config.txt) и перезагрузку

Что дальше

  • Датчик HC-SR04 на Raspberry Pi — «парктроник»: сервопривод с дальномером как сканер;
  • Камера Raspberry Pi — сервопривод для поворота камеры;
  • Веб-интерфейс на Flask для GPIO — управление углом с телефона.

Материалы

Оставьте комментарий

Отправляя комментарий, вы соглашаетесь с обработкой ваших данных (имя, адрес электронной почты, текст комментария) в соответствии с политикой конфиденциальности.