Найти в Дзене
Stepan.Burmistrov

В мире сервоприводов…

Что такое Сервопривод? Сервопривод — это тип привода в робототехнике, механике и элементах автоматизации, предназначенный для точного управления положением поворота какой-либо детали. Он состоит из мотора, системы редукторов и управляющей электроники с потенциометром для обратной связи о положении. Как работает сервопривод Сервоприводы работают, преобразуя электрическую энергию, под управлением ШИМ-сигнала в механическое движение. Серво содержит небольшой электродвигатель, который через систему редукторов соединен с выходным валом. Внутри сервопривода находится потенциометр, который связан с выходным валом. Он измеряет угол поворота вала и отправляет эту информацию обратно на управляющую электронику, обеспечивая точное позиционирование.
Управление сервоприводом происходит через ШИМ (широтно-импульсную модуляцию). ШИМ-сигнал определяет угол поворота вала сервопривода. Что такое ШИМ? ШИМ (широтно-импульсная модуляция) — это метод управления мощностью, подаваемой на электронное устройство
Оглавление

Что такое Сервопривод?

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

Различные виды сервоприводов1 из 6
Различные виды сервоприводов1 из 6

Как работает сервопривод

Сервоприводы работают, преобразуя электрическую энергию, под управлением ШИМ-сигнала в механическое движение. Серво содержит небольшой электродвигатель, который через систему редукторов соединен с выходным валом. Внутри сервопривода находится потенциометр, который связан с выходным валом. Он измеряет угол поворота вала и отправляет эту информацию обратно на управляющую электронику, обеспечивая точное позиционирование.
Управление сервоприводом происходит через ШИМ (широтно-импульсную модуляцию). ШИМ-сигнал определяет угол поворота вала сервопривода.

Что такое ШИМ?

ШИМ (широтно-импульсная модуляция) — это метод управления мощностью, подаваемой на электронное устройство, путем изменения длительности импульсов электрического сигнала.

-2

Как работает ШИМ

  1. Импульсы постоянной частоты: В ШИМ используются импульсы постоянной частоты, но изменяется их длительность (ширина). Частота ШИМ-сигнала обычно измеряется в герцах (Гц).
  2. Скважность: Основным параметром ШИМ является скважность, которая определяется как отношение времени включения сигнала к общему периоду цикла. Скважность может варьироваться от 0% (сигнал всегда выключен) до 100% (сигнал всегда включен).

Использование ШИМ для управления сервоприводом

При управлении сервоприводами ШИМ-сигнал используется для задания угла поворота вала.

  1. Стандартные ШИМ-параметры для сервоприводов: Для большинства моделей сервоприводов стандартный ШИМ-сигнал имеет частоту 50 Гц (период около 20 мс). Изменение ширины импульса в этом сигнале приводит к изменению угла поворота сервопривода.
  2. Длительность импульса: для управления сервоприводом обычно находится в диапазоне от 1 мс до 2 мс. Импульс длительностью 1 мс обычно заставляет сервопривод повернуться на 0°, 1.5 мс соответствует примерно 90°, а 2 мс — 180°. Эти значения могут варьироваться в зависимости от конкретной модели сервопривода.
  3. Точность управления: зависит от стабильности и точности ШИМ-сигнала. Микроконтроллеры, такие как Arduino, могут генерировать достаточно точные ШИМ-сигналы для большинства приложений.
-3

Для начала, ознакомимся со схемой, которая генерирует ШИМ сигнал без использования микроконтроллера.
https://www.tinkercad.com/things/4wUYzuSpZ9T-ne555-servotester2

-4

Arduino и библиотека Servo.h

В Arduino для работы с сервоприводами используется библиотека Servo.h. Она позволяет легко управлять сервоприводами, отправляя на них ШИМ-сигналы.

Простой подход

-5

Пример простого кода управления сервоприводом в Arduino:

#include <Servo.h>

Servo motor; // создаем объект серво

int button1 = 3; // кнопка 1 подключена к пину 3
int button2 = 4; // кнопка 2 подключена к пину 4
int servoPin = 2; // серво подключено к пину 2

