Сервоприводы PDM постоянного вращения: особенности применения и примеры кода

Сервоприводы PDM постоянного вращения — это моторы, которые управляются сигналом PDM (Pulse Duration Modulation) и поддерживает скорость вращения вала в любом направлении без ограничений по углу поворота.

Сервоприводы непрерывного вращения используются для моделирования движения различных механизмов. На основе сервопривода можно собрать вращающийся столик для предметной фотосъёмки, мобильного бота или радар кругового обзора.

Список моделей

Модель Форм-фактор Сигнал управления Обратная связь Назначение Внутренний интерфейс Диапазон вращения
Feetech FS90R / Документация Микро PDM Нет Постоянное вращение Аналоговый 360°
Feetech FT90R / Документация Микро PDM Нет Постоянное вращение Цифровой 360°
Feetech FS5103R Стандарт PDM Нет Постоянное вращение Аналоговый 360°
Feetech FS5113R Стандарт PDM Нет Постоянное вращение Аналоговый 360°
Feetech FB5317M-360 / Документация Стандарт PDM Да Постоянное вращение Цифровой 360°

Интерфейс управления

В сервоприводах PDM постоянного вращения скорость вращения вала зависит от длины импульса. Для управления мотором необходимо подавать особый сигнал PDM (Pulse Duration Modulation) — импульсы постоянной частоты и переменной ширины. При поступлении с внешнего контроллера управляющего импульса начинка сервопривода генерирует свой сигнал мотору.

В сигнале PDM частота импульсов постоянна и равна 50 Гц, т. е. период подачи импульсов равен 20 мс. А вот ширина импульса изменяется, и именно от неё зависит направление и скорость вращения мотора. Принято считать, что рабочая ширина импульса лежит в пределах 544–2400 мкс.

В качестве примера приведём данные для популярного хобби-сервопривода Feetech FS90R.

Период импульсов Ширина импульса Положение сервопривода
20 мс 544 мкс Вал сервопривода вращается по часовой стрелке (CW) с максимальной скоростью.
20 мс 1540 мкс Вал сервопривода стоит на месте.
20 мс 2400 мкс Вал сервопривода вращается против часовой стрелки (CWW) с максимальной скоростью.

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

Даже в рамках одной и той же модели сервопривода существует погрешность, допускаемая при производстве, которая приводит к тому, что рабочий диапазон длин импульсов отличается. Для точной работы каждый конкретный сервопривод должен быть откалиброван: путём экспериментов необходимо подобрать корректный диапазон, характерный именно для него.

Часто способ управления сервоприводами называют PWM (Pulse Width Modulation) или PPM (Pulse Position Modulation). Это не так, и использование этих способов может даже повредить привод. Корректный термин — PDM (Pulse Duration Modulation) в котором важна длина импульсов, а не частота.

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

Сервоприводы с обратной связью

В сервоприводах PDM постоянного вращения также встречаются модели с обратной связью, у которых сигнал внутреннего потенциометра выведен отдельным проводом. Однако обратная связь показывает не скорость вращения вала, а его текущий угол поворота.

Обычно наличие сигнала обратной связи явно указывается в характеристиках сервопривода.

Питание сервопривода

Сервопривод можно запитать двумя способами: напрямую от контроллера (если позволяют характеристики) или же от внешнего источника питания.

Без внешнего источника питания

Если сервопривод питается напряжением 5 вольт и потребляет ток менее 500 мА, есть возможность запитать сервомотор непосредственно от контроллера.

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

Цвет провода Контакт Функция Подключение
Оранжевый / Жёлтый / Белый S Пин управления сервоприводом Подключите к пину ввода-вывода контроллера.
Красный V Питание Подключите к питанию контроллера.
Коричневый / Чёрный G Земля Подключите к земле контроллера.

С внешним источником питания

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

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

Цвет провода Контакт Функция Подключение
Оранжевый / Жёлтый / Белый S Пин управления сервоприводом Подключите к пину ввода-вывода контроллера.
Красный V Питание Подключите к внешнему источнику питания с подходящим вольтажом.
Коричневый / Чёрный G Земля Подключите к земле внешнего источника питания и земле контроллера.

Примеры работы для Arduino

Возьмём для тестовых проектов популярный сервопривод Feetech FS90R. А мозгом выступит платформа Arduino Uno.

Подключение и настройка

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

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

Схема устройства

