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

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

Сервоприводы с удержанием угла используются для моделирования движения различных механизмов: Привод с лёгкостью сможет открывать/закрывать заслонки для создания кормушки домашнему питомцу или для проектирования маленького тайника в квеструме. А ещё такие сервомоторы дадут возможность вашему роботу управлять руками или вращать головой.

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

Модель Форм-фактор Сигнал управления Обратная связь Назначение Внутренний интерфейс Диапазон вращения
Feetech FS90 / Документация Микро PDM Нет Удержание угла Аналоговый 0–180°
Feetech FT90B / Документация Микро PDM Нет Удержание угла Цифровой 0–180°
Feetech FS90-FB / Документация Микро PDM Да Удержание угла Аналоговый 0–180°
Feetech FS0403-FB / Документация Микро PDM Да Удержание угла Аналоговый 0–180°
Feetech FS5106B Стандарт PDM Нет Удержание угла Аналоговый 0–180°
Feetech FS5109M Стандарт PDM Нет Удержание угла Аналоговый 0–180°
Feetech FT5519M Стандарт PDM Да Удержание угла Цифровой 180°
Feetech FB5118M / Документация Стандарт PDM Да Удержание угла Цифровой 0–300°
Feetech FT6335M / Документация Стандарт PDM Нет Удержание угла Цифровой 0–360°

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

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

  • Если длительность разная → электромотор включается. Направление вала зависит от знака разности импульсов.
  • Если длины импульсов равны → электромотор останавливается.

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

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

Период импульсов Ширина импульса Положение сервопривода Описание
20 мс 544 мкс Максимальное положение вала по часовой стрелке CW
20 мс 1540 мкс 90° Нейтральное положение вала
20 мс 2400 мкс 180° Максимальное положение вала против часовой стрелки CCW

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

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

Часто способ управления сервоприводами называют 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

Мозгом для проекта выступит платформа Arduino Uno.

В качестве тестового сервопривода возьмём Feetech FS90. Примеры совместимы со всеми серво PDM с удержанием угла, необходимо только внести в программу характеристики конкретно вашего мотора.

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

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

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

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

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

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

Смена положения угла

Для старта протестируем мотор в положениях 0, 90 и 180 градусов.

Код для Arduino IDE

servo-pdm-standard-switch.ino
// Библиотека с расширенными функциями для работы с Servo
#include <AmperkaServo.h>
 
// Создаём объект для работы с сервомоторами
AmperkaServo servo;
 
// Задаём имя пина, к которому подключён сервопривод
constexpr uint8_t SERVO_PIN = 9;
 
void setup() {
  // Подключаем сервомотор
  // servo.attach(SERVO_PIN);
  // Подключаем сервомотор с расширенными параметрами
  // Советуем использовать именно этот вариант для точной настройки мотора  
  // servo.attach(pin, minPulseWidth, maxPulseWidth, minAngle, maxAngle);
  // - pin: номер пина, к которому подключён сервопривод
  // - minPulseWidth: ширина импульса, соответствующая минимальному углу поворота. 
  // Опциональный и по умолчанию стоит 544 мкс.
  // - maxPulseWidth: ширина импульса, соответствующая максимальному углу поворота. 
  // Опциональный и по умолчанию стоит 2400 мкс.
  // - minAngle: минимальный угол поворота сервопривода. 
  // Опциональный и по умолчанию стоит 0°.
  // - maxAngle: максимальный угол поворота сервопривода. 
  // Опциональный и по умолчанию стоит 180°.
  // Данные возьмите из технических характеристик мотора
  servo.attach(SERVO_PIN, 544, 2400, 0, 180);
}
 
void loop() {
  // Устанавливаем минимальный угол
  servo.writeAngle(servo.getMinAngle());
  // Ждём 1 секунду
  delay(1000);
  // Устанавливаем среднее положение
  servo.writeAngle(servo.getMidAngle());
  // Ждём 1 секунду
  delay(1000);
  // Устанавливаем максимальный угол
  servo.writeAngle(servo.getMaxAngle());
  // Ждём 1 секунду
  delay(1000);
  // Устанавливаем среднее положение
  servo.writeAngle(servo.getMidAngle());
  // Ждём 1 секунду
  delay(1000);
}

После прошивки устройства вал сервопривода будет поворачиваться на 0, 90 и 180 градусов.

