Из предыдущих статей нашей серии про сервоприводы вы знаете, что для управления сервоприводами используется широтно-импульсная модуляция ШИМ (Pulse Width Modulation,  PWM). Это третья статья серии статей про сервоприводы и в ней мы расскажем, как управлять сервоприводами напрямую с портов GPIO 40-pin разъёма одноплатного микрокомпьютера Repka Pi 3, полностью аналогичного в этом смысле Raspberry Pi 3B+. Мы с Вами разберём, как сделать чисто программный генератор ШИМ, а также как использовать аппаратный генератор ШИМ, доступный в Repka Pi.

Рассмотрим, почему, если нужно управлять большим количеством сервоприводов, то наилучшим решением будет использование специально предназначенных для этого контроллеров серво-драйверов (ServoDriver).

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

Если Вам интересна тема управления сервоприводами, то все статьи данного цикла статей Вы можете найти здесь:

Содержание данной статьи


Программный генератор ШИМ

На первый взгляд нетрудно сделать чисто программный генератор, который будет создавать импульсы ШИМ‑модуляции на контактах порта GPIO. При этом длительность импульсов, а также паузу между ними можно задавать при помощи программной задержки или таймеров.

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

Собираем макет для тестирования программного ШИМ

Для изучения возможностей программной генерации ШИМ‑импульсов на микрокомпьютере Repka Pi соберем макет (рис. 1).

Рис. 1. Подключение сервопривода к контакту PA8 микрокомпьютера Repka Pi
Рис. 1. Подключение сервопривода к контакту PA8 микрокомпьютера Repka Pi

Подключите сервопривод к GPIO:

  • коричневый провод сервопривода подключите к земле Repka Pi (физические контакты 6, 9, 14, 20, 25, 30, 34 или 39);

  • красный провод подключите к питанию +5 В (физические контакты 2 или 4);

  • оранжевый провод (используется для передачи управляющих импульсов) подключите к физическому контакту 11 (PA8)

Если в вашем распоряжении имеется осциллограф, подключите его к контакту 11 для контроля качества создаваемых импульсов ШИМ. Также вы можете подключить к этому выводу светодиод через резистор на 1 КОм. Его свечение будет сигнализировать о поступлении импульсов.

Попытка 1: Программная задержка при формировании импульсов

Попробуем использовать для генерации импульсов функцию задержки time.sleep.  Исходный код соответствующей программы pwm_sleep.py вы найдете в листинге 1.

Листинг 1. https://raw.githubusercontent.com/AlexandreFrolov/repka-pi-pwm/main/pwm_sleep.py

import RepkaPi.GPIO as GPIO
import time

GPIO.setboard(GPIO.REPKAPI3)
GPIO.setmode(GPIO.SUNXI)

servo_pin = "PA8"
GPIO.setup(servo_pin, GPIO.OUT)
GPIO.setwarnings(False)

def set_servo_angle(angle, frequency_Hz):
    dutyCycle = angle / 18. + 3.
    period_duration = 1 / frequency_Hz * 1000 
    pulse_duration = ((period_duration / 100) * dutyCycle)
    wait_after_pulse = period_duration - round(pulse_duration, 1)
    
    GPIO.output(servo_pin, GPIO.HIGH) 
    time.sleep((pulse_duration /  1000.0))
    GPIO.output(servo_pin, GPIO.LOW)
    time.sleep(wait_after_pulse / 1000.0)
    
try:
    while True:
        set_servo_angle(0, 50)

except KeyboardInterrupt:
    GPIO.cleanup()

В этой программе исходя из требуемого угла поворота вала сервопривода вычисляется длительность импульса ШИМ pulse_duration, а также длительность паузы перед следующим импульсом wait_after_pulse.

Для формирования импульса на контакте servo_pin функция set_servo_angle с помощью функции GPIO.output вначале устанавливается высокий уровень напряжения (3.3 В), а затем, после задержки на pulse_duration миллисекунд, — низкий уровень.