Для быстрой сборки и отладки устройства рекомендуем взять плату расширения Troyka Shield, которая надевается сверху на Uno R3 методом бутерброда.

Программная настройка

Вращение вала

Для старта протестируем мотор в трёх режимах вращения:

  • Максимальная скорость по часовой стрелке (CW).
  • Пауза — вал мотора стоит на месте.
  • Максимальная скорость против часовой стрелки (CCW).

Код для Arduino IDE

servo-pdm-continuous-rotation-speed.ino
// Библиотека с расширенными функциями для работы с Servo
#include <AmperkaServo.h>
 
// Создаём объект для работы с сервомоторами
AmperkaServo servo;
 
// Задаём имя пина, к которому подключён сервопривод
constexpr uint8_t SERVO_PIN = 9;
 
void setup() {
  // Подключаем сервомотор
  // servo.attach(SERVO_PIN);
  // Подключаем сервомотор с расширенными параметрами
  // Советуем использовать именно этот вариант для точной настройки мотора  
  // servo.attach(pin, minPulseWidth, maxPulseWidth);
  // - pin: номер пина, к которому подключён сервопривод
  // - minPulseWidth: ширина импульса, 
  // соответствующая максимальной скорости по часовой стрелке CW.
  // Опциональный и по умолчанию стоит 544 мкс.
  // - maxPulseWidth: ширина импульса,
  // соответствующая максимальной скорости против часовой стрелки CW.
  // Опциональный и по умолчанию стоит 2400 мкс.
  // Данные возьмите из технических характеристик мотора
  servo.attach(SERVO_PIN, 544, 2400);
}
 
void loop() {
  // Задаём максимальную скорость вращения по часовой стрелке CW
  servo.writeSpeed(255);
  // Ждём 3 секунды
  delay(3000);
  // Останавливаем сервопривод
  servo.writeSpeed(0);
  // Ждём 1 секунду
  delay(1000);
  // Задаём максимальную скорость вращения против часовой стрелки CCW
  servo.writeSpeed(-255);
  // Ждём 3 секунды
  delay(3000);
  // Останавливаем сервопривод
  servo.writeSpeed(0);
  // Ждём 1 секунду
  delay(1000);
}

После прошивки устройства вал сервопривода будет сначала вращаться с максимальной скоростью по часовой стрелке, сделает паузу, а затем начнёт вращение против часовой стрелки.

Вращение вала с плавным разгоном

Усложним задачу. Сделаем плавный разгон вала по очереди в каждую сторону:

  • Плавный разгон до максимальной скорости по часовой стрелке (CW).
  • Пауза — вал мотора стоит на месте.
  • Плавный разгон до максимальной скорости против часовой стрелки (CCW).

Код для Arduino IDE

servo-pdm-continuous-rotation-speed-sweep.ino
// Библиотека с расширенными функциями для работы с Servo
#include <AmperkaServo.h>
 
// Создаём объект для работы с сервомоторами
AmperkaServo servo;
 
// Задаём имя пина, к которому подключён сервопривод
constexpr uint8_t SERVO_PIN = 9;
 
void setup() {
  // Подключаем сервомотор
  // servo.attach(SERVO_PIN);
  // Подключаем сервомотор с расширенными параметрами
  // Советуем использовать именно этот вариант для точной настройки мотора  
  // servo.attach(pin, minPulseWidth, maxPulseWidth);
  // - pin: номер пина, к которому подключён сервопривод
  // - minPulseWidth: ширина импульса, 
  // соответствующая максимальной скорости по часовой стрелке CW.
  // Опциональный и по умолчанию стоит 544 мкс.
  // - maxPulseWidth: ширина импульса,
  // соответствующая максимальной скорости против часовой стрелки CW.
  // Опциональный и по умолчанию стоит 2400 мкс.
  // Данные возьмите из технических характеристик мотора
  servo.attach(SERVO_PIN, 544, 2400);
}
 
void loop() {
  // Перебираем значения скорости вала сервы
  // от нуля до максимальной по часовой стрелке CW
  for (int speed = 0; speed < 255; speed++) {
    // Отправляем текущую скорость на серво
    servo.writeSpeed(speed);
    // Ждём 10 мс
    delay(10);
  }
 
  // Останавливаем сервопривод
  servo.writeSpeed(0);
  // Ждём 1 секунду
  delay(1000);
 
  // Перебираем значения скорости вала сервы
  // от нуля до максимальной против часовой стрелке CCW
  for (int speed = 0; speed > -255; speed--) {
    // Отправляем текущую скорость на серво
    servo.writeSpeed(speed);
    // Ждём 10 мс
    delay(10);
  }
 
  // Останавливаем сервопривод
  servo.writeSpeed(0);
  // Ждём 1 секунду
  delay(1000);
}