void setup() {
motor.attach(servoPin); // подключаем серво к соответствующему пину
pinMode(button1, INPUT_PULLUP); // настраиваем пин кнопки 1 как вход
pinMode(button2, INPUT_PULLUP); // настраиваем пин кнопки 2 как вход
motor.write(0); // начальное положение серво - 0 градусов
}

void loop() {
if (digitalRead(button1) == LOW) { // если нажата кнопка 1
motor.write(180); // поворачиваем серво на 180 градусов
delay(1000); // задержка для устойчивости
}

if (digitalRead(button2) == LOW) { // если нажата кнопка 2
motor.write(0); // поворачиваем серво в исходное положение 0 градусов
delay(1000); // задержка для устойчивости
}
}

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

Подключение библиотеки и создание сбъекта

#include <Servo.h>
Servo motor;

  • #include <Servo.h>: Эта строка подключает библиотеку Servo.h, которая необходима для управления сервоприводами с помощью Arduino.
  • Servo motor;: Здесь мы создаем объект motor из класса Servo. Этот объект будет использоваться для управления сервоприводом.

Настройка портов (пинов)

int button1 = 3; // кнопка 1 подключена к пину 3
int button2 = 4; // кнопка 2 подключена к пину 4
int servoPin = 2; // серво подключено к пину 2

  • В этой части кода мы определяем пины, к которым подключены кнопки и сервопривод. button1 и button2 это пины для кнопок, а servoPin это пин для сервопривода.

Начальная настройка

void setup() {
motor.attach(servoPin); // подключаем серво к соответствующему пину
pinMode(button1, INPUT_PULLUP); // настраиваем пин кнопки 1 как вход
pinMode(button2, INPUT_PULLUP); // настраиваем пин кнопки 2 как вход
motor.write(0); // начальное положение серво - 0 градусов
}

  • Функция setup() вызывается один раз при запуске или перезагрузке Arduino. Здесь мы настраиваем пины и сервопривод.
  • motor.attach(servoPin);: Присоединяем сервопривод к указанному пину.
  • pinMode(button1, INPUT_PULLUP); и pinMode(button2, INPUT_PULLUP);: Настраиваем пины кнопок как входные, используя внутреннюю подтяжку к питанию.
  • motor.write(0);: Устанавливаем начальное положение сервопривода на 0 градусов.

Почему используется подтяжка к питанию INPUT_PULLUP?

Использование внутренней подтяжки к питанию упрощает подключение и программирование, так как:

  1. Избавляет от необходимости внешних резисторов: Без подтяжки к питанию, вам бы потребовалось добавить внешний резистор к каждой кнопке для обеспечения стабильного состояния входа, когда кнопка не нажата.
  2. Предотвращает "плавающие" значения: Без подтяжки, входной пин может случайно переключаться между HIGH и LOW из-за электрических помех, что приводит к непредсказуемому поведению.

Основной цикл

void loop() {
if (digitalRead(button1) == LOW) { // если нажата кнопка 1
motor.write(180); // поворачиваем серво на 180 градусов
delay(1000); // задержка для устойчивости
}

if (digitalRead(button2) == LOW) { // если нажата кнопка 2
motor.write(0); // поворачиваем серво в исходное положение 0 градусов
delay(1000); // задержка для устойчивости
}
}

  • void loop() {}: Эта функция выполняется постоянно после setup().
  • if (digitalRead(button1) == LOW) {}: Проверяем, нажата ли кнопка 1. Если да, то выполняется код внутри фигурных скобок.
  • motor.write(180);: Поворачиваем сервопривод на 180 градусов.
  • delay(1000);: Делаем задержку в 1 секунду, чтобы сервопривод успел повернуться и стабилизироваться

Сложности «Простого подхода»

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

Выход с использованием millis()

Для улучшения работы кода можно использовать функцию millis(), которая позволяет выполнять управление сервоприводом без блокировки loop.

Для начала пример всего кода, а дальше разберем его детально:

#include <Servo.h>