Функция set_servo_angle вызывается в цикле до тех пор, пока работа программы не будет прервана с помощью комбинации клавиш Ctrl+C.

Перед запуском этой программы на микрокомпьютере Repka Pi необходимо установить библиотеку RepkaPi.GPIO, доступную на GitFlic, а также другие программы и библиотеки.

Установка библиотеки RepkaPi.GPIO выполняется следующим образом:

# apt-get update
# apt-get install python3-dev python3-setuptools git
# git clone https://github.com/DikShv/RepkaPi3.GPIO.git
# cd RepkaPi3.GPIO
# python3 setup.py install 

Установив библиотеку RepkaPi.GPIO, загрузите программу pwm_sleep.py из репозитория по ссылке, приведенной в листинге 1, а затем запустите ее из командной строки:

# python3 pwm_sleep.py

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

На этом видео показано, как длительность импульса хаотически изменяется, что приводит к дрожанию вала сервопривода.

Попытка 2: Использование таймеров

Попробуем улучшить стабильность, использовав таймеры вместо функции задержки в программе pwm_threads.py (листинг 2).

Листинг 2. https://raw.githubusercontent.com/AlexandreFrolov/repka-pi-pwm/main/pwm_threads.py

import RepkaPi.GPIO as GPIO
import time
import threading

GPIO.setboard(GPIO.REPKAPI3)
GPIO.setmode(GPIO.SUNXI)

servo_pin = "PA8"
GPIO.setup(servo_pin, GPIO.OUT)
GPIO.setwarnings(False)

def set_servo_angle(angle, frequency_Hz):
    dutyCycle = angle / 18. + 3.
    period_duration = 1 / frequency_Hz * 1000
    pulse_duration = ((period_duration / 100) * dutyCycle)
    wait_after_pulse = period_duration - round(pulse_duration, 1)
    
    GPIO.output(servo_pin, GPIO.HIGH)
    threading.Timer(pulse_duration / 1000.0, lambda: GPIO.output(servo_pin, GPIO.LOW)).start()
    threading.Timer(wait_after_pulse / 1000.0, lambda: set_servo_angle(angle, frequency_Hz)).start()

try:
    threading.Timer(2, lambda: set_servo_angle(0, 50)).start()

except KeyboardInterrupt:
    GPIO.cleanup()

Здесь функция set_servo_angle устанавливает высокий уровень напряжения на контакте servo_pin. Затем она запускает таймер threading.Timer на время pulse_duration, а после его срабатывания — сбрасывает уровень напряжения на этом контакте в ноль.

Далее опять же с помощью таймера функция set_servo_angle дожидается окончания паузы wait_after_pulse после импульса, и запускает set_servo_angle снова.

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

Попытка 3: Давайте все напишем на С++

Но может быть проблема в Python? Все же это интерпретируемый язык, работает не очень быстро.

В листинге 3 мы привели в сокращенном виде исходный код программы pwm_timers.cpp, составленной на языке C++.

Листинг 3. https://raw.githubusercontent.com/AlexandreFrolov/repka-pi-pwm/main/cpp/pwm_timers.cpp

…
void generate_pulse(int pulse_duration_us) {
    set_gpio_value(1);
    std::this_thread::sleep_for(std::chrono::microseconds(pulse_duration_us));
    set_gpio_value(0);
}

void pulse_function() {
    float dutyCycle = 90 / 18.0f + 3.0f;
    float period_duration = (1000.0f / 50) * 1000;
    float pulse_duration = static_cast<int>(period_duration * (dutyCycle / 100.0f));
    int pulse_duration_us = static_cast<int>(pulse_duration_ms * 1000);
    int wait_after_pulse = static_cast<int>(period_duration - pulse_duration_us);

    std::cout << "wait_after_pulse: " << wait_after_pulse << " ms" << std::endl;
    std::cout << "pulse_duration: " << pulse_duration << " us" << std::endl;
    std::cout << "pulse_duration_us: " << pulse_duration_us << " us" << std::endl;
    std::cout << "dutyCycle: " << dutyCycle << std::endl;
    std::cout << "period_duration: " << period_duration << " ms" << std::endl;

    while (!should_exit) {
        auto pulse_task = std::async(std::launch::async, generate_pulse, pulse_duration_us);
        pulse_task.wait();
        std::this_thread::sleep_for(std::chrono::microseconds(wait_after_pulse));
    }
}
…