После прошивки устройства вал сервопривода будет плавно разгоняться то в одну сторону, то в другую.

Считывание угла сервопривода

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

Для теста сыграем в игру «Угадай угол». Будем включать сервопривод на максимальной скорости на случайный промежуток времени и после остановки измерять текущий угол вала.

Схема устройства

Код для Arduino IDE

servo-pdm-continuous-rotation-speed-feedback.ino
// Библиотека с расширенными функциями для работы с Servo
#include <AmperkaServo.h>
 
// Создаём объект для работы с сервомоторами
AmperkaServo servo;
 
// Задаём имя пина, к которому подключён сервопривод
constexpr uint8_t SERVO_PIN = 9;
// Задаём имя пина FB, к которому подключён сервопривод
constexpr uint8_t SERVO_PIN_FB = A5;
 
void setup() {
  // Открываем Serial-порт
  Serial.begin(9600);
  // Подключаем сервомотор
  // servo.attach(SERVO_PIN);
  // Подключаем сервомотор с расширенными параметрами
  // Советуем использовать именно этот вариант для точной настройки мотора  
  // servo.attach(pin, minPulseWidth, maxPulseWidth);
  // - pin: номер пина, к которому подключён сервопривод
  // - minPulseWidth: минимальная ширина импульса, соответствующая 
  // максимальной скорости по часовой CW или против часовой CCW стрелки.
  // Опциональный и по умолчанию стоит 544 мкс.
  // - maxPulseWidth: максимальная ширина импульса, соответствующая 
  // максимальной скорости по часовой CW или против часовой CCW стрелки.
  // Опциональный и по умолчанию стоит 2400 мкс.
  // - minAngle: минимальный угол поворота сервопривода. 
  // Опциональный и по умолчанию стоит 0°.
  // Для сервопривода постоянного вращения установите 0°.
  // - maxAngle: максимальный угол поворота сервопривода. 
  // Опциональный и по умолчанию стоит 180°.
  // Для сервопривода постоянного вращения установите 360°.
  // Данные возьмите из технических характеристик мотора
  // Если направление вращения окажется противоположным,
  // поменяйте показания импульсов местами
  servo.attach(SERVO_PIN, 544, 2400, 0, 360);
  // Подключаем обратную связь сервомотора
  // servo.attachFB(pinFB, voltageFBCW, voltageFBCCW);
  // - pinFB: номер пина, к которому подключён сигнал обратной связи от сервопривода
  // - voltageFBCW: напряжение обратной связи в крайнем положении по часовой стрелке CW
  // - voltageFBCCW: напряжение обратной связи в крайнем положении против часовой стрелки CCW
  // Данные возьмите из технических характеристик мотора
  servo.attachFB(SERVO_PIN_FB, 0, 3.3);
}
 
void loop() {
  // Задаём максимальную скорость вращения сервопривода по часовой стрелке CW
  servo.writeSpeed(255);
  // Ждём от 1 до 5 секунд
  delay(random(1000, 5000));
  // Останавливаем сервопривод
  servo.writeSpeed(0);
  // Ждём одну секунду для полной остановки мотора
  delay(1000);
  // Считываем и выводим текущий угол сервопривода
  Serial.println(servo.readAngleFB());
  // Ждём одну секунду для просмотра результата
  delay(1000);
}

После прошивки устройства вал сервопривода будет вращаться случайный промежуток времени и выводить в Serial-порт угол после остановки.

Примеры работы для Espruino

Мозгом для проекта выступит платформа из семейства Espruino, например Iskra JS.

Подключение и настройка

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

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

Схема устройства

Для быстрой сборки и отладки устройства рекомендуем взять плату расширения Troyka Shield, которая надевается сверху на Iskra JS методом бутерброда.

Программная настройка

Настройте плату Iskra JS в среде Espruino Web IDE.

Вращение вала

Для старта протестируем мотор в трёх режимах вращения:

  • Максимальная скорость по часовой стрелке (CW).
  • Пауза — вал мотора стоит на месте.
  • Максимальная скорость против часовой стрелки (CCW).

Код для Espruino IDE

