Глава 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` не будут работать. Примеры команд для управления сервоприводом из командной строки: .. code-block:: bash # Если 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-приложения для управления сервоприводом, включая подробные комментарии, объясняющие каждую часть: .. code-block:: c #include // Включаем заголовочный файл для библиотеки GTK+ (для создания графического интерфейса пользователя) #include // Включаем заголовочный файл для стандартных функций, таких как system() и snprintf() #include // Включаем заголовочный файл для 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`. 1. **Установите необходимые библиотеки (если еще не установлены):** .. code-block:: bash sudo apt update sudo apt install libgtk-3-dev 2. **Сохраните исходный код** программы в файл `servo_gui.c` в папке вашего проекта (например, `/home/Alex/GUI4RPiZ2W/5/`). 3. **Скомпилируйте программу** с помощью следующей команды, находясь в папке проекта: .. code-block:: bash 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:\Users\User\Documents\GitHub\GUI4RPiZ2W\6\servo_gui.png` Если иконка отсутствует или путь неверен, приложение запустится, но иконка отображаться не будет. ### Файл ярлыка (`.desktop` файл) Для удобного запуска приложения из меню вашей операционной системы или с рабочего стола, создадим файл ярлыка. 1. **Создайте файл** `servo_gui.desktop` со следующим содержимым: .. code-block:: ini [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; 2. **Сохраните этот файл** в директории, предназначенной для пользовательских ярлыков: .. code-block:: bash cp /home/Alex/GUI4RPiZ2W/6/servo_gui.desktop ~/.local/share/applications/servo_gui.desktop Это поместит ярлык в место, где система сможет его найти и добавить в меню приложений. 3. **Сделайте файл ярлыка исполняемым**: .. code-block:: bash chmod +x ~/.local/share/applications/servo_gui.desktop Это необходимо, чтобы система могла выполнить файл ярлыка. После выполнения этих шагов, "Servo SG90 Controller" должен появиться в вашем меню приложений (например, в категории "Утилиты" или "Робототехника"). --- Особенности запуска `pigpiod` ------------------------------ Как было упомянуто, для работы команд `pigs` (и соответственно нашего GUI-приложения, которое их использует через `system()`) необходимо, чтобы демон `pigpiod` был запущен. Мы решили эту задачу, настроив запуск `pigpiod` через `crontab` при загрузке системы. Это гарантирует, что сокет `pigs` всегда доступен. .. note:: Важно убедиться, что `pigpiod` действительно запущен. Вы можете проверить его статус командой `systemctl status pigpiod` или просто попробовать выполнить любую команду `pigs` в терминале. Если демон не запущен, команды `pigs` не будут работать, и наше приложение не сможет управлять сервоприводом. --- Выводы ------ Сервопривод SG90 — это простой и удобный способ управления механикой на Raspberry Pi. Благодаря интерфейсу `pigpio` и утилите `pigs`, мы можем добиться точного управления углом поворота. Создание графического интерфейса с GTK+ значительно упрощает взаимодействие с сервоприводом, предоставляя интуитивно понятные элементы управления, такие как кнопки для предустановленных положений и ползунок для точного позиционирования. ---