Эта программа генерирует импульсы ШИМ с помощью асинхронного запуска функции generate_pulse, формирующей управляющий импульс ШИМ. Далее она дожидается завершения интервала перед началом следующего импульса и запускает генерацию снова.

Скомпилируйте программу и запустите ее с максимальным приоритетом:

# g++ -o pwm_timers pwm_timers.cpp -pthread
# nice -n -20 chrt --rr 99 ./pwm_timers

Нужно сказать, что стабильность длительности импульса стала гораздо выше, чем при использовании программы на Phyton, однако ее все же совершенно недостаточно для управления сервоприводами. Если вы запустите эту программу на выполнение, то увидите заметное дрожание вала сервопривода.

В чем причина нестабильности длительности импульсов при попытке генерации ШИМ‑импульсов чисто программными методами?

Дело в том, что Repka OS, установленная на микрокомпьютере Repka Pi, создана на базе Ubuntu. Это операционная система не предназначена для работы в реальном времени, у нее совсем другие области применения.

Возможно, вы уже успешно генерировали ШИМ‑импульсы программными методами на микроконтроллерах, таких как Ардуино. Да, в этих микроконтроллерах нет такого количества прерываний и переключений процессов, как при использовании Ubuntu, поэтому длительность импульсов будет стабильна.

Но что же делать, если возможностей простых микроконтроллеров не хватает? Как правильно управлять сервоприводами с помощью микрокомпьютера Repka Pi с установленной на нем Repka OS?

Тут есть два решения — использование аппаратного ШИМ‑генератора, встроенного в процессор, а также использование внешних контроллеров ШИМ.


Аппаратный ШИМ-генератор на примере Repka Pi

Если нужно получить стабильные управляющие импульсы ШИМ, не обойтись без аппаратного генератора. Обычно в процессорах, установленных в микрокомпьютерах, уже есть некоторое количество таких генераторов. В микрокомпьютере Repka Pi выход одного из таких генераторов выведен на контакт 33 интерфейса GPIO, чем мы и воспользуемся.

Установка нужной распиновки

Для использования аппаратного генератора ШИМ в Repka Pi нужно после загрузки Repka OS установить пятый вариант распиновки. В результате этого контакт 33 будет играть роль выхода генератора ШИМ, встроенного в процессор.

Для установки распиновки запустите утилиту repka‑config и после выбора частоты процессора в меню 3 Frequency / Pinout Options в меню выбора варианта прошивки задайте пятый вариант, в описании которого встречается строка PWM-1 (рис. 2).

Рис. 2. Выбор пятого варианта распиновки с подключением PWM
Рис. 2. Выбор пятого варианта распиновки с подключением PWM

После выбора нужного варианта выделите Ok, нажмите клавишу Enter и перезагрузите ОС. Вообще нужно отметить, что у одноплатника Repka Pi уже успело сформироваться определённое онлайн‑профессиональное сообщество, основавшееся в Телеграм группе обсуждения канала проекта, там можно задавать любые вопросы по проектам на основе Репки, участники дают ответы и помогают разобраться с возникающими вопросами и что не маловажно, как правило весьма дружелюбное отношение и на сложные вопросы часто можно получить ответы от разработчиков одноплатника.

Собираем макет для тестирования аппаратной ШИМ

Добавьте к собранному ранее макету еще один сервопривод, подключив его к контакту 33, как это показано на рис. 3.

Рис. 3. Подключение сервопривода к контакту аппаратного ШИМ-генератора
Рис. 3. Подключение сервопривода к контакту аппаратного ШИМ-генератора