Servo motor; // Объект сервопривода

int button1 = 3; // Пин подключения кнопки 1
int button2 = 4; // Пин подключения кнопки 2
int servoPin = 2; // Пин подключения сервопривода
int servoCurrentPos = 0; // Текущая позиция сервопривода
int servoTargetPos = 0; // Целевая позиция сервопривода
uint32_t servoTimer = 0; // Таймер для контроля скорости перемещения сервопривода
int servoDelay = 5; // Задержка между шагами перемещения сервопривода

// Функция для плавного управления положением сервопривода
void servoPosControl() {
// Проверяем, прошло ли достаточно времени с последнего изменения положения
if (millis() - servoTimer > servoDelay) {
int delta = 0; // Разница между текущим и целевым положением
// Определяем направление движения сервопривода
if (servoCurrentPos > servoTargetPos){
delta = -1;
} else if (servoCurrentPos < servoTargetPos){
delta = 1;
}
// Обновляем текущую позицию и время
servoCurrentPos += delta;
servoTimer = millis();
// Устанавливаем новую позицию сервопривода
motor.write(servoCurrentPos);
}
}

void setup() {
motor.attach(servoPin); // Подключаем сервопривод
pinMode(button1, INPUT_PULLUP); // Настраиваем пин кнопки 1 как вход
pinMode(button2, INPUT_PULLUP); // Настраиваем пин кнопки 2 как вход
motor.write(0); // Устанавливаем начальное положение сервопривода
}

void loop() {
servoPosControl(); // Вызываем функцию управления положением сервопривода
// Проверяем состояние кнопок и обновляем целевую позицию сервопривода
if (digitalRead(button1) == LOW) {
servoTargetPos = 180;
}

if (digitalRead(button2) == LOW) {
servoTargetPos = 0;
}
}

Обзор функции servoPosControl()

Функция servoPosControl() в коде Arduino представляет собой эффективный способ управления сервоприводом для достижения плавного движения. Эта функция использует неблокирующий подход с таймерами, что позволяет Arduino выполнять другие задачи во время управления сервоприводом. Давайте подробно разберем, как работает эта функция.

Работа функции

// Функция для плавного управления положением сервопривода
void servoPosControl() {
// Проверяем, прошло ли достаточно времени с последнего изменения положения
if (millis() - servoTimer > servoDelay) {
int delta = 0; // Разница между текущим и целевым положением
// Определяем направление движения сервопривода
if (servoCurrentPos > servoTargetPos){
delta = -1;
} else if (servoCurrentPos < servoTargetPos){
delta = 1;
}
// Обновляем текущую позицию и время
servoCurrentPos += delta;
servoTimer = millis();
// Устанавливаем новую позицию сервопривода
motor.write(servoCurrentPos);
}
}

  1. Проверка времени: Если разница между текущим временем и servoTimer больше servoDelay, значит пришло время обновить положение сервопривода.millis() возвращает количество миллисекунд, прошедших с момента запуска программы.
    servoTimer это переменная, которая хранит время последнего изменения положения сервопривода.
    servoDelay это задержка (в миллисекундах) между последовательными изменениями положения сервопривода.
  2. Определение направления движения:servoCurrentPos это текущее положение сервопривода.
    servoTargetPos это целевое положение сервопривода.
    delta определяет направление движения: уменьшение (-1) или увеличение (+1) текущего положения.
  3. Обновление положения сервопривода:servoCurrentPos += delta;: Обновляем текущее положение сервопривода, прибавляя или вычитая delta.
    servoTimer = millis();: Обновляем время последнего изменения положения.
    motor.write(servoCurrentPos);: Передаем новое положение в сервопривод.

Преимущества использования servoPosControl()

  • Плавность движения: Постепенное изменение положения сервопривода делает его движение более плавным.
  • Неблокирующий код: Использование millis() вместо delay() позволяет избежать блокировки выполнения других задач во время управления сервоприводом.
  • Гибкость: Функция позволяет легко настраивать скорость движения сервопривода, изменяя servoDelay.