Плавная смена положения угла

Усложним задачу. Сделаем плавное перемещение вала от 0 до 180 градусов.

Код для Arduino IDE

servo-pdm-standard-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, minAngle, maxAngle);
  // - pin: номер пина к которому подключён сервопривод
  // - minPulseWidth: ширина импульса, соответствующая минимальному углу поворота. 
  // Опциональный и по умолчанию стоит 544 мкс.
  // - maxPulseWidth: ширина импульса, соответствующая максимальному углу поворота. 
  // Опциональный и по умолчанию стоит 2400 мкс.
  // - minAngle: минимальный угол поворота сервопривода. 
  // Опциональный и по умолчанию стоит 0°.
  // - maxAngle: максимальный угол поворота сервопривода. 
  // Опциональный и по умолчанию стоит 180°.
  // Данные возьмите из технических характеристик мотора
  servo.attach(SERVO_PIN, 544, 2400, 0, 180);
}
 
void loop() {
  // Перебираем значения угла сервы от min до max
  for (int pos = servo.getMinAngle(); pos <= servo.getMaxAngle(); pos++) {
    // Отправляем текущий угол на серво
    servo.writeAngle(pos);
    // Ждём 20 мс
    delay(20);
  }
  // Перебираем значения угла сервы от max до min
  for (int pos = servo.getMaxAngle(); pos >= servo.getMinAngle(); pos--) {
    // Отправляем текущий угол на серво
    servo.writeAngle(pos);
    // Ждём 20 мс
    delay(20);
  }
}

После прошивки устройства вал сервопривода будет плавно перемещаться от 0 до 180 градусов и обратно.

Смена положения угла двух сервоприводов

Добавим к проекту второй сервопривод.

Будем вращать по очереди вал двух моторов в положения 0, 90 и 180 градусов.

Код для Arduino IDE

servo-pdm-standard-sweep-multiple.ino
// Библиотека с расширенными функциями для работы с Servo
#include <AmperkaServo.h>
 
// Создаём объект для работы с первым сервомотором
AmperkaServo servoOne;
// Создаём объект для работы со вторым сервомотором
AmperkaServo servoTwo;
 
// Задаём имя пина, к которому подключён первый сервопривод
constexpr uint8_t SERVO_PIN_ONE = 9;
// Задаём имя пина, к которому подключён второй сервопривод
constexpr uint8_t SERVO_PIN_TWO = 11;
 
void setup() {
  // Подключаем первый и второй сервомотор
  // servoOne.attach(SERVO_PIN_ONE);
  // servoTwo.attach(SERVO_PIN_TWO);
  // Подключаем сервомотор с расширенными параметрами
  // Советуем использовать именно этот вариант для точной настройки мотора  
  // servo.attach(pin, minPulseWidth, maxPulseWidth, minAngle, maxAngle);
  // - pin: номер пина, к которому подключён сервопривод
  // - minPulseWidth: ширина импульса, соответствующая минимальному углу поворота. 
  // Опциональный и по умолчанию стоит 544 мкс.
  // - maxPulseWidth: ширина импульса, соответствующая максимальному углу поворота. 
  // Опциональный и по умолчанию стоит 2400 мкс.
  // - minAngle: минимальный угол поворота сервопривода. 
  // Опциональный и по умолчанию стоит 0°.
  // - maxAngle: максимальный угол поворота сервопривода. 
  // Опциональный и по умолчанию стоит 180°.
  // Данные возьмите из технических характеристик мотора
  servoOne.attach(SERVO_PIN_ONE, 544, 2400, 0, 180);
  servoTwo.attach(SERVO_PIN_TWO, 544, 2400, 0, 180);
 
  // Устанавливаем минимальный угол первого мотора
  servoOne.writeAngle(servoOne.getMinAngle());
  // Устанавливаем минимальный угол второго мотора
  servoTwo.writeAngle(servoTwo.getMinAngle());
}
 