Подключите сервопривод к контакту GPIO аппаратного ШИМ-генератора (рис. 4):

  • коричневый провод сервопривода подключите к земле Repka Pi (физические контакты 6, 9, 14, 20, 25, 30, 34 или 39);

  • красный провод подключите к питанию +5 В (физические контакты 2 или 4);

  • оранжевый провод (используется для передачи управляющих импульсов) подключите к физическому контакту 33

Рис. 4. Подключение сервопривода к контактам GPIO микрокомпьютера Repka Pi
Рис. 4. Подключение сервопривода к контактам GPIO микрокомпьютера Repka Pi

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


Управляем сервоприводом на примере SG90

Для управления сервоприводом sg90 с помощью аппаратного генератора ШИМ, встроенного в процессор микрокомпьютера Repka Pi, загрузите программу sg90_test.py (листинг 4).

Листинг 4. https://raw.githubusercontent.com/AlexandreFrolov/repka-pi-pwm/main/sg90_test.py

import RepkaPi.GPIO as GPIO
from time import sleep
import sys

def setServoAngle(servo, frequency_Hz, angle):
    servo.start_pwm()  
    dutyCycle = angle / 18. + 3.
    
    print("Угол поворота: " + str(angle) + "\xb0")
    print("Коэффициент заполнения Duty: " + str(dutyCycle) + "%")
    period_duration = 1 / frequency_Hz * 1000
    print("Длительность периода: ", period_duration, "мс, частота " + str(frequency_Hz) + " Гц")
    pulse_duration = (period_duration / 100) * dutyCycle
    print("Длительность импульса ШИМ :", round(pulse_duration, 1), "мс")    
    
    servo.duty_cycle(dutyCycle)
    sleep(0.3)
    servo.stop_pwm()

if __name__ == "__main__":

    PWM_chip = 0
    PWM_pin = 0
    frequency_Hz = 50
    Duty_Cycle_Percent = 8
    servo = GPIO.PWM_A(PWM_chip, PWM_pin, frequency_Hz, Duty_Cycle_Percent)

    setServoAngle(servo, frequency_Hz, 0)
    sleep(2)
    setServoAngle(servo, frequency_Hz, 90)
    sleep(2)
    setServoAngle(servo, frequency_Hz, 180)
    sleep(2)
    servo.pwm_close()
    del servo

После запуска эта программа поочередно поворачивает вал сервопривода в положения 0⁰, 90⁰ и 180⁰ с интервалом 2 секунды.

Для каждого положения программа выводит на консоль значения коэффициента заполнения Duty, длительность периода и длительность импульса ШИМ:

# python3 sg90_test.py
Угол поворота: 0°
Коэффициент заполнения Duty: 3.0%
Длительность периода:  20.0 мс, частота 50 Гц
Длительность импульса ШИМ : 0.6 мс
Угол поворота: 90°
Коэффициент заполнения Duty: 8.0%
Длительность периода:  20.0 мс, частота 50 Гц
Длительность импульса ШИМ : 1.6 мс
Угол поворота: 180°
Коэффициент заполнения Duty: 13.0%
Длительность периода:  20.0 мс, частота 50 Гц
Длительность импульса ШИМ : 2.6 мс

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

При вызове функция setServoAngle пересчитывает угол поворота в коэффициент заполнения ШИМ для функции duty_cycle, выдающей управляющий сигнал на сервопривод.

Для преобразования угла в коэффициент заполнения dutyCycle, используется формула:

dutyCycle = angle / 18. + 3.

Здесь сначала выполняется деление значения угла на 18. В итоге диапазон изменения угла [0, 180] приводится к диапазону [0, 10]. Далее этот диапазон смещается добавлением значения 3. Как результат получается диапазон [3, 13].

Что касается диапазона изменения длительности импульса ШИМ, то для изменения угла в пределах [0, 180] он составит [0.6, 2.6]. Такой диапазон вполне подходит для управления сервоприводами SG90 и mg90s.

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


Управляем сервоприводом постоянного вращения DS04-NFC