servo-pdm-continuous-rotation-speed.js
// Подключаем сервопривод к пину 9 через библиотеку Servo
var servo = require("@amperka/servo").connect(P9);
 
// Создаём таймер, тикающий каждые 3 секунды
var timerServo = require("@amperka/timer").create(3);
 
// Задаём максимальную скорость сервопривода
// По часовой и против часовой стрелки
var SPEED_MAX_CW = 544;
var SPEED_MAX_CCW = 2400;
var SPEED_STOP = (SPEED_MAX_CW + SPEED_MAX_CCW) / 2;
 
// Скорость вращения сервопривода
var speed = SPEED_MAX_CW;
 
// Функция вращения сервопривода
function startServo() {
  servo.write(speed,'us');
  // Запускаем таймер
  timerServo.run();
}
 
// Подписываемся на событие таймера
timerServo.on("tick", function () {
  // Останавливаем сервопривод
  servo.write(SPEED_STOP,'us');
  // Запускаем мотор в противоположную сторону через одну секунду
  setTimeout(function () {
    if (speed == SPEED_MAX_CW) {
      speed = SPEED_MAX_CCW;
    } else if (speed == SPEED_MAX_CCW) {
      speed = SPEED_MAX_CW;
    }
    startServo();
  }, 1000);
});
 
// Выполняем событие таймера немедленно
timerServo.tick();

После прошивки устройства вал сервопривода будет сначала вращаться с максимальной скоростью по часовой стрелке, сделает паузу, а затем начнёт вращение против часовой стрелки.

Вращение вала с плавным разгоном

Усложним задачу. Сделаем плавный разгон вала по очереди в каждую сторону:

  • Плавный разгон до максимальной скорости по часовой стрелке (CW).
  • Пауза — вал мотора стоит на месте.
  • Плавный разгон до максимальной скорости против часовой стрелки (CCW).

Код для Espruino IDE

servo-pdm-continuous-rotation-speed-sweep.js
// Подключаем сервопривод к пину 9 через библиотеку Servo
var servo = require("@amperka/servo").connect(P9);
 
// Задаём максимальную скорость сервопривода
// По часовой и против часовой стрелки
var SPEED_MAX_CW = 544;
var SPEED_MAX_CCW = 2400;
var SPEED_STOP = (SPEED_MAX_CW + SPEED_MAX_CCW) / 2;
 
// Переменная для хранения ID анимации
var idAnimServo;
 
// Создаём объект анимация
// для плавного изменения параметров вращения мотора
var animServo = require("@amperka/animation")
  .create({
    // Начальное значение скорости
    from: SPEED_STOP,
    // Конечное значение скорости
    to: SPEED_MAX_CW,
    // Продолжительность полного перехода
    // за 3 секунды мотор пройдёт диапазон значений от 0 до 180
    duration: 3,
    // Шаг обновления: каждые 20 мс
    updateInterval: 0.02,
  })
  .queue({
    // Начальное значение скорости
    from: SPEED_STOP,
    // Конечное значение скорости
    to: SPEED_MAX_CCW,
    // Продолжительность полного перехода
    // за 3 секунды мотор пройдёт диапазон значений от 180 до 0
    duration: 3,
  });
 
// Запускаем анимацию
idAnimServo = animServo.play();
 
// Обработчик анимации
animServo.on("update", function (val) {
  servo.write(val,'us');
  // Если анимация закончилась,
  // Запускаем её повторно
  if (idAnimServo._intervalID == null) {
    idAnimServo = animServo.play();
  }
});

После прошивки устройства вал сервопривода будет плавно разгоняться то в одну сторону, то в другую.

Примеры работы для Raspberry Pi

Мозгом для проекта выступит микрокомпьютер Raspberry Pi 4 Model B.

Подключение и настройка

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

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

Схема устройства

Для быстрой сборки и отладки устройства рекомендуем взять плату расширения Troyka HAT, которая надевается сверху на Raspberry Pi методом бутерброда.

Имена пинов на Troyka HAT относятся к нумерации Wiring Pi, которая отличается от стандартной нумерации BCM/GPIO одноплатника Raspberry Pi. Подробности смотрите в распиновке Troyka HAT и нумерации в библиотеке.

Программная настройка

  1. Для работы примеров понадобится модуль Servo из штатной библиотеки GPIO Zero.

Вращение вала

Для старта протестируем мотор в трёх режимах вращения:

  • Максимальная скорость по часовой стрелке (CW).
  • Пауза — вал мотора стоит на месте.
  • Максимальная скорость против часовой стрелки (CCW).