Теперь разберем оставшуюся часть кода:

#include <Servo.h>

Servo motor; // Объект сервопривода

int button1 = 3; // Пин подключения кнопки 1
int button2 = 4; // Пин подключения кнопки 2
int servoPin = 2; // Пин подключения сервопривода
int servoCurrentPos = 0; // Текущая позиция сервопривода
int servoTargetPos = 0; // Целевая позиция сервопривода
uint32_t servoTimer = 0; // Таймер для контроля скорости перемещения сервопривода
int servoDelay = 5; // Задержка между шагами перемещения сервопривода

Объявление Переменных:

  • #include <Servo.h>;: Подключаем библиотеку.
  • Servo motor;: Объект класса Servo для управления сервоприводом.
  • int button1 = 3; и int button2 = 4;: Пины, к которым подключены кнопки.
  • int servoPin = 2;: Пин, к которому подключен сервопривод.
  • int servoCurrentPos = 0;: Текущая позиция сервопривода.
  • int servoTargetPos = 0;: Целевая позиция сервопривода.
  • uint32_t servoTimer = 0;: Таймер для контроля времени между обновлениями положения сервопривода.
  • int servoDelay = 5;: Задержка между шагами перемещения сервопривода в миллисекундах.

void setup() {
motor.attach(servoPin); // Подключаем сервопривод
pinMode(button1, INPUT_PULLUP); // Настраиваем пин кнопки 1 как вход
pinMode(button2, INPUT_PULLUP); // Настраиваем пин кнопки 2 как вход
motor.write(0); // Устанавливаем начальное положение сервопривода
}

Действия вsetup():

  • Присоединяет сервопривод к пину и устанавливает пины кнопок как входные с включенной внутренней подтяжкой к питанию.
  • Устанавливает начальное положение сервопривода.

void loop() {
servoPosControl(); // Вызываем функцию управления положением сервопривода
// Проверяем состояние кнопок и обновляем целевую позицию сервопривода
if (digitalRead(button1) == LOW) {
servoTargetPos = 180;
}

if (digitalRead(button2) == LOW) {
servoTargetPos = 0;
}
}

Основной Цикл loop():

  • Непрерывно вызывает servoPosControl(), обеспечивая плавное изменение положения сервопривода.
  • Проверяет состояние кнопок и обновляет servoTargetPos.

Важные замечания!

  • С этим подходом использование delay() в loop() или других функциях может привести к нестабильной работе, так как оно блокирует выполнение программы, включая обновление положения сервопривода.
  • Неблокирующий подход: Использование millis() позволяет избежать блокировки, что критически важно для многозадачных решений в роботах!

Посмотреть проект в TinkerCad и испытать его в деле можно по ссылке:

https://www.tinkercad.com/things/d0hzbpr34DJ-servo-2-metoda

Завершающая часть статьи: преимущества и использование кода для управления массивом сервоприводов

-6

#include <Servo.h> // Подключаем библиотеку Servo для управления сервоприводами

Servo part0; // Создаем объекты сервоприводов
Servo part1;
Servo part2;

Servo servos[] = { part0, part1, part2}; // Массив объектов сервоприводов для удобства управления
int servosCurrentPos[] = {90, 90, 90}; // Массив текущих позиций сервоприводов
int servosTargetPos[] = {90, 90, 90}; // Массив целевых позиций сервоприводов
uint32_t servosTimer[] = {0, 0, 0}; // Таймеры для контроля времени обновления положения каждого сервопривода
int sDelay = 30; // Задержка между обновлениями положения
uint32_t servosDelay[] = {sDelay, sDelay, sDelay}; // Массив задержек для каждого сервопривода