Подавая управляющие импульсы ШИМ на сервоприводы с удержанием угла, вы можете задавать нужное положение вала такого сервопривода.

Что касается сервоприводов постоянного вращения (рис. 5), то с помощью импульсов ШИМ можно управлять скоростью и направлением вращения его вала. Также можно полностью остановить вращение.

Рис. 5. Сервопривод постоянного вращения DS04-NFC
Рис. 5. Сервопривод постоянного вращения DS04-NFC

Подключение сервопривода DS04-NFC

Замените в нашем макете сервопривод с удержанием угла sg90 сервоприводом постоянного вращения DS04-NFC.

Подключите его к контакту аппаратного ШИМ‑генератора GPIO, к питанию и земле следующим образом:

  • черный провод сервопривода подключите к земле Repka Pi (физические контакты 6, 9, 14, 20, 25, 30, 34 или 39);

  • красный провод подключите к питанию +5 В (физические контакты 2 или 4).

  • белый провод нужно подключить к физическому контакту 33 (используется для передачи управляющих импульсов)

Проверьте все соединения дважды перед включением питания микрокомпьютера.

В зависимости от длительности управляющего импульса ШИМ вал сервопривода будет находиться в одном из трех состояний:

  • 1 мс — вал вращается с полной скоростью против часовой стрелки;

  • 1.5 мс — вал остановлен;

  • 2 мс — вал вращается с полной скоростью по часовой стрелке

Промежуточные значения длительности импульсов от 1 мс до 1.5 мс и от 1.5 мс до 2 мс используются для управления скоростью вращения вала.

Программа управления сервоприводом DS04-NFC