void loop() {
  // Устанавливаем среднее положение первого мотора
  servoOne.writeAngle(servoOne.getMidAngle());
  // Ждём 1 секунду
  delay(1000);
  // Устанавливаем максимальный угол первого мотора
  servoOne.writeAngle(servoOne.getMaxAngle());
  // Ждём 1 секунду
  delay(1000);
  // Устанавливаем среднее положение первого мотора
  servoOne.writeAngle(servoOne.getMidAngle());
  // Ждём 1 секунду
  delay(1000);
  // Устанавливаем минимальный угол первого мотора
  servoOne.writeAngle(servoOne.getMinAngle());
  // Ждём 1 секунду
  delay(1000);
 
  // Устанавливаем среднее положение второго мотора
  servoTwo.writeAngle(servoTwo.getMidAngle());
  // Ждём 1 секунду
  delay(1000);
  // Устанавливаем максимальный угол второго мотора
  servoTwo.writeAngle(servoTwo.getMaxAngle());
  // Ждём 1 секунду
  delay(1000);
  // Устанавливаем среднее положение второго мотора
  servoTwo.writeAngle(servoTwo.getMidAngle());
  // Ждём 1 секунду
  delay(1000);
  // Устанавливаем минимальный угол второго мотора
  servoTwo.writeAngle(servoTwo.getMinAngle());
  // Ждём 1 секунду
  delay(1000);
}

После прошивки устройства вал каждого сервопривода будет по очереди поворачиваться на 0, 90 и 180 градусов.

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

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

Для теста повторим штатный пример для плавного перемещения сервопривода от 0 до 180 градусов и параллельно будем считывать текущий угол вала мотора.

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

Код для Arduino IDE

servo-pdm-standard-sweep-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, minAngle, maxAngle);
  // - pin: номер пина, к которому подключён сервопривод
  // - minPulseWidth: ширина импульса, соответствующая минимальному углу поворота. 
  // Опциональный и по умолчанию стоит 544 мкс.
  // - maxPulseWidth: ширина импульса, соответствующая максимальному углу поворота. 
  // Опциональный и по умолчанию стоит 2400 мкс.
  // - minAngle: минимальный угол поворота сервопривода. 
  // Опциональный и по умолчанию стоит 0°.
  // - maxAngle: максимальный угол поворота сервопривода. 
  // Опциональный и по умолчанию стоит 180°.
  // Данные возьмите из технических характеристик мотора
  servo.attach(SERVO_PIN, 544, 2400, 0, 180);
  // Подключаем сервомотор к обратной связи
  // servo.attachFB(pinFB, voltageFBCW, voltageFBCCW);
  // - pinFB: номер пина, к которому подключён сигнал обратной связи от сервопривода
  // - voltageFBCW: напряжение обратной связи в крайнем положении по часовой стрелке CW
  // - voltageFBCCW: напряжение обратной связи в крайнем положении против часовой стрелки CCW
  // Данные возьмите из технических характеристик мотора
  servo.attachFB(SERVO_PIN_FB, 2.3, 0.5);
}
 
void loop() {
  // Перебираем значения угла сервы от min до max
  for (int pos = servo.getMinAngle(); pos <= servo.getMaxAngle(); pos++) {
    // Отправляем текущий угол на серво
    servo.writeAngle(pos);
    // Ждём 20 мс
    delay(20);
    // Считываем и выводим текущий угол сервопривода
    Serial.println(servo.readAngleFB());
  }
  // Перебираем значения угла сервы от max до min
  for (int pos = servo.getMaxAngle(); pos >= servo.getMinAngle(); pos--) {
    // Отправляем текущий угол на серво
    servo.writeAngle(pos);
    // Ждём 20 мс
    delay(20);
    // Считываем и выводим текущий угол сервопривода
    Serial.println(servo.readAngleFB());
  }
}

После прошивки устройства вал сервопривода будет плавно перемещаться от 0 до 180 градусов и обратно. И параллельно в консоли будет выводиться текущий угол вала.

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

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

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

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

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

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

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

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

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

Смена положения угла

Для старта протестируем мотор в положениях 0, 90 и 180 градусов.

Код для Espruino IDE

servo-pdm-standard-switch.js
// Подключаем сервопривод к пину 9 через библиотеку Servo
var servo = require("@amperka/servo").connect(P9);
 
// Создаём таймер, тикающий каждые 1 секунды
var timerServo = require("@amperka/timer").create(3);
 
// Задаём минимальный и максимальный угол поворота
var MIN_ANGLE = 0;
var MAX_ANGLE = 180;
var MID_ANGLE = (MIN_ANGLE + MAX_ANGLE) / 2;
 
// Переменная для хранения текущей позиции сервомотора
var pos = MIN_ANGLE;
 