Код для Python

servo-pdm-continuous-rotation-speed.py
# Библиотека для работы с сервоприводами
from gpiozero import Servo
# Библиотека для работы со временем
from time import sleep
 
# Создаём объект для работы с сервоприводом
# - pin: номер пина, к которому подключён сервопривод
# WPIx, нумерация WiringPi, где x — номер пина
# GPIOx, нумерация GPIO/BCM, где x — номер пина
# - min_pulse_width: ширина импульса, соответствующая
# максимальной скорости по часовой стрелке CW
# Опциональный и по умолчанию стоит: 0.001 c = 1000 мкс
# - max_pulse_width: ширина импульса, соответствующая
# максимальной скорости против часовой стрелки CCW
# Опциональный и по умолчанию стоит: 0.002 c = 2000 мкс
# Подробности нумерации: https://gpiozero.readthedocs.io/en/stable/recipes.html#pin-numbering
# Каждая модель сервопривода имеет свои характеристики
servo = Servo("WPI6", min_pulse_width=0.000544, max_pulse_width=0.0024)
 
while True:
    # Задаём максимальную скорость вращения сервопривода по часовой стрелке
    servo.min()
    # Ждём 1 секунду
    sleep(1)
    # Останавливаем сервопривод
    servo.mid()
    # Ждём 1 секунду
    sleep(1)
    # Задаём максимальную скорость вращения сервопривода против часовой стрелки
    servo.max()
    # Ждём 1 секунду
    sleep(1)
    # Останавливаем сервопривод
    servo.mid()
    # Ждём 1 секунду
    sleep(1)

После прошивки устройства вал сервопривода будет сначала вращаться с максимальной скоростью по часовой стрелке, сделает паузу, а затем начнёт вращение против часовой стрелки.

Вращение вала с плавным разгоном

Усложним задачу. Сделаем плавный разгон вала по очереди в каждую сторону:

  • Плавный разгон до максимальной скорости по часовой стрелке (CW).
  • Пауза — вал мотора стоит на месте.
  • Плавный разгон до максимальной скорости против часовой стрелки (CCW).

Код для Python

servo-pdm-continuous-rotation-speed-sweep.py
# Библиотека для работы с сервоприводами
from gpiozero import Servo
from time import sleep
 
# Библиотека для расширенных методов обработки чисел
import numpy as np
 
# Создаём объект для работы с сервоприводом
# - pin: номер пина, к которому подключён сервопривод
# WPIx, нумерация WiringPi, где x — номер пина
# GPIOx, нумерация GPIO/BCM, где x — номер пина
# - min_pulse_width: ширина импульса, соответствующая
# максимальной скорости по часовой стрелке CW
# Опциональный и по умолчанию стоит: 0.001 c = 1000 мкс
# - max_pulse_width: ширина импульса, соответствующая
# максимальной скорости против часовой стрелки CCW
# Опциональный и по умолчанию стоит: 0.002 c = 2000 мкс
# Подробности нумерации: https://gpiozero.readthedocs.io/en/stable/recipes.html#pin-numbering
# Каждая модель сервопривода имеет свои характеристики
servo = Servo("WPI6", min_pulse_width=0.000544, max_pulse_width=0.0024)
 
# Задаём максимальную скорость вращения сервопривода
# По часовой (CW) и против (CCW) часовой стрелки
SPEED_MAX_CW = -1
SPEED_MAX_CCW = 1
SPEED_STOP = 0
 
# Шаг сервопривода
step_angle_servo = 0.005
 
while True:
    # Перебираем значения скорости вала сервы от 0 до максимального по часовой стрелке
    for val in np.arange(SPEED_STOP, SPEED_MAX_CW, -step_angle_servo):
        servo.value = val
        sleep(0.02)
    # Останавливаем сервопривод
    servo.value = SPEED_STOP
    # Ждём 1 секунду
    sleep(1)
    # Перебираем значения скорости вала сервы от 0 до максимального против часовой стрелки
    for val in np.arange(SPEED_STOP, SPEED_MAX_CCW, step_angle_servo):
        servo.value = val
        sleep(0.02)
    # Останавливаем сервопривод
    servo.value = SPEED_STOP
    # Ждём 1 секунду
    sleep(1)

После прошивки устройства вал сервопривода будет плавно разгоняться то в одну сторону, то в другую.

Библиотеки

Полезные статьи