Для управления скоростью и направлением вращения вала сервопривода DS04-NFC мы подготовили программу [DS04-NFC.py](http://DS04-NFC.py) (листинг 5).

Листинг 5. https://raw.githubusercontent.com/AlexandreFrolov/repka-pi-pwm/main/DS04-NFC.py

#!/usr/bin/env python3
import RepkaPi.GPIO as GPIO
import time
import warnings

if __name__ == '__main__':
    try:
        PWM_chip = 0
        PWM_pin = 0
        frequency_Hz = 50
        Duty_Cycle_Percent = 0
        servo = GPIO.PWM_A(PWM_chip, PWM_pin, frequency_Hz, Duty_Cycle_Percent)    

        while True:
            pulse_width = float(input("Введите длительность импульса (1-2): "))
            pulse_width = pulse_width / 1000;
            duty = pulse_width / (1 / frequency_Hz) * 100
            print(pulse_width)
            print(duty)
            
            servo.start_pwm()
            servo.duty_cycle(duty)
            time.sleep(10)
            servo.stop_pwm()
    except KeyboardInterrupt:
        servo.pwm_close()
        del servo
        GPIO.cleanup()

Запустите программу DS04-NFC.py в командной строке без параметров. После запуска программа попросит вас ввести длительность импульса в мс. Указывайте здесь значения в диапазоне [1, 2]:

# python3 DS04-NFC.py
Введите длительность импульса (1-2): 1
Длительность импульса ШИМ :0.001 mc
Коэффициент заполнения Duty: 5.0%
Введите длительность импульса (1-2): 2
Длительность импульса ШИМ :0.002 mc
Коэффициент заполнения Duty: 10.0%
Введите длительность импульса (1-2): 1.5
Длительность импульса ШИМ :0.0015 mc
Коэффициент заполнения Duty: 7.5%

Убедитесь, что сервопривод работает, как ожидается. Для завершения работы программы нажмите в консоли комбинацию клавиш Ctrl+C.


Управляем светодиодом

С помощью ШИМ‑генератора можно управлять яркостью свечения светодиода. Чем больше коэффициент заполнения duty, тем дольше длится вспышка светодиода, и тем он ярче светится.

Подключите положительный вывод светодиода через резистор 1 КОм к контакту 33, как это показано на рис. 6.

Рис. 6. Подключение светодиода к контакту 33
Рис. 6. Подключение светодиода к контакту 33

В листинге 6 мы привели исходный код программы, меняющей яркость от нулевой до максимальной и обратно.

Листинг 6. https://raw.githubusercontent.com/AlexandreFrolov/repka‑pi‑pwm/main/led_test.py

import RepkaPi.GPIO as GPIO
from time import sleep
import sys

if __name__ == "__main__":

    PWM_chip = 0
    PWM_pin = 0
    frequency_Hz = 1000
    Duty_Cycle_Percent = 8
    led = GPIO.PWM_A(PWM_chip, PWM_pin, frequency_Hz, Duty_Cycle_Percent)

    try:
        while True:
            for duty_cycle in range(0, 101, 5):
                led.start_pwm()  
                led.duty_cycle(duty_cycle)
                sleep(0.1)
                led.stop_pwm()

            for duty_cycle in range(100, -1, -5):
                led.start_pwm()  
                led.duty_cycle(duty_cycle)
                sleep(0.1)
                led.stop_pwm()

    except KeyboardInterrupt:
        pass

    led.pwm_close()
    del led

При инициализации ШИМ‑генератора программа устанавливает частоту управляющих импульсов, равную 1000 Гц. При такой частоте мерцание светодиода будет незаметно.

Далее программа в первом цикле меняет значение duty от нуля до 100 с интервалом 5, а затем во втором цикле выполняет аналогичным образом постепенное уменьшение duty.

Чтобы прервать работу программы, нажмите комбинацию клавиш Ctrl+C.

Результат работы программы можно посмотреть на этом видео.


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

Контакты интерфейса GPIO в микрокомпьютере Repka Pi имеют очень небольшую нагрузочную способность — к ним можно подключать устройства, потребляющие ток не более нескольких миллиампер. Если контакт GPIO используется как вход, то на него должно подаваться положительное напряжение величиной до 3.3 В.

Защитный резистор

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

Преобразователи уровней

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

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

Например, для управления сервоприводом MG995 необходимы импульсы ШИМ амплитудой от 4.8 В до 7.2 В. Вы не сможете управлять этим сервоприводом, подключив его непосредственно к GPIO микрокомпьютера Repka Pi — там на выходе всего лишь 3.3 В.

Чтобы решить эту задачу, можно использовать преобразователи уровней, например 8-канальный преобразователь TXS0108E или аналогичный. В этой статье вы найдете пример подключения этого преобразователя.

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

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

Внешний источник питания сервопривода

Другой важный момент — не всегда можно использовать для питания сервопривода напряжение 5 В, взятое с физических контактов 2 или 4 разъема GPIO микрокомпьютера Repka Pi. Ток, потребляемый мощными сервоприводами, может оказаться слишком большим, поэтому тут лучше использовать отдельный источник питания.

Разумеется, земля внешнего источника питания сервопривода должна быть соединена с землей микрокомпьютера (если только вы не используете оптопары для гальванической развязки). Но это нужно сделать так, чтобы через контакты микрокомпьютера не протекал большой ток питания сервопривода.

Пример подключения внешнего источника питания для сервопривода показан на рис. 7.

Рис. 7. Подключение внешнего источника питания для сервопривода
Рис. 7. Подключение внешнего источника питания для сервопривода

Обратите внимание, что большой ток, необходимый для работы сервопривода, идет «в обход» платы микрокомпьютера. Однако ток управления поступает в сервопривод по другому, слаботочному контуру.

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

Хотя процессоры, на базе которых создаются микрокомпьютеры, содержат в себе довольно много аппаратных генераторов ШИМ, их обычно не выводят все на интерфейс GPIO.

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

Наилучшее решение для подключения сервоприводов к микрокомпьютеру — использование специализированных контроллеров. Такие контроллеры совместимы с GPIO микрокомпьютеров и обеспечивают необходимые параметры управляющих сигналов для сервоприводов.

Кроме того, контроллеры допускают одновременное подключение большого количества сервоприводов. Например, к контроллеру Robointellect Controller 001 (рис. 8), созданному на базе микросхемы PCA9685, можно подключить до 16 сервоприводов. И при необходимости такие контроллеры можно каскадировать через I2C, подключая десятки и сотни сервоприводов.

Рис. 8. Контроллер Robointellect Controller 001
Рис. 8. Контроллер Robointellect Controller 001

Контроллер Robointellect Controller 001 позволяет питать подключенные к нему сервоприводы как от внешнего блока питания на 5 В, так и от лабораторного блока питания, или любого внешнего блока питания с подключением через клеммы (рис. 9)

Рис. 9. Разные способы подключения внешних блоков питания
Рис. 9. Разные способы подключения внешних блоков питания

Запуск с правами обычного пользователя

Все программы, приведенные в этой статье, требуют для своей работы прав пользователя root. Это связано с тем, что нужной для этих программ библиотеке RepkaPi.GPIO требуется доступ к системным файлам, например, к файлу /sys/class/pwm/pwmchip0/export:

# ls -lh /sys/class/pwm/pwmchip0/export
--w------- 1 root root 4,0K авг  3 08:19 /sys/class/pwm/pwmchip0/export

Если по каким‑то причинам требуется запустить программу, управляющую сервоприводами, от непривилегированного пользователя, придется использовать sudo.

Добавление пользователя

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

# adduser developer
Добавляется пользователь «developer» ...
Добавляется новая группа «developer» (1000) ...
Добавляется новый пользователь «developer» (1000) в группу «developer» ...
Создаётся домашний каталог «/home/developer» ...
Копирование файлов из «/etc/skel» ...
Новый пароль :
Повторите ввод нового пароля :
passwd: пароль успешно обновлён
Изменение информации о пользователе developer
Введите новое значение или нажмите ENTER для выбора значения по умолчанию
        Полное имя []:
        Номер комнаты []:
        Рабочий телефон []:
        Домашний телефон []:
        Другое []:
Данная информация корректна? [Y/n] y

Также задайте пароль пользователя и подтвердите его добавление.

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

После добавления пользователя разрешите ему работать с командой sudo:

# usermod -aG sudo developer

Теперь проверьте запуск какой-нибудь программы из этой статьи от имени непривилегированного пользователя с помощью sudo, например:

$ git clone https://github.com/AlexandreFrolov/repka-pi-pwm.git
$ cd repka-pi-pwm
$ sudo python3 DS04-NFC.py

Если все работает, то можно разрешить запуск программы DS04-NFC.py от имени пользователя developer.

Разрешение запуска программы DS04-NFC.py

При настройке разрешения запуска программы DS04-NFC.py (или любой другой) обычному пользователю с предоставлением ей прав root нужно указывать полный путь к программе, а не ее имя. В противном случае злоумышленник сможет создать программу с таким же именем и запустить ее с правами root.

Чтобы можно было запускать программу DS04-NFC.py не как параметр для python3, а непосредственно из командой строки, указывая полный путь к ее файлу, нужно добавить путь к python3 в первую строку файла программы:

#!/usr/bin/env python3
import RepkaPi.GPIO as GPIO
…

Также потребуется установить атрибут, разрешающий запуск программы.

$ chmod +x DS04-NFC.py

Теперь проверьте, что программа запускается с указанием ее полного пути от имени непривилегированного пользователя developer:

$ sudo /home/developer/repka-pi-pwm/DS04-NFC.py

Создайте файл DS04-NFC.sh с таким содержимым:

#!/bin/bash
sudo /home/developer/repka-pi-pwm/DS04-NFC.py

Разрешите запуск программы DS04-NFC.py и скрипта DS04-NFC.sh:

$ chmod +x /home/developer/repka-pi-pwm/DS04-NFC.py
$ chmod +x DS04-NFC.sh

Проверьте, что скрипт DS04-NFC.sh запускается по его полному пути от имени пользователя developer:

$ /home/developer/repka-pi-pwm/DS04-NFC.sh

Добавление программы в sudoers

Введите команду visudo от имени пользователя root:

# visudo

На экране появится редактор файла sudoers.

Добавьте в него после строки, задающей права root новую строку для пользователя developer:

root    ALL=(ALL:ALL) ALL
developer ALL=(ALL:ALL) /home/developer/repka-pi-pwm/DS04-NFC.sh

Сохраните содержимое файла при помощи комбинации клавиш Ctrl+O, подтвердив сохранение файла, а затем закройте редактор комбинацией клавиш Ctrl_X.

Проверьте, что теперь вы можете запускать программу DS04-NFC.py через полный путь к скрипту DS04-NFC.sh:

developer@Repka-Pi:~$ /home/developer/repka-pi-pwm/DS04-NFC.sh

Итоги

Из этой статьи Вы узнали, как можно управлять сервоприводами, подключенными непосредственно к контактам GPIO на примере одноплатного микрокомпьютера Repka Pi (Raspberry совместимого Российского аналога).

Вы так же попробовали создать чисто программные ШИМ‑генераторы, управляющие уровнем выходного напряжения на контактах GPIO. Вы убедились, что длительность создаваемых такими генераторами, весьма нестабильна, так как Repka OS, как и большинство подобных операционных систем общего назначения, не является операционной системой реального времени и предназначена для решения других задач.

Однако с помощью аппаратного ШИМ‑генератора, доступного в Repka Pi, можно вырабатывать стабильные импульсы, подходящие в т.ч. для точного и стабильного управления сервоприводами и подобными им устройствами.

Вы также знаете, что при необходимости управления большим количеством сервоприводов имеет смысл использовать контроллер RoboIntellect Controller 001 или подобные ему контроллеры серво‑драйверы. С учетом возможности каскадирования такие контроллеры могут управлять десятками и сотнями сервоприводов одновременно.

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

Вы также теперь умеете запускать программы управления сервоприводами с правами непривилегированного пользователя в ОС Linux.

Комментарии (5)


  1. Sergey78
    15.09.2023 19:23
    +1

    Не совсем понял, зачем вы пользователя и sudo заводите. Только из-за того, что права на pwmchip0 надо? Это обычно не так решают. Через udev делаете правило, чтоб на pwmchip* (или конкретно pwmchip0 ) назначалась группа "pwmgroup" и устанавливались права разрешающие группе писать. А нужного пользователя просто в эту группу добавляете. И не нужно sudo. Для простых задача не нужен даже питон, можно через sysfs писать прямо из шелла.


    1. AlexandreFrolov
      15.09.2023 19:23
      +1

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

      Но через группу тоже, конечно, можно.


      1. Sergey78
        15.09.2023 19:23

        Вы же можете создать отдельную группу только для 1 пользователя. Такой вариант позволяет выдать только нужные привелегии. В данном случае, на запись в устройство. В случае со скриптом, у вас через sudo выполняется всё, что там написано.

        Я не настаиваю, если что :)


        1. AlexandreFrolov
          15.09.2023 19:23

          По опыту администрирования серверов на своем сервисе SAAS привык не предоставлять доступа больше, чем это нужно. Когда я выдаю доступ к конкретному скрипту, то никакой другой скрипт его не получит. И каждый такой скрипт я прописываю отдельно явным образом, для чего мне нужны права рута.

          А если есть группа, то туда может быть добавлен пользователь или пользователи, у которого (или у которых) много скриптов. И все эти скрипты получат доступ, даже если он им и не нужен.

          С группой удобнее, с настройкой доступа для каждого скрипта на мой взгляд безопаснее)


  1. Sergey78
    15.09.2023 19:23

    Был почти уверен, что в ядре должен быть готовый драйвер для управления сервой. Как ни странно, готового драйвера нет. Я нашел пример https://github.com/maciejjo/pwm-servo но судя по его давности (7 лет), в mainline его не приняли.

    Если надо управлять сервой, мне кажется вполне нормально написать свой драйвер. Параметры pwm ему через dts указывать, а в условное устройство "/dev/servo0" писать просто нужный угол. Странно, что готового нет.