void parseSerialInput() {
// Функция обработки данных, получаемых через Serial Monitor
String inputString = ""; // Строка для хранения входных данных
int inputArray[3]; // Массив для хранения распарсенных значений углов
int inputIndex = 0; // Индекс для перебора элементов массива

// Читаем данные из Serial пока они доступны
while (Serial.available() > 0) {
delay(1); // Небольшая задержка для стабилизации данных
char inChar = Serial.read(); // Читаем символ из Serial

// Проверяем на символ конца строки
if (inChar == '|') {
// Печатаем полученную строку для отладки
Serial.print("Received String: ");
Serial.println(inputString);

// Преобразуем String в массив char для разбора
char tempStr[inputString.length() + 1];
inputString.toCharArray(tempStr, sizeof(tempStr));

// Разбираем строку на отдельные числа
char* ptr = strtok(tempStr, ";");
while (ptr != NULL && inputIndex < 3) {
inputArray[inputIndex] = atoi(ptr);
// Выводим целевое положение для каждого сервопривода
Serial.print("Servo ");
Serial.print(inputIndex);
Serial.print(" Target: ");
Serial.println(inputArray[inputIndex]);
inputIndex++;
ptr = strtok(NULL, ";");
}

// Обновляем целевые позиции сервоприводов
for (int i = 0; i <= 2; i++) {
servosTargetPos[i] = inputArray[i];
}

// Сброс переменных для следующего чтения
inputString = "";
inputIndex = 0;
} else if (isdigit(inChar) || inChar == ';') {
// Если символ является числом или разделителем, добавляем его к строке
inputString += inChar;
}
}
}

void servoPosControl() {
// Функция для плавного управления положением сервоприводов
for (int i = 0; i <= 2; i++) {
// Проверяем, прошло ли достаточно времени с последнего обновления положения
if (millis() - servosTimer[i] > servosDelay[i]) {
// Вычисляем разницу между текущим и целевым положением
int delta = servosCurrentPos[i] == servosTargetPos[i] ? 0 : (servosCurrentPos[i] < servosTargetPos[i] ? 1 : -1);
// Обновляем текущее положение
servosCurrentPos[i] += delta;
// Обновляем таймер
servosTimer[i] = millis();
// Устанавливаем новое положение сервопривода
servos[i].write(servosCurrentPos[i]);
}
}
}


void setup() {
Serial.begin(9600);
part0.attach(2);
part1.attach(3);
part2.attach(4);
}
void loop() {
parseSerialInput(); // Обрабатываем входные данные из Serial Monitor
servoPosControl(); // Управляем положением сервоприводов
}

//Откройте Serial Monitor
//Отправьте строку в формате "90;120;45;|" для установки углов поворота сервоприводов.

Этот код является отличным инструментом для управления несколькими сервоприводами в проектах на Arduino. Для его использования необходимо загрузить код на плату Arduino и подключить сервоприводы к соответствующим пинам. Управление положением сервоприводов осуществляется через Serial Monitor путем отправки команд в формате, например, "90;120;45;|".

Преимущества обработки массива сервоприводов

  1. Плавное управление: функция servoPosControl() обеспечивает плавное изменение положения каждого сервопривода, что предотвращает резкие движения и улучшает точность позиционирования.
  2. Неблокирующая работа: использование millis() вместо delay() позволяет избежать блокировки основного цикла loop, что важно для поддержания отзывчивости системы и возможности параллельного выполнения других задач.
  3. Гибкая настройка: код легко адаптируется под различное количество сервоприводов, достаточно изменить массивы и добавить необходимые объекты Servo.
  4. Удобство управления: функция parseSerialInput() позволяет легко управлять сервоприводами через Serial, что делает процесс интуитивно понятным и удобным для тестирования и прототипирования.
  5. Отладка и мониторинг: встроенные сообщения Serial.print() предоставляют важную информацию для отладки, позволяя пользователю отслеживать процесс обработки команд и движения сервоприводов.

Заключение

Применение данного кода открывает широкие возможности для разработчиков и энтузиастов Arduino в сферах робототехники, автоматизации и интерактивных проектов. Он обеспечивает точное управление несколькими сервоприводами одновременно, что делает его ценным инструментом для множества механизмов!

Конечно же, для вас подготовлен проект в TinkerCad, где можно все испытать!

https://www.tinkercad.com/things/aBrLwNGxIF1-servo-massiv

Удачи и успехов в ваших разработках!