Глава 6. Управление сервоприводом SG90 на Raspberry Pi
В данной главе рассматривается подключение и управление сервоприводом SG90 с использованием библиотеки pigpio на Raspberry Pi Zero 2W. Мы исследуем характеристики SG90, принципы работы сервомашинок и демонстрируем работу через CLI и GUI-приложение.
—
Технические характеристики SG90
Угол поворота: 0–180 градусов
Управление: широтно-импульсная модуляция (ШИМ)
Ширина импульса: от 500 до 2500 микросекунд (для углов от 0 до 180°)
Напряжение питания: 4.8–6 В
—
Подключение
Сервопривод SG90 подключается следующим образом:
VCC (обычно красный провод) — к 5V на Raspberry Pi
GND (обычно коричневый/черный провод) — к GND на Raspberry Pi
Signal (обычно оранжевый/жёлтый провод) — к GPIO17 или GPIO18 (в нашем случае используем GPIO17)
—
Управление через CLI
Для управления сервомотором используется демон pigpiod и утилита pigs. Демон pigpiod должен быть запущен в фоновом режиме, чтобы утилита pigs могла с ним взаимодействовать.
Важное примечание: Демон pigpiod у вас запускается через crontab при загрузке системы, что является надёжным способом обеспечения его работы. Если по какой-то причине он не запущен, команды pigs не будут работать.
Примеры команд для управления сервоприводом из командной строки:
# Если pigpiod не запущен через cron, его можно запустить вручную (обычно не требуется):
# sudo pigpiod -l
pigs s 17 1500 # Установить сервопривод на GPIO17 в центральное положение (примерно 90°)
pigs s 17 1000 # Повернуть сервопривод на GPIO17 влево (примерно ~45°)
pigs s 17 2000 # Повернуть сервопривод на GPIO17 вправо (примерно ~135°)
Диапазон командной ширины импульса для SG90:
500 микросекунд — соответствует минимальному углу (приблизительно 0°)
2500 микросекунд — соответствует максимальному углу (приблизительно 180°)
—
Графический интерфейс
Для более наглядного и интерактивного управления сервомотором мы разработали простое GUI-приложение с использованием библиотеки GTK+.
### Исходный код servo_gui.c
Ниже представлен полный исходный код нашего GTK-приложения для управления сервоприводом, включая подробные комментарии, объясняющие каждую часть:
#include <gtk/gtk.h> // Включаем заголовочный файл для библиотеки GTK+ (для создания графического интерфейса пользователя)
#include <stdlib.h> // Включаем заголовочный файл для стандартных функций, таких как system() и snprintf()
#include <stdio.h> // Включаем заголовочный файл для snprintf() - используется для форматирования строки команды
// --- Функции управления сервоприводом ---
/**
* @brief Устанавливает положение сервопривода, отправляя команду через pigs.
* @param pulsewidth Ширина импульса в микросекундах (обычно от 500 до 2500 для SG90).
* 1500 us обычно соответствует центральному положению.
*/
void set_servo(int pulsewidth) {
char cmd[64]; // Создаем буфер для хранения строки команды.
// Достаточно большой для команды "pigs s 17 2500" и завершающего нуля.
// Форматируем строку команды: "pigs s 17 [pulsewidth]".
// "pigs s" - команда pigs для установки ширины импульса (серво).
// "17" - номер GPIO-пина, к которому подключен сервопривод (GPIO17).
// "%d" - placeholder для значения pulsewidth.
snprintf(cmd, sizeof(cmd), "pigs s 17 %d", pulsewidth);
// Выполняем команду в системной оболочке.
// Это запускает внешний инструмент 'pigs', который управляет сервоприводом.
// Важно: для работы 'pigs' должен быть запущен демон pigpiod, и у пользователя должны быть права.
system(cmd);
}
// --- Функции обратного вызова для GUI (GTK+) ---
/**
* @brief Функция обратного вызова, вызываемая при нажатии одной из кнопок управления сервоприводом.
* @param widget Указатель на виджет, вызвавший событие (в данном случае GtkButton).
* @param data Пользовательские данные, переданные при подключении сигнала (здесь - целевая ширина импульса).
*/
void on_button_clicked(GtkWidget *widget, gpointer data) {
// Преобразуем gpointer (обобщенный указатель) обратно в int (ширину импульса).
int value = GPOINTER_TO_INT(data);
// Устанавливаем положение сервопривода.
set_servo(value);
}
/**
* @brief Функция обратного вызова, вызываемая при изменении положения ползунка (GtkScale).
* @param range Указатель на GtkRange (базовый тип для GtkScale), вызвавший событие.
* @param user_data Пользовательские данные (не используются в данной функции, NULL).
*/
void on_scale_moved(GtkRange *range, gpointer user_data) {
// Получаем текущее значение ползунка.
int value = (int)gtk_range_get_value(range);
// Устанавливаем положение сервопривода на основе значения ползунка.
set_servo(value);
}
// --- Главная функция программы ---
/**
* @brief Точка входа в программу. Инициализирует GTK+, создает графический интерфейс
* и запускает основной цикл обработки событий.
* @param argc Количество аргументов командной строки.
* @param argv Массив строк аргументов командной строки.
* @return Код завершения программы (0 при успешном выполнении, 1 при ошибке).
*/
int main(int argc, char *argv[]) {
// Инициализация библиотеки GTK+. Должна быть вызвана первой для любого GTK-приложения.
gtk_init(&argc, &argv);
// --- Создание главного окна GTK+ ---
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // Создаем новое окно верхнего уровня.
gtk_window_set_title(GTK_WINDOW(window), "Servo SG90 Controller"); // Устанавливаем заголовок окна.
gtk_window_set_default_size(GTK_WINDOW(window), 400, 200); // Устанавливаем размер окна по умолчанию.
// Устанавливаем иконку окна из файла. Путь должен быть абсолютным или относительным к месту запуска.
gtk_window_set_icon_from_file(GTK_WINDOW(window), "/home/Alex/GUI4RPiZ2W/5/servo_gui.png", NULL);
// Подключаем сигнал "destroy" (закрытие окна) к функции gtk_main_quit.
// Это гарантирует, что приложение завершится при закрытии окна.
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
// --- Создание вертикального контейнера (VBox) ---
// GtkBox - это контейнер, который упорядочивает виджеты в одном измерении (вертикально или горизонтально).
GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10); // Создаем вертикальный контейнер с отступом 10 пикселей между дочерними элементами.
gtk_container_add(GTK_CONTAINER(window), vbox); // Добавляем этот контейнер в главное окно.
// --- Создание горизонтального контейнера (HBox) для кнопок ---
GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); // Создаем горизонтальный контейнер с отступом 5 пикселей.
// Упаковываем HBox в VBox. FALSE, FALSE, 0 означает: не растягивать, не заполнять, без дополнительного отступа.
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
// --- Определение данных для кнопок ---
// Это анонимная структура и массив для удобного создания кнопок.
struct {
const char *label; // Текст метки кнопки
int value; // Значение ширины импульса, которое будет отправлено сервоприводу при нажатии кнопки
} buttons[] = {
{"<<", 500}, // Полностью влево
{"<", 1000}, // Немного влево
{"Center", 1500}, // Центр
{">", 2000}, // Немного вправо
{">>", 2500} // Полностью вправо
};
// --- Создание и подключение кнопок ---
for (int i = 0; i < 5; i++) {
GtkWidget *btn = gtk_button_new_with_label(buttons[i].label); // Создаем кнопку с заданной меткой.
// Подключаем сигнал "clicked" кнопки к функции on_button_clicked.
// GINT_TO_POINTER преобразует целочисленное значение (ширину импульса) в указатель,
// который будет передан в on_button_clicked через 'data'.
g_signal_connect(btn, "clicked", G_CALLBACK(on_button_clicked), GINT_TO_POINTER(buttons[i].value));
// Упаковываем кнопку в горизонтальный контейнер.
// TRUE, TRUE - кнопка будет растягиваться и заполнять доступное пространство.
// 0 - без дополнительного отступа.
gtk_box_pack_start(GTK_BOX(hbox), btn, TRUE, TRUE, 0);
}
// --- Создание ползунка (GtkScale) ---
// Создаем горизонтальный ползунок с диапазоном значений от 500 до 2500
// и шагом изменения значения 10.
GtkWidget *scale = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 500, 2500, 10);
gtk_scale_set_value_pos(GTK_SCALE(scale), GTK_POS_TOP); // Размещаем текущее значение ползунка сверху.
// Подключаем сигнал "value-changed" (изменение значения ползунка) к функции on_scale_moved.
// NULL в user_data, так как значение берется напрямую из ползунка в on_scale_moved.
g_signal_connect(scale, "value-changed", G_CALLBACK(on_scale_moved), NULL);
gtk_range_set_value(GTK_RANGE(scale), 1500); // Устанавливаем начальное значение ползунка в 1500 (центр).
// Упаковываем ползунок в вертикальный контейнер.
// TRUE, TRUE - ползунок будет растягиваться и заполнять доступное пространство.
// 0 - без дополнительного отступа.
gtk_box_pack_start(GTK_BOX(vbox), scale, TRUE, TRUE, 0);
// --- Отображение GUI и запуск основного цикла GTK+ ---
gtk_widget_show_all(window); // Отображает все виджеты, содержащиеся в окне.
gtk_main(); // Запускает основной цикл обработки событий GTK+.
// Программа будет работать, пока gtk_main_quit() не будет вызвана.
return 0; // Возвращаем 0, указывая на успешное завершение программы.
}
### Компиляция программы
Для компиляции приложения вам потребуется установленный компилятор GCC и библиотеки gtk+-3.0.
Установите необходимые библиотеки (если еще не установлены):
sudo apt update sudo apt install libgtk-3-dev
Сохраните исходный код программы в файл servo_gui.c в папке вашего проекта (например, /home/Alex/GUI4RPiZ2W/5/).
Скомпилируйте программу с помощью следующей команды, находясь в папке проекта:
gcc servo_gui.c -o servo_gui `pkg-config --cflags --libs gtk+-3.0`
Эта команда компилирует servo_gui.c и создаёт исполняемый файл с именем servo_gui. Опции pkg-config автоматически добавляют необходимые флаги компилятора и библиотеки для GTK+.
### Проверка иконки
Убедитесь, что файл иконки servo_gui.png находится по указанному пути в вашем коде и в файле ярлыка:
C:UsersUserDocumentsGitHubGUI4RPiZ2W6servo_gui.png
Если иконка отсутствует или путь неверен, приложение запустится, но иконка отображаться не будет.
### Файл ярлыка (.desktop файл)
Для удобного запуска приложения из меню вашей операционной системы или с рабочего стола, создадим файл ярлыка.
Создайте файл servo_gui.desktop со следующим содержимым:
[Desktop Entry] Type=Application Name=Servo SG90 Controller Comment=Control a SG90 servo via GPIO Exec=/home/Alex/GUI4RPiZ2W/6/servo_gui Icon=/home/Alex/GUI4RPiZ2W/6/servo_gui.png Terminal=false Categories=Utility;Education;Robotics;
Сохраните этот файл в директории, предназначенной для пользовательских ярлыков:
cp /home/Alex/GUI4RPiZ2W/6/servo_gui.desktop ~/.local/share/applications/servo_gui.desktop
Это поместит ярлык в место, где система сможет его найти и добавить в меню приложений.
Сделайте файл ярлыка исполняемым:
chmod +x ~/.local/share/applications/servo_gui.desktop
Это необходимо, чтобы система могла выполнить файл ярлыка.
После выполнения этих шагов, «Servo SG90 Controller» должен появиться в вашем меню приложений (например, в категории «Утилиты» или «Робототехника»).
—
Особенности запуска pigpiod
Как было упомянуто, для работы команд pigs (и соответственно нашего GUI-приложения, которое их использует через system()) необходимо, чтобы демон pigpiod был запущен.
Мы решили эту задачу, настроив запуск pigpiod через crontab при загрузке системы. Это гарантирует, что сокет pigs всегда доступен.
Примечание
Важно убедиться, что pigpiod действительно запущен. Вы можете проверить его статус командой systemctl status pigpiod или просто попробовать выполнить любую команду pigs в терминале. Если демон не запущен, команды pigs не будут работать, и наше приложение не сможет управлять сервоприводом.
—
Выводы
Сервопривод SG90 — это простой и удобный способ управления механикой на Raspberry Pi. Благодаря интерфейсу pigpio и утилите pigs, мы можем добиться точного управления углом поворота. Создание графического интерфейса с GTK+ значительно упрощает взаимодействие с сервоприводом, предоставляя интуитивно понятные элементы управления, такие как кнопки для предустановленных положений и ползунок для точного позиционирования.
—