// Функция вращения сервопривода
function startServo() {
  servo.write(pos);
  // Запускаем таймер
  timerServo.run();
}
 
// Подписываемся на событие таймера
timerServo.on("tick", function () {
  // Неважно, какое было прошлое состояние угла
  // Устанавливаем среднее положение
  servo.write(MID_ANGLE);
  // Запускаем мотор в противоположную сторону 
  // От прошлого положения через одну секунду
  setTimeout(function () {
    if (pos == MIN_ANGLE) {
      pos = MAX_ANGLE;
    } else if (pos == MAX_ANGLE) {
      pos = MIN_ANGLE;
    }
    startServo();
  }, 1500);
});
 
// Запускаем функцию вращения мотора
startServo();

После прошивки устройства вал сервопривода будет поворачиваться на 0, 90 и 180 градусов.

Плавная смена положения угла

Усложним задачу. Сделаем плавное перемещение вала от 0 до 180 градусов.

Код для Espruino IDE

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

После прошивки устройства вал сервопривода будет плавно перемещаться от 0 до 180 градусов и обратно.

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

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

Для теста повторим штатный пример для плавного перемещения сервопривода от 0 до 180 градусов и параллельно будем считывать текущий угол вала мотора.

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

Код для Espruino IDE

servo-pdm-standard-sweep-feedback.js
// Подключаем сервопривод к пину 9 через библиотеку Servo
var servo = require("@amperka/servo").connect(P9);
// Подключаем сервопривод к пину A5 для считывания данных угла поворота
var sensor = require('@amperka/pot').connect(A5);
 
// Переменная для хранения ID анимации
var idAnimServo;
 
// Создаём объект анимации
// для плавного изменения параметров вращения мотора
var animServo = require("@amperka/animation")
  .create({
    // Начальное значение в градусах
    from: 0,
    // Конечное значение в градусах
    to: 180,
    // Продолжительность полного перехода
    // за 3 секунд мотор пройдёт диапазон значений от 0 до 180
    duration: 3,
    // Шаг обновления: каждые 20 мс
    updateInterval: 0.02,
  })
  .queue({
    // Начальное значение в градусах
    from: 180,
    // Конечное значение в градусах
    to: 0,
    // Продолжительность полного перехода
    // за 3 секунд мотор пройдёт диапазон значений от 180 до 0
    duration: 3,
  });
 
// Запускаем анимацию
idAnimServo = animServo.play();
 
// Обработчик анимации
animServo.on("update", function (val) {
  servo.write(val);
  // Если анимация закончилась,
  // Запускаем её повторно
  if (idAnimServo._intervalID == null) {
    idAnimServo = animServo.play();
  }
});
 
// Каждые 100 мс
// Считываем и выводим текущий угол сервопривода
setInterval(function() {
  value = sensor.read();
  valueAngle = map(value, 0.11, 0.70, 0, 180);
  console.log('Servo value =', Math.round(valueAngle), 'degrees');
}, 100);
 
// Функция преобразования одного диапазона в другой
function map(x, minIn, maxIn, minOut, maxOut) {
  return (x - minIn) * (maxOut - minOut) / (maxIn - minIn) + minOut;
}

После прошивки устройства вал сервопривода будет плавно перемещаться от 0 до 180 градусов и обратно. И параллельно в консоли будет выводиться текущий угол вала.

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

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

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

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

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

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

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

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

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

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

Смена положения угла

Для старта протестируем мотор в положениях 0, 90 и 180 градусов.

Код для Python

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

После прошивки устройства вал сервопривода будет поворачиваться на 0, 90 и 180 градусов.

Плавная смена положения угла

Усложним задачу. Сделаем плавное перемещение вала от 0 до 180 градусов.

Код для Python

servo-pdm-standard-sweep.py
# Библиотека для работы с сервоприводами
from gpiozero import AngularServo
from time import sleep
 
# Создаём объект для работы с сервоприводом
# - pin: номер пина, к которому подключён сервопривод
# WPIx, нумерация WiringPi, где x — номер пина
# GPIOx, нумерация GPIO/BCM, где x — номер пина 
# - min_angle: минимальный угол поворота сервопривода
# Опциональный и по умолчанию стоит: -90°
# - min_angle: максимальный угол поворота сервопривода
# Опциональный и по умолчанию стоит: 90°
# - min_pulse_width: ширина импульса, соответствующая минимальному углу поворота сервопривода
# Опциональный и по умолчанию стоит: 0.001 c = 1000 мкс
# - max_pulse_width: ширина импульса, соответствующая максимальному углу поворота сервопривода
# Опциональный и по умолчанию стоит: 0.002 c = 2000 мкс
# Подробности нумерации: https://gpiozero.readthedocs.io/en/stable/recipes.html#pin-numbering
# Каждая модель сервопривода имеет свои характеристики
servo = AngularServo(
    "WPI6", min_angle=0, max_angle=180, min_pulse_width=0.000544, max_pulse_width=0.0024
)
 
# Шаг сервопривода
step = 1
 
while True:
    for val in range(servo.min_angle, servo.max_angle, step):
        servo.angle = val
        sleep(0.02)
    for val in range(servo.max_angle, servo.min_angle, -step):
        servo.angle = val
        sleep(0.02)

После прошивки устройства вал сервопривода будет плавно перемещаться от 0 до 180 градусов и обратно.

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

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

Для теста повторим штатный пример для плавного перемещения сервопривода от 0 до 180 градусов и параллельно будем считывать текущий угол вала мотора.

Поскольку в микрокомпьютерах Raspberry Pi отсутствует АЦП, то для работы примера ниже понадобится плата расширения Troyka HAT.

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

Код для Python

servo-pdm-standard-sweep-feedback.py
# Библиотека для работы с сервоприводами
from gpiozero import AngularServo
 
# Библиотека для работы с платой Troyka HAT
import troykahat
 
# Библиотека для работы со временем
from time import sleep
 
# Создаём объект для работы с сервоприводом
# - pin: номер пина, к которому подключён сервопривод
# WPIx, нумерация WiringPi, где x — номер пина
# GPIOx, нумерация GPIO/BCM, где x — номер пина
# - min_angle: минимальный угол поворота сервопривода
# Опциональный и по умолчанию стоит: -90°
# - min_angle: максимальный угол поворота сервопривода
# Опциональный и по умолчанию стоит: 90°
# - min_pulse_width: ширина импульса, соответствующая минимальному углу поворота сервопривода
# Опциональный и по умолчанию стоит: 0.001 c = 1000 мкс
# - max_pulse_width: ширина импульса, соответствующая максимальному углу поворота сервопривода
# Опциональный и по умолчанию стоит: 0.002 c = 2000 мкс
# Подробности нумерации: https://gpiozero.readthedocs.io/en/stable/recipes.html#pin-numbering
# Каждая модель сервопривода имеет свои характеристики
servo = AngularServo(
    "WPI6", min_angle=0, max_angle=180, min_pulse_width=0.000544, max_pulse_width=0.0024
)
 
# Назначаем константное имя пину 5 из группы "Analog IO",
# Подключите потенциометр к этому пину.
PIN_AP_SERVO_FEEDBACK = 5
# Создаём объект ap для работы с пинами,
# помеченными как «Analog IO» на плате Troyka HAT.
# Это пины, подключенные к встроенному на плате I²C-расширителю
# на микроконтроллере STM32F030F4P6.
ap = troykahat.analog_io()
 
# Шаг сервопривода
step_angle_servo = 1
 
 
def remap(x, min_in, max_in, min_out, max_out):
    return (x - min_in) * (max_out - min_out) / (max_in - min_in) + min_out
 
 
# Функция для считывания «сырых» данных положения сервопривода
# и перевода их в угол поворота
def read_position_angle(pin):
    position_raw = ap.analogRead(pin)
    position_angle = round(remap(position_raw, 0.12, 0.7, 0, 180))
    return position_angle
 
 
while True:
    for val in range(servo.min_angle, servo.max_angle, step_angle_servo):
        servo.angle = val
        print(f"Servo position = {read_position_angle(PIN_AP_SERVO_FEEDBACK)} degrees")
        sleep(0.02)
    for val in range(servo.max_angle, servo.min_angle, -step_angle_servo):
        servo.angle = val
        print(f"Servo position = {read_position_angle(PIN_AP_SERVO_FEEDBACK)} degrees")
        sleep(0.02)

После прошивки устройства вал сервопривода будет плавно перемещаться от 0 до 180 градусов и обратно. И параллельно в консоли будет выводиться текущий угол вала.

Библиотеки

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