alexandre_frolov
alexandre_frolov
2644 просмотров0 комментариев

Repka Pi и управление сервоприводами, ЧАСТЬ 2. Управляем сервоприводами с помощью Robointellect Controller 001 и RISDK

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

С помощью этой, второй статьи серии, вы научитесь пользоваться 16-канальным ШИМ-контроллером Robointellect Controller 001. Данный контроллер создан на базе микросхемы PCA9685 и предназначен для управления различными исполнительными устройствами, использующими ШИМ-модуляцию:

  • светодиоды;
  • сервоприводы;
  • шаговые приводы;
  • реле

Вы узнаете, что этим новым устройством можно работать через порт USB ноутбука или настольного компьютера, и вам не потребуются никакие дополнительные микроконтроллеры или наборы устройств. С другой стороны, мы расскажем, как вместо ноутбука подключить этот контроллер к микрокомпьютеру Repka Pi для управления сервоприводами и светодиодом RGB.

В практической части вы научитесь пользоваться библиотекой RISDK для управления сервоприводами и светодиодами RGB, вызывая ее функции из программ, составленных на языке Python.

Контроллер Robointellect Controller 001 разработан и производится в России. Его можно приобрести на сайте производителя, а также на Озон.

Другие статьи серии про управление сервоприводами вы найдете здесь:

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

Как устроен Robointellect Controller 001

Контроллер Robointellect Controller 001 представляет собой очень полезное устройство при создании робототехнических систем и систем автоматизации (рис. 1).

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

Плата контроллера, созданная НПО Робоинтеллект, объединяет сразу четыре устройства:

  • 16-канальный 12-битный контроллер PWM на базе микросхемы PCA9685;
  • SB-I2C конвертер на базе микросхемы CH341T;
  • модуль RGB-светодиода;
  • I2C Hub для подключения дополнительных устройств I2C

Расскажем о возможностях этих устройств в составе Robointellect Controller 001.

Контроллер PWM

На плате Robointellect Controller 001 имеется контроллер ШИМ, созданный на базе микросхемы PCA9685. Если поискать контроллеры на Алиэкспресс по этому названию, то  в результатах поиска вам попадется контроллер, показанный на рис. 2.

Рис. 2. Контроллер ШИМ на базе PCA9685

Как видите, здесь есть 16 портов для подключения сервоприводов, отдельные контакты для электропитания, а также контакты для подключения интерфейса I2C.

По сравнению с подобными контроллерами в Robointellect Controller 001 есть такие дополнения:

  • около каждого порта сервопривода находится светодиод, по свечению которого можно судить о наличии управляющих импульсов. Эти светодиоды помогают при отладке программного обеспечения (ПО);
  • есть контакты (пины) для подключения датчика тока, такого как INA219. Этот датчик позволяет контролировать потребление тока сервоприводами;
  • в комплекте с Robointellect Controller 001 поставляется сетевой блок для питания сервоприводов, который можно включить в специально предназначенный для этого разъем. Также имеется разъем для подключения питания с помощью проводов;
  • для Robointellect Controller 001 создана библиотека RISDK, с помощью которой можно управлять оборудованием, подключенным к этому контроллеру, из программ на языках C, C++, Golang, PHP и Python

Конвертер SB-I2C на базе микросхемы CH341T

В то время как управление контроллером, показанным на рис. 2, возможно только с помощью интерфейса I2C, контроллер Robointellect Controller 001 дает вам возможность подключения к управляющему компьютеру через USB. Причем это может быть компьютер под управлением Microsoft Windows или Linux, например, микрокомпьютер Repka Pi.

Управление через USB выполняет микросхема конвертера (моста) SB-I2C, созданная на базе микросхемы CH341T.

Не исключено что вы уже использовали подобные конвертеры, они есть в продаже на Алиэкспресс (рис. 3).

Рис. 3. Конвертер USB для I2C и UART

Наличие подобного конвертера в составе контроллера Robointellect Controller 001 упрощает работу с сервоприводами и другими устройствами. Вы можете разрабатывать ПО управления сервоприводами с помощью обычного ноутбука или настольного компьютера.

Модуль RGB-светодиода

На плате контроллера Robointellect Controller 001 установлен RGB-светодиод, для управления которым используются порты ШИМ с номерами 13, 14 и 15.

Светодиод можно использовать для отладки, а также для индикации состояния каких-либо процессов при работе вашего робота или другого устройства.

I2C Hub для подключения дополнительных устройств I2C

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

Последнее обстоятельство дает возможность управления сотнями сервоприводов при каскадировании контроллеров Robointellect Controller 001.

Кроме этого, сам контроллер Robointellect Controller 001 может управляться через I2C, при этом с помощь набора переключателей можно устанавливать адреса контроллеров на шине I2C.

Установка для Windows

Для управления устройствами, подключенными к Robointellect Controller 001, доступна библиотека RISDK. В этом разделе мы установим ее на компьютер, работающий под управлением ОС Microsoft Windows.

Установка RISDK

Прежде всего, нужно открыть эту страницу и загрузить с нее RISDK для Windows.

В результате вы получите файл robointellect_sdk_windows_amd64.exe (номер версии в имени файла может отличаться). Запустите файл с правами администратора. Для этого щелкните имя файла в папке правой клавишей мыши и выберите в контекстном меню строку Запуск от имени администратора.

Если после запуска появится предупреждение, показанное на рис. 4, щелкните кнопку Подробнее.

Рис. 4. Предупреждение о запуске неопознанного приложения

Вы увидите информацию о запуске программы неизвестного издателя (рис. 5).

Рис. 5. Информация о запускаемой программе

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

Вы увидите первое окно мастера установки RoboIntellect (рис. 6).

Рис. 6. Мастер установки RoboIntellect

Продолжите работу мастера при помощи кнопки Далее. Вам будет предложено согласиться с условиями лицензионного соглашения (рис. 7).

Рис. 7. Просмотр условий лицензионного соглашения

Установите отметку для флажка Я принимаю условия соглашения и затем щелкните кнопку Далее.

Вам будет предложено выбрать папку для установки (рис. 8).

Рис. 8. Выбор папки для установки RoboIntellect

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

На следующем этапе нужно выбрать компоненты для установки (рис. 9).

Рис. 9. Выбор компонентов для установки

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

Теперь все готово для установки (рис. 10).

Рис. 10. Подготовка к установке завершена

Чтобы запустить установку, щелкните кнопку Установить и наблюдайте за ходом установки (рис. 11).

Рис. 11. Ход процесса установки RoboIntellect

После завершения установки вам будет предложено открыть руководство пользователя (рис. 12).

Рис. 12. Установка завершена

Далее нужно вручную скопировать файлы из каталога C:\Program Files (x86)\RoboIntellect\ri_sdk\ri_sdk_x64\drivers\x64\ в каталог C:\Windows\System32:

  • CH341DLLA64.dll
  • librisdk.dll
  • libusb-1.0.dll
  • SLABHIDDevice.dll
  • SLABHIDtoSMBus.dll
  • vcruntime140.dll

Для копирования нужны права администратора.

Чтобы не копировать эти файлы, можно добавить путь к каталогу C:\Program Files (x86)\RoboIntellect\ri_sdk\ri_sdk_x64\drivers\x64\ в переменную среды "PATH".

Чтобы это сделать в ОС Microsoft Windows 11, откройте окно Параметры. Далее выберите О системе и Дополнительные параметры системы.

На вкладке Дополнительно щелкните кнопку Переменные среды. В разделе Переменные среды для пользователя найдите переменную PATH и выделите её.

Затем щелкните кнопку Изменить. В открывшемся окне нажмите кнопку Новый и добавьте путь к каталогу C:\Program Files (x86)\RoboIntellect\ri_sdk\ri_sdk_x64\drivers\x64.

И, наконец, щелкните кнопку ОК во всех открытых окнах, чтобы сохранить изменения.

Установка пульта управления роботом-манипулятором

Библиотека RISDK также поставляется в составе ПО пульта управления роботом-манипулятором. Для установки этого ПО нужно открыть страницу и загрузить с нее Пульт управления роботом-манипулятором для Windows.

Установка Python

Установите Python 3.11 или более новой версии с помощью приложения Microsoft Store (рис. 13), если он еще не установлен.

Рис. 13. Установка Python

Далее откройте командное приглашение Microsoft Windows и введите там команду python -V:

C:\Users\1>python -V
Python 3.11.4

На консоль будет выведена версия Python.

Сборка макета

Для подключения контроллера Robointellect Controller 001 к порту USB компьютера вам потребуется кабель USB типа «папа-папа» (рис. 14).

Рис. 14. Кабель USB типа «папа-папа»

Соедините таким кабелем порт USB контроллера с одним из портов вашего ноутбука или настольного компьютера.

Подключите сервоприводы к соответствующим портам контроллера. Обратите внимание, что контакты портов раскрашены в черный, красный и желтый цвета.

Вам нужно подключить сервоприводы аналогично тому, как это показано на рис. 15.

Рис. 15. Подключение сервоприводов к контроллеру Robointellect Controller 001

Здесь коричневый провод подключается к черному контакту, это земля. Для сервопривода постоянного вращения DS4-NFC провод земли черный.

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

Обратите внимание, что управляющий провод сервопривода постоянного DS4-NFC вращения белый.

Также подключите блок питания, который идет в комплекте с Robointellect Controller 001, к соответствующему разъему на плате контроллера.

Библиотека RISDK

Библиотека RISDK была создана для управления роботом-манипулятором, а также всеми устройствами, подключенными к контроллеру Robointellect Controller 001.

Эта весьма обширная библиотека работает на платформах Microsoft Windows и Linux. Она насчитывает десятки функций, предназначенных для работы с сервоприводами, RGB-светодиодами, адаптером I2C, а также цифровым датчиком тока, напряжения и мощности INA219.

В документации к библиотеке приведены многочисленные примеры программ для следующих языков программирования:

  • Python
  • C
  • C++
  • Golang
  • Фреймворк Golang gRPC
  • PHP

В этой статье мы сосредоточимся на использовании RISDK в программах Python.

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

Начнем с самого простого — научимся управлять светодиодом RGB, установленным на плате контроллера Robointellect Controller 001, из программы, составленной на языке Python.

Управление светодиодом с помощью функций RISDK

Пример программы risdk_led_sample.py, включающей RGB светодиод на 10 секунд, приведен в листинге 1. Вы также можете найти этот пример в описании функции RI_SDK_exec_RGB_LED_SinglePulse.

Листинг 1. https://raw.githubusercontent.com/AlexandreFrolov/ri-controller-001-risdk/main/risdk_led_sample.py

import sys
from ctypes import *

# Подключаем внешнюю библиотеку для работы с SDK
lib = cdll.LoadLibrary("C:\Windows\system32\librisdk.dll")

# Указываем типы аргументов для функций библиотеки RI_SDK
lib.RI_SDK_InitSDK.argtypes = [c_int, c_char_p]
lib.RI_SDK_CreateModelComponent.argtypes = [c_char_p, c_char_p, c_char_p, POINTER(c_int), c_char_p]
lib.RI_SDK_LinkPWMToController.argtypes = [c_int, c_int, c_uint8, c_char_p]
lib.RI_SDK_LinkLedToController.argtypes = [c_int, c_int, c_int, c_int, c_int, c_char_p]
lib.RI_SDK_DestroySDK.argtypes = [c_bool, c_char_p]
lib.RI_SDK_exec_RGB_LED_SinglePulse.argtypes = [c_int, c_int, c_int, c_int, c_int, c_bool, c_char_p]

def main():
    errTextC = create_string_buffer(1000)  # Текст ошибки. C type: char*
    i2c = c_int()
    pwm = c_int()
    led = c_int()

    # Инициализация библиотеки RI SDK с уровнем логирования 3
    errCode = lib.RI_SDK_InitSDK(3, errTextC)
    if errCode != 0:
        print(errCode, errTextC.raw.decode())
        sys.exit(2)

    # Создание компонента i2c адаптера модели ch341
    errCode = lib.RI_SDK_CreateModelComponent("connector".encode(), "i2c_adapter".encode(), "ch341".encode(), i2c, errTextC)
    if errCode != 0:
        print(errCode, errTextC.raw.decode())
        sys.exit(2)

    print("i2c: ", i2c.value)

    # Создание компонента ШИМ модели pca9685
    errCode = lib.RI_SDK_CreateModelComponent("connector".encode(), "pwm".encode(), "pca9685".encode(), pwm, errTextC)
    if errCode != 0:
        print(errCode, errTextC.raw.decode())
        sys.exit(2)

    print("pwm: ", pwm.value)

     # Создание компонента светодиода модели ky016
    errCode = lib.RI_SDK_CreateModelComponent("executor".encode(), "led".encode(), "ky016".encode(), led, errTextC)
    if errCode != 0:
        print(errCode, errTextC.raw.decode())
        sys.exit(2)

    print("led: ", led.value)

    # Связывание i2c с ШИМ
    errCode = lib.RI_SDK_LinkPWMToController(pwm, i2c, 0x40, errTextC)
    if errCode != 0:
        print(errCode, errTextC.raw.decode())
        sys.exit(2)
    
    # Связывание ШИМ со светодиодом
    errCode = lib.RI_SDK_LinkLedToController(led, pwm, 15, 14, 13, errTextC)
    if errCode != 0:
        print(errCode, errTextC.raw.decode())
        sys.exit(2)

    # Непрерывное свечение светодиода красным цветом в течение 10 секунд.
    # Яркость красного цвета - 150. Яркость остальных цветов - 0.
    errCode = lib.RI_SDK_exec_RGB_LED_SinglePulse(led, 150, 0, 0, 10000, False, errTextC)
    if errCode != 0:
        print(errCode, errTextC.raw.decode())
        sys.exit(2) 

    # Удаление библиотеки со всеми компонентами
    errCode = lib.RI_SDK_DestroySDK(True, errTextC)
    if errCode != 0:
        print(errCode, errTextC.raw.decode())
        sys.exit(2)

    print("Success")

main()

Прежде всего необходимо подключить файл библиотеки librisdk.dll с помощью функции cdll.LoadLibrary:

lib = cdll.LoadLibrary("C:\Windows\system32\librisdk.dll")

Здесь предполагается, что в процессе установки пульта управления роботом-манипулятором, описанным ранее в этой главе, были скопированы в системный каталог  C:\Windows\system32\ все необходимые файлы DLL библиотек.

После загрузки библиотеки с помощью функции RI_SDK_CreateModelComponent программа создает компонент i2c для адаптера ch341, установленного на плате контроллера Robointellect Controller 001:

i2c = c_int()
…
errCode = lib.RI_SDK_CreateModelComponent("connector".encode(), "i2c_adapter".encode(), "ch341".encode(), i2c, errTextC)
if errCode != 0:
    print(errCode, errTextC.raw.decode())
    sys.exit(2)

print("i2c: ", i2c.value)

На следующем шаге наша программа создает компонент ШИМ модели pca9685, вызывая ту же функцию RI_SDK_CreateModelComponent:

pwm = c_int()
…
errCode = lib.RI_SDK_CreateModelComponent("connector".encode(), "pwm".encode(), "pca9685".encode(), pwm, errTextC)

Также требуется создать компонент для GRB светодиода модели ky016, установленного на плате контроллера:

led = c_int()
…
errCode = lib.RI_SDK_CreateModelComponent("executor".encode(), "led".encode(), "ky016".encode(), led, errTextC)

После создания компонент для i2c, ШИМ и светодиода нужно связать i2c и ШИМ, а ШИМ — со светодиодом:

errCode = lib.RI_SDK_LinkPWMToController(pwm, i2c, 0x40, errTextC)
…    
errCode = lib.RI_SDK_LinkLedToController(led, pwm, 15, 14, 13, errTextC)

Для привязки i2c к ШИМ используется функция RI_SDK_LinkPWMToController. Обратите внимание, что в качестве третьего параметра передается адрес ШИМ контроллера на шине i2c, равный 40.

Привязка светодиода выполняется функцией RI_SDK_LinkLedToController. Через третий, четвертый и пятый параметры этой функции нужно передать номера каналов ШИМ на плате контроллера Robointellect Controller 001, к которым подключены, соответственно, красный (R), зеленый (G) и голубой (B) контакт RGB светодиода, соответственно. По умолчанию это каналы 15, 14 и 13.

Обращаем ваше внимание, что на плате Robointellect Controller 001 имеется перемычка ON/OFF RGB LED. Если ее удалить, перечисленные выше каналы будут отключены от встроенного светодиода, и их можно будет использовать для подключения других устройств.

Теперь программа готова для работы со светодиодом. В примере программы с помощью функции RI_SDK_exec_RGB_LED_SinglePulse задается непрерывное свечение светодиода красным цветом в течение 10 секунд:

errCode = lib.RI_SDK_exec_RGB_LED_SinglePulse(led, 150, 0, 0, 10000, False, errTextC)

Второй, третий и четвертый параметры задают яркости для красного, зеленого и голубого цвета в диапазоне от 0 до 255. В приведенном примере горит только красный свет с яркостью 155.

Пятый параметр задает длительность свечения в мс. В данном случае светодиод будет гореть 10 секунд.

Шестой параметр позволяет запустить выполнение команды в асинхронном режиме. При этом функция сразу вернет управление.

Перед завершением программы нужно удалить библиотеку со всеми компонентами. Это действие выполняется функцией RI_SDK_DestroySDK:

errCode = lib.RI_SDK_DestroySDK(True, errTextC)

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

Программа risk_led_demo.py, представленная в листинге 2, демонстрирует все функции управления RGB светодиодом, реализованные в RISDK, и работающие в синхронном режиме.

Листинг 2. https://raw.githubusercontent.com/AlexandreFrolov/ri-controller-001-risdk/main/risdk_led_demo.py

import sys
from ctypes import *
import platform
import traceback

def err_msg(errTextC):
    return(errTextC.raw.decode())

def init(i2c, pwm):
    platform_os = platform.system()
    try:
        if platform_os == "Windows":
            lib = cdll.LoadLibrary("C:\Windows\system32\librisdk.dll")
        if platform_os == "Linux":
            lib = cdll.LoadLibrary("/usr/local/robohand_remote_control/librisdk.so")
    except OSError as e:
        raise Exception("Failed to load: " + str(e))

    lib.RI_SDK_InitSDK.argtypes = [c_int, c_char_p]
    lib.RI_SDK_CreateModelComponent.argtypes = [c_char_p, c_char_p, c_char_p, POINTER(c_int), c_char_p]
    lib.RI_SDK_LinkPWMToController.argtypes = [c_int, c_int, c_uint8, c_char_p]

    errTextC = create_string_buffer(1000)
    errCode = lib.RI_SDK_InitSDK(3, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_InitSDK failed with error code {errCode}: {err_msg(errTextC)}")        

    errCode = lib.RI_SDK_CreateModelComponent("connector".encode(), "i2c_adapter".encode(), "ch341".encode(), i2c, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_CreateModelComponent failed with error code {errCode}: {err_msg(errTextC)}")        

    errCode = lib.RI_SDK_CreateModelComponent("connector".encode(), "pwm".encode(), "pca9685".encode(), pwm, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_CreateModelComponent failed with error code {errCode}: {err_msg(errTextC)}")        

    errCode = lib.RI_SDK_LinkPWMToController(pwm, i2c, 0x40, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_LinkPWMToController failed with error code {errCode}: {err_msg(errTextC)}")        

    return lib
    
def add_led(lib, led, pwm, r, g, b):
    lib.RI_SDK_CreateModelComponent.argtypes = [c_char_p, c_char_p, c_char_p, POINTER(c_int), c_char_p]
    lib.RI_SDK_LinkLedToController.argtypes = [c_int, c_int, c_int, c_int, c_int, c_char_p]

    errTextC = create_string_buffer(1000)
    errCode = lib.RI_SDK_CreateModelComponent("executor".encode(), "led".encode(), "ky016".encode(), led, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_CreateModelComponent failed with error code {errCode}: {err_msg(errTextC)}")        

    errCode = lib.RI_SDK_LinkLedToController(led, pwm, 15, 14, 13, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_LinkLedToController failed with error code {errCode}: {err_msg(errTextC)}")        

def cleanup(lib):
    lib.RI_SDK_DestroySDK.argtypes = [c_bool, c_char_p]

    errTextC = create_string_buffer(1000)
    errCode = lib.RI_SDK_DestroySDK(True, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_DestroySDK failed with error code {errCode}: {err_msg(errTextC)}")        

def led_flicker(lib, led, r, g, b, duration, qty, async_mode):
    lib.RI_SDK_exec_RGB_LED_Flicker.argtypes = [c_int, c_int, c_int, c_int, c_int, c_int, c_bool, c_char_p]

    errTextC = create_string_buffer(1000)
    errCode = lib.RI_SDK_exec_RGB_LED_Flicker(led, r, g, b, duration, qty, async_mode, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_exec_RGB_LED_Flicker failed with error code {errCode}: {err_msg(errTextC)}")        

def led_pulse(lib, led, r, g, b, duration, async_mode):
    lib.RI_SDK_exec_RGB_LED_SinglePulse.argtypes = [c_int, c_int, c_int, c_int, c_int, c_bool, c_char_p]
    errTextC = create_string_buffer(1000)
    errCode = lib.RI_SDK_exec_RGB_LED_SinglePulse(led, r, g, b, duration, async_mode, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_exec_RGB_LED_SinglePulse failed with error code {errCode}: {err_msg(errTextC)}")        

def led_pulse_pause(lib, led, r, g, b, duration, pause, limit, async_mode):
    lib.RI_SDK_exec_RGB_LED_FlashingWithPause.argtypes = [c_int, c_int, c_int, c_int, c_int, c_int, c_int, c_bool, c_char_p]

    errTextC = create_string_buffer(1000)
    errCode = lib.RI_SDK_exec_RGB_LED_FlashingWithPause(led, r, g, b, duration, pause, limit, async_mode, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_exec_RGB_LED_FlashingWithPause failed with error code {errCode}: {err_msg(errTextC)}")        

def led_pulse_frequency(lib, led, r, g, b, frequency, limit, async_mode):
    lib.RI_SDK_exec_RGB_LED_FlashingWithFrequency.argtypes = [c_int, c_int, c_int, c_int, c_int, c_int, c_bool, c_char_p]

    errTextC = create_string_buffer(1000)
    errCode = lib.RI_SDK_exec_RGB_LED_FlashingWithFrequency(led, r, g, b, frequency, limit, async_mode, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_exec_RGB_LED_FlashingWithFrequency failed with error code {errCode}: {err_msg(errTextC)}")        

def led_cleanup(lib, led):
    lib.RI_SDK_DestroyComponent.argtypes = [c_int, c_char_p]

    errTextC = create_string_buffer(1000)
    errCode = lib.RI_SDK_DestroyComponent(led, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_DestroyComponent failed with error code {errCode}: {err_msg(errTextC)}")        


if __name__ == "__main__":
    try:
        i2c = c_int()
        pwm = c_int()
        led = c_int()
        
        lib = init(i2c, pwm)

#        add_led(lib, led, pwm, 15, 14, 13)
        add_led(lib, led, pwm, 14, 15, 13)

        print("Start pulse...")

        led_pulse(lib, led, 255, 0, 0, 1500, False)
        led_pulse(lib, led, 0, 255, 0, 1500, False)
        led_pulse(lib, led, 0, 0, 255, 1500, False)

        print("Start flicker...")

        led_flicker(lib, led, 255, 0, 0, 500, 5, False)
        led_flicker(lib, led, 0, 255, 0, 500, 5, False)
        led_flicker(lib, led, 0, 0, 255, 500, 5, False)

        print("Start pulse_pause...")

        led_pulse_pause(lib, led, 255, 0, 0, 1000, 200, 3, False)
        led_pulse_pause(lib, led, 0, 255, 0, 1000, 200, 3, False)
        led_pulse_pause(lib, led, 0, 0, 255, 1000, 200, 3, False)

        print("Start pulse_frequency...")

        led_pulse_frequency(lib, led, 255, 0, 0, 10, 10, False)
        led_pulse_frequency(lib, led, 0, 255, 0, 20, 10, False)
        led_pulse_frequency(lib, led, 0, 0, 255, 30, 10, False)

        led_cleanup(lib, led)
        cleanup(lib)
    except Exception as e:
        print(traceback.format_exc() + "===> ", str(e))
        sys.exit(2)

Инициализация

В самом начале этой программы определены переменные i2c и pwm, необходимые для инициализации контроллера Robointellect Controller 001, а также переменная led для работы со светодиодом:

i2c = c_int()
pwm = c_int()
led = c_int()
lib = init(i2c, pwm)

Инициализация выполняется функцией init, которой передаются переменные i2c и pwm:

def init(i2c, pwm):
    platform_os = platform.system()
    try:
        if platform_os == "Windows":
            lib = cdll.LoadLibrary("C:\Windows\system32\librisdk.dll")
        if platform_os == "Linux":
            lib = cdll.LoadLibrary("/usr/local/robohand_remote_control/librisdk.so")
    except OSError as e:
        raise Exception("Failed to load: " + str(e))

    lib.RI_SDK_InitSDK.argtypes = [c_int, c_char_p]
    lib.RI_SDK_CreateModelComponent.argtypes = [c_char_p, c_char_p, c_char_p, POINTER(c_int), c_char_p]
    lib.RI_SDK_LinkPWMToController.argtypes = [c_int, c_int, c_uint8, c_char_p]

    errTextC = create_string_buffer(1000)
    errCode = lib.RI_SDK_InitSDK(3, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_InitSDK failed with error code {errCode}: {err_msg(errTextC)}")        

    errCode = lib.RI_SDK_CreateModelComponent("connector".encode(), "i2c_adapter".encode(), "ch341".encode(), i2c, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_CreateModelComponent failed with error code {errCode}: {err_msg(errTextC)}")        

    errCode = lib.RI_SDK_CreateModelComponent("connector".encode(), "pwm".encode(), "pca9685".encode(), pwm, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_CreateModelComponent failed with error code {errCode}: {err_msg(errTextC)}")        

    errCode = lib.RI_SDK_LinkPWMToController(pwm, i2c, 0x40, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_LinkPWMToController failed with error code {errCode}: {err_msg(errTextC)}")        

    return lib

Для инициализации RISDK вызывается функция RI_SDK_InitSDK. В качестве первого параметра ей передается уровень глубины логирования (0 — только верхний уровень сообщений, 1, 2 и 3 — более подробная трассировка).

В качестве второго параметра функции необходимо передать ссылку на строку errTextC, определенную как буфер размером 1000 байт:

errTextC = create_string_buffer(1000)

Заметим, что такая строка используется для записи текста сообщения об ошибке во всех функциях RISDK.

На следующем этапе инициализации вызывается функция RI_SDK_CreateModelComponent, создающие компоненты для моста USB-I2C (микросхема ch341) и PWM-модуляция (микросхема pca9685).

В качестве первого параметра функции RI_SDK_CreateModelComponent нужно передать тип компонента — «executor», «connector» или «sensor». В нашем примере мы указываем тип «connector».

Второй параметр позволяет указать устройство компонента. Это могут быть такие устройства: «i2c», «pwm», «servodrive», «servodrive_rotate», «led» или, «voltage_sensor».

Третий параметр задает модель компонента. В библиотеке RISDK определены компоненты следующих типов:

  • контроллеры ch341, cp2112, pca9685;
  • сервоприводы mg90s, a0090, mg996, corona_ds929mg, corona_sb9039, corona_ds843mg, corona_ds238mg, mg996r;
  • светодиод ky016;
  • измеритель тока ina219

В четвертом параметре нужно указать ссылку на переменную, в которую будет записан объект созданного компонента.

Наша функция init вызывает функцию RI_SDK_CreateModelComponent три раза, создавая компоненты для моста ch341, а также контроллеров ch341 и pca9685.

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

Последний этап инициализации заключается в присоединении ШИМ-модулятора к адаптеру i2c с помощью функции RI_SDK_LinkPWMToController библиотеки RISDK.

Через первый и второй параметры этой функции нужно передать дескрипторы ШИМ и адаптера i2c, участвующие в присоединении.

Третий параметр задает адрес адаптера на шине i2c.

Последний параметр предназначен для получения текста ошибки (если она возникнет).

Добавление светодиода

Прежде чем программа сможет работать со светодиодом RGB, она должна добавить его к контроллеру, указав тип светодиода и номера каналов ШИМ на плате контроллера Robointellect Controller 001, к которому подключен светодиод.

Ниже приведен код функции add_led, выполняющий операцию добавления:

def add_led(lib, led, pwm, r, g, b):
    lib.RI_SDK_CreateModelComponent.argtypes = [c_char_p, c_char_p, c_char_p, POINTER(c_int), c_char_p]
    lib.RI_SDK_LinkLedToController.argtypes = [c_int, c_int, c_int, c_int, c_int, c_char_p]

    errTextC = create_string_buffer(1000)
    errCode = lib.RI_SDK_CreateModelComponent("executor".encode(), "led".encode(), "ky016".encode(), led, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_CreateModelComponent failed with error code {errCode}: {err_msg(errTextC)}")        

    errCode = lib.RI_SDK_LinkLedToController(led, pwm, r, g, b, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_LinkLedToController failed with error code {errCode}: {err_msg(errTextC)}")

Эта функция создает компонент светодиода функцией RI_SDK_CreateModelComponent, а затем подключает этот компонент к контроллеру, вызывая RI_SDK_LinkLedToController.

Через первый и второй параметры функции передаются соответственно дескриптор светодиода и контроллера ШИМ, созданные на этапе инициализации.

Следующие параметры задают порты подключения на шине ШИМ контроллера красного, зеленого и синего светодиодов (установленных внутри светодиода RGB), соответственно.

Последний параметр нужен для передачи текста ошибки.

Наша программа добавляет светодиод следующим образом:

# add_led(lib, led, pwm, 15, 14, 13)
add_led(lib, led, pwm, 14, 15, 13)

Обратите внимание, что вместо последовательности (15,14, 13) здесь указано (14, 15, 13). В экземпляре контроллера, который был у автора этой статьи, в светодиоде RGB оказались перепутаны контакты. Поэтому пришлось при добавлении светодиода сделать коррекцию.

Операции со светодиодом

Когда светодиод добавлен, можно с ним работать. Сначала программа вызывает функцию led_pulse, включающую светодиод с помощью функции RI_SDK_exec_RGB_LED_SinglePulse библиотеки RISDK:

led_pulse(lib, led, 255, 0, 0, 1500, False)
led_pulse(lib, led, 0, 255, 0, 1500, False)
led_pulse(lib, led, 0, 0, 255, 1500, False)

Здесь программа последовательно зажигает светодиод красным, зеленым и голубым цветом на полторы секунды.

Код функции led_pulse приведен ниже:

def led_pulse(lib, led, r, g, b, duration, async_mode):
    lib.RI_SDK_exec_RGB_LED_SinglePulse.argtypes = [c_int, c_int, c_int, c_int, c_int, c_bool, c_char_p]
    errTextC = create_string_buffer(1000)
    errCode = lib.RI_SDK_exec_RGB_LED_SinglePulse(led, r, g, b, duration, async_mode, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_exec_RGB_LED_SinglePulse failed with error code {errCode}: {err_msg(errTextC)}")

Через первый параметр функции RI_SDK_exec_RGB_LED_SinglePulse передается дескриптор светодиода, полученный от функции add_led.

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

Пятый параметр позволяет выбрать синхронный или асинхронный режим работы функции.

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

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

После вызова led_pulse программа вызывает еще три функции:

led_flicker(lib, led, 255, 0, 0, 500, 5, False)
led_flicker(lib, led, 0, 255, 0, 500, 5, False)
led_flicker(lib, led, 0, 0, 255, 500, 5, False)

led_pulse_pause(lib, led, 255, 0, 0, 1000, 200, 3, False)
led_pulse_pause(lib, led, 0, 255, 0, 1000, 200, 3, False)
led_pulse_pause(lib, led, 0, 0, 255, 1000, 200, 3, False)

led_pulse_frequency(lib, led, 255, 0, 0, 10, 10, False)
led_pulse_frequency(lib, led, 0, 255, 0, 20, 10, False)
led_pulse_frequency(lib, led, 0, 0, 255, 30, 10, False)

Функция led_flicker реализована с помощью функции RI_SDK_exec_RGB_LED_Flicker и вызывает мерцание светодиода и постепенным изменением яркости от нулевой до значения, указанного третьим, четвертым и пятым параметрами.

Шестой параметр задает длительность мерцания в мс, а седьмой — количество мерцаний.

Таким образом, приведенный выше фрагмент кода с функцией led_flicker вызывает последовательное мерцание светодиода красным, зеленым и голубым цветом.

Функция led_pulse_pause реализована при помощи функции RI_SDK_exec_RGB_LED_FlashingWithPause.

Здесь светодиод мигает цветом, заданным при помощи третьего, четвертого и пятого параметров. Шестой параметр задает длительность одного импульса в мс, седьмой — паузу между импульсами в мс, а восьмой — количество миганий.

Если нужно мигать светодиодом с заданной частотой, используйте функцию led_pulse_frequency, реализованную с помощью RI_SDK_exec_RGB_LED_FlashingWithFrequency.

Третий, четвертый и пятый параметры задают цвет мигания, шестой — частоту мигания в Гц, а седьмой — количество миганий.

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

Освобождение ресурсов

Перед завершением работы программа должна освободить ресурсы, занятые светодиодом и библиотекой RISDK. Для этого она вызывает функции  led_cleanup и cleanup:

led_cleanup(lib, led)
cleanup(lib

Функция led_cleanup вызывает функцию RI_SDK_DestroyComponent библиотеки RISDK. Она удаляет компонент, созданный для светодиода RGB.

Что касается функции cleanup, то она вызывает функции RI_SDK_sigmod_PWM_ResetAll, RI_SDK_DestroyComponent и RI_SDK_DestroySDK.

Функция RI_SDK_sigmod_PWM_ResetAll устанавливает скважность, равную нулю на всех портах  ШИМ модулятора. С помощью функции RI_SDK_DestroyComponent удаляется компонент i2c. И, наконец, функция RI_SDK_DestroySDK освобождает память, выделенную RISDK.

Асинхронное управление светодиодом

Библиотека RISDK может работать в асинхронном режиме. При этом асинхронные функции возвращают управление в вызывающую программу немедленно, а вызванный ими процесс будет продолжен до завершения или отмены.

В листинге 3 мы привели в сокращенном виде пример программы risdk_led_demo_async.py асинхронного управления светодиодом RGB.

Листинг 3. https://raw.githubusercontent.com/AlexandreFrolov/ri-controller-001-risdk/main/risdk_led_demo_async.py

…
if __name__ == "__main__":
    try:
        i2c = c_int()
        pwm = c_int()
        led = c_int()
        
        lib = init(i2c, pwm)

#        add_led(lib, led, pwm, 15, 14, 13)
        add_led(lib, led, pwm, 14, 15, 13)
        print_led_state(lib, led)


        print("Start pulse...")

        led_pulse(lib, led, 255, 0, 0, 1500, True)
        time.sleep(0.3) 
        print_led_state(lib, led)
        time.sleep(2) 
        led_pulse(lib, led, 0, 255, 0, 1500, True)
        time.sleep(0.3) 
        print_led_state(lib, led)
        time.sleep(2) 
        led_pulse(lib, led, 0, 0, 255, 1500, True)
        time.sleep(0.3) 
        print_led_state(lib, led)
        time.sleep(2) 

        print("Start flicker...")

        led_flicker(lib, led, 255, 0, 0, 500, 5, True)
        time.sleep(2) 
        led_flicker(lib, led, 0, 255, 0, 500, 5, True)
        time.sleep(2) 
        led_flicker(lib, led, 0, 0, 255, 500, 5, True)
        time.sleep(2) 

        print("Start pulse_pause...")

        led_pulse_pause(lib, led, 255, 0, 0, 1000, 200, 3, True)
        time.sleep(2) 
        led_pulse_pause(lib, led, 0, 255, 0, 1000, 200, 3, True)
        time.sleep(2) 
        led_pulse_pause(lib, led, 0, 0, 255, 1000, 200, 3, True)
        time.sleep(2) 

        print("Start pulse_frequency...")

        led_pulse_frequency(lib, led, 255, 0, 0, 10, 10, True)
        time.sleep(2) 
        led_pulse_frequency(lib, led, 0, 255, 0, 10, 10, True)
        time.sleep(2) 
        led_pulse_frequency(lib, led, 0, 0, 255, 10, 10, True)
        time.sleep(2) 

        led_stop(lib)
        led_cleanup(lib, led)
        cleanup(lib)
    except Exception as e:
        print(traceback.format_exc() + "===> ", str(e))
        sys.exit(2)

Обратите внимание, что в качестве последнего параметра мы передаем функциям, управляющим светодиодом, значение True, а не False, как это было в предыдущем примере (листинг 2). В результате все функции RISDK вызываются в асинхронном режиме.

После вызова метода led_pulse добавлены две задержки. Первая вызывается перед функцией print_led_state, которая выводит на консоль состояние светодиода, а вторая — после вывода этого состояния.

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

Для получения состояния и текущих компонентов цвета светодиода RGB здесь вызывается функция led_get_state, основанная на вызове функции RI_SDK_exec_RGB_LED_GetState библиотеки RISDK.

Функция RI_SDK_exec_RGB_LED_GetState записывает в переменную, адрес которой указан во втором параметре, код состояния для светодиода, дескриптор которого указан в первом параметре.

Возможны такие значения состояния:

  • 0 — компонент ожидает вызова действий, светодиод не выполняет никакую команду;
  • 1 — компонент выполняет действие;
  • 2 — простое свечение, светодиод выполняет команду простого свечения;
  • 3 — мигание, светодиод выполняет одну из 2-х команд мигания;
  • 4 — мерцание, светодиод выполняет команду мерцания

Текущий цвет светодиода возвращается функцией led_get_color, которая, в свою очередь, вызывает функцию RI_SDK_exec_RGB_LED_GetColor библиотеки RISDK.

Также обратите внимание на функцию led_stop, полезную в асинхронном режиме  и останавливающую выполнение текущей операции для светодиода. Она вызывает функцию RI_SDK_exec_RGB_LED_Stop.

Управление сервоприводами с удержанием угла

Для управления сервоприводами с удержанием угла была создана программа risdk_servo_mg90s.py. Исходный текст этой программы приведен в листинге 4 (в сокращенном виде).

Листинг 4. https://raw.githubusercontent.com/AlexandreFrolov/ri-controller-001-risdk/main/risdk_servo_mg90s.py

…
i2c = c_int()
pwm = c_int()
mg90s = c_int()

lib = init(i2c, pwm)

servo_add(lib, pwm, mg90s, "mg90s", 0)
print_servo_state(lib, mg90s)

print("\nMG90S поворот в крайние положения")

servo_rotate(lib, mg90s, 0, 200, False)
time.sleep(2) 
print("mg90s angle: " + str(servo_get_angle(lib, mg90s)))
print_servo_state(lib, mg90s)

servo_rotate(lib, mg90s, 1, 200, False)
time.sleep(2) 
print("mg90s angle: " + str(servo_get_angle(lib, mg90s)))

servo_set_middle(lib, mg90s)
time.sleep(2) 
print("mg90s angle: " + str(servo_get_angle(lib, mg90s)))


print("\nMG90S управление через длительность импульсов")

servo_turn_by_pulse(lib, mg90s, 2650)
time.sleep(2) 
print("mg90s angle: " + str(servo_get_angle(lib, mg90s)))

servo_turn_by_pulse(lib, mg90s, 365)
time.sleep(2) 
print("mg90s angle: " + str(servo_get_angle(lib, mg90s)))
        
servo_turn_by_pulse(lib, mg90s,1500)
time.sleep(2) 
print("mg90s angle: " + str(servo_get_angle(lib, mg90s)))
        

print("\nMG90S Минимальный шаг")

servo_set_middle(lib, mg90s)
time.sleep(2) 
print("mg90s angle: " + str(servo_get_angle(lib, mg90s)))

servo_rotate_min_step(lib, mg90s, 1, 100, False)
time.sleep(2) 
print("mg90s angle: " + str(servo_get_angle(lib, mg90s)))

servo_rotate_min_step(lib, mg90s, 0, 100, False)
time.sleep(2) 
print("mg90s angle: " + str(servo_get_angle(lib, mg90s)))

print("\nMG90S управление через Duty")

servo_turn_by_duty(lib, mg90s, 75)
time.sleep(2) 
print("mg90s angle: " + str(servo_get_angle(lib, mg90s)))

servo_turn_by_duty(lib, mg90s, 300)
time.sleep(2) 
print("mg90s angle: " + str(servo_get_angle(lib, mg90s)))

servo_turn_by_duty(lib, mg90s, 540)
time.sleep(2) 
print("mg90s angle: " + str(servo_get_angle(lib, mg90s)))
     
print("\nMG90S поворот на заданный угол")

servo_set_middle(lib, mg90s)
time.sleep(2) 
print("mg90s angle: " + str(servo_get_angle(lib, mg90s)))

servo_turn_by_angle(lib, mg90s, 90, 200, False)
time.sleep(1) 
print("mg90s angle: " + str(servo_get_angle(lib, mg90s)))

servo_turn_by_angle(lib, mg90s, -90, 300, False)
time.sleep(1) 
print("mg90s angle: " + str(servo_get_angle(lib, mg90s)))

cleanup_servo(lib, mg90s)
cleanup(lib)
…

Инициализация

На этапе инициализации с помощью функции init программа risdk_servo_mg90s.py создает переменные i2c, pwm и mg90s:

i2c = c_int()
pwm = c_int()
mg90s = c_int()
lib = init(i2c, pwm)

Работа этой функции уже была описана ранее в предыдущем разделе (листинге 2).

Добавление сервопривода

Для работы с сервоприводом его нужно добавить функцией servo_add:

servo_add(lib, pwm, mg90s, "mg90s", 0)

В качестве третьего параметра функции servo_add нужно передать переменную для сохранения дескриптора сервопривода, в качестве четвертого — имя модели сервопривода, а в качестве пятого — номер канала ШИМ контроллера Robointellect Controller 001, к которому подключен сервопривод.

Функция servo_add вначале вызывает функцию RI_SDK_CreateModelComponent для создания компонента уровня устройства, а затем — функцию RI_SDK_LinkServodriveToController для привязки контроллера к ШИМ модулятору:

def servo_add(lib, pwm, servo, servo_type, channel):
    lib.RI_SDK_CreateModelComponent.argtypes = [c_char_p, c_char_p, c_char_p, POINTER(c_int), c_char_p]
    lib.RI_SDK_LinkPWMToController.argtypes = [c_int, c_int, c_uint8, c_char_p]

    errTextC = create_string_buffer(1000)
    errCode = lib.RI_SDK_CreateModelComponent("executor".encode(), "servodrive".encode(), servo_type.encode(), servo, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_CreateModelComponent failed with error code {errCode}: {err_msg(errTextC)}")

    errCode = lib.RI_SDK_LinkServodriveToController(servo, pwm, channel, errTextC)
    if errCode != 0:
        raise Exception(f"RI_SDK_LinkServodriveToController failed with error code {errCode}: {err_msg(errTextC)}")

Определение текущего состояния сервопривода

После добавления сервопривода программа вызывает функцию print_servo_state чтобы вывести на консоль состояние сервопривода:

print_servo_state(lib, mg90s)

Функция print_servo_state определена так:

def print_servo_state(lib, servo):
    print(f"Servo state: : {str(servo_get_state(lib, servo).value)}")

Она получает состояние с помощью функции servo_get_state. Эта функция, в свою очередь, вызывает функцию RI_SDK_exec_ServoDrive_GetState.

Функция  RI_SDK_exec_ServoDrive_GetState может вернуть одно из двух значений:

  • 0 — сервопривод ожидает вызова действий, ничего не делает и готов к работе;
  • 1 — компонент выполняет действие. Сервопривод в данный момент осуществляет движение

Таким образом, если функция servo_get_state возвращает нулевое значение, сервопривод находится в состоянии ожидания, если же значение единицы — вал сервопривода находится в движении.

Поворот в крайние положения

В программе risdk_servo_mg90s.py есть функция servo_rotate для поворота вала сервопривода в крайние положения, а также функция servo_set_middle для установки вала в среднее положение:

servo_rotate(lib, mg90s, 0, 200, False)
servo_rotate(lib, mg90s, 1, 200, False)
servo_set_middle(lib, mg90s)

Функции servo_rotate, реализованная с помощью функции RI_SDK_exec_ServoDrive_Rotate, в качестве первого параметра нужно передать направление вращения. Значение 0 соответствует вращению по часовой стрелке, а значение 1 — против часовой стрелки.

Второй параметр функции servo_rotate задает угловую скорость поворота (градусы в секунду).

Функция servo_set_middle  работает с помощью функции RI_SDK_exec_ServoDrive_SetPositionToMidWorkingRange. Она устанавливает вал сервопривода в среднее положение (в середину рабочего диапазона углов поворота).

Определение текущего угла поворота

При выполнении операций с сервоприводом программа risdk_servo_mg90s.py после выполнения тех или иных операций выводит на консоль текущий угол поворота вала сервопривода:

print("mg90s angle: " + str(servo_get_angle(lib, mg90s)))

Текущий угол поворота определяется функцией servo_get_angle, вызывающей функцию RI_SDK_exec_ServoDrive_GetCurrentAngle библиотеки RISDK. Это значение вычисляется на основе подсчета управляющих импульсов.

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

Управление через длительность импульсов

Функция servo_turn_by_pulse позволяет управлять вращением вала сервопривода, задавая длительность управляющих импульсов в мс:

servo_turn_by_pulse(lib, mg90s, 2650)
servo_turn_by_pulse(lib, mg90s, 365)
servo_turn_by_pulse(lib, mg90s, 1500)

Длительность передается этим функциям в качестве последнего параметра.

Функция servo_turn_by_pulse вызывает функцию RI_SDK_exec_ServoDrive_TurnByPulse библиотеки RISDK. В качестве первого параметра этой функции передается дескриптор сервопривода, в качестве второго — необходимая длительность импульсов, а в качестве третьего — ссылка на буфер для записи строки сообщения об ошибке.

Управление через минимальный шаг

Если нужно перемещать вал сервопривода минимальными шагами, по одному градусу, пригодится функция servo_rotate_min_step, созданная на базе функции RI_SDK_exec_ServoDrive_MinStepRotate.

Фрагмент программы, приведенный ниже, вначале устанавливает вал сервопривода в среднее положение, а затем поворачивает его на один градус сначала по часовой стрелке, а затем — против часовой стрелке:

servo_set_middle(lib, mg90s)
servo_rotate_min_step(lib, mg90s, 1, 100, False)
servo_rotate_min_step(lib, mg90s, 0, 100, False)

Угловая скорость поворота задана как 100⁰ в секунду.

В качестве первого параметра функции RI_SDK_exec_ServoDrive_MinStepRotate нужно передать дескриптор сервопривода.

Второй и третий параметры задают, соответственно, направление и угловую скорость поворота (градусы в секунду).

Направление движения задается так:

  • 0 — по часовой стрелке;
  • 1 — против часовой стрелки

Управление через скважность импульсов Duty

Функция servo_turn_by_duty, реализованная с помощью функции библиотеки RISDK RI_SDK_exec_ServoDrive_TurnByDutyCycle, позволяет задавать угол поворота вала сервопривода через скважность управляющих импульсов:

servo_turn_by_duty(lib, mg90s, 75)
servo_turn_by_duty(lib, mg90s, 300)
servo_turn_by_duty(lib, mg90s, 540)

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

Поворот на заданный угол

Функция servo_turn_by_angle позволяет поворачивать вал сервопривода на заданный угол по часовой стрелке или против часовой стрелке. Она вызывает функцию RI_SDK_exec_ServoDrive_Turn библиотеки RISDK.

Ниже приведен фрагмент программы, который вначале устанавливает вал сервопривода в среднее положение, а затем поворачивает его почасовой стрелке на 90⁰ и вслед за этим против часовой стрелки на -90⁰:

servo_set_middle(lib, mg90s)
servo_turn_by_angle(lib, mg90s, 90, 200, False)
servo_turn_by_angle(lib, mg90s, -90, 300, False)

Освобождение ресурсов

Перед завершением своей работы программа освобождает ресурсы, выделенные для сервопривода и для библиотеки RISDK:

cleanup_servo(lib, mg90s)
cleanup(lib)

Функция cleanup_servo вызывает функцию RI_SDK_DestroyComponent библиотеки RISDK для освобождения ресурсов, выделенных сервоприводу. Дескриптор сервопривода передается этой функции в качестве первого параметра.

Функция cleanup обращается к функции RI_SDK_DestroySDK, передавая ей в качестве первого параметра значение True для полного очищения реестра.

Подключение нестандартного сервопривода

Библиотека RISDK позволяет вам работать с нестандартными сервоприводами, названия и параметры которых не определены в этой библиотеке.

В листинге 5 показана в сокращенном виде программа risdk_servo_sg90.py, которая демонстрирует такую поддержку для широко распространенного и недорогого сервопривода SG90.

Листинг 5. https://raw.githubusercontent.com/AlexandreFrolov/ri-controller-001-risdk/main/risdk_servo_sg90.py

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

Инициализация выполняется функцией add_custom_servo:

sg90 = add_custom_servo(lib, pwm, sg90, 2350, 365, 200, 180, 0)

Она задает параметры нашего сервопривода SG90 и вызывает функции RI_SDK_CreateDeviceComponent, RI_SDK_exec_ServoDrive_CustomDeviceInit и RI_SDK_LinkServodriveToController.

Параметры нестандартного сервопривода передаются функции RI_SDK_exec_ServoDrive_CustomDeviceInit.

Через первый параметр функция RI_SDK_exec_ServoDrive_CustomDeviceInit получает указатель на переменную, в которую будет записан дескриптор нестандартного сервопривода после инициализации.

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

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

Четвертый параметр задает максимально допустимую угловую скорость вращения сервопривода (градусы в секунду).

Пятый параметр определяет максимальный угол поворота вала сервопривода в градусах.

И, наконец, в шестом параметре необходимо указать номер канала ШИМ контроллера Robointellect Controller 001, к которому подключен сервопривод.

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

servo_rotate(lib, sg90, 0, 200, False)
servo_rotate(lib, sg90, 1, 200, False)
servo_set_middle(lib, sg90)

servo_turn_by_pulse(lib, sg90, 2350)
servo_turn_by_pulse(lib, sg90, 365)
servo_turn_by_pulse(lib, sg90,1500)

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

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

Стандартные сервоприводы

В листинге 6 вы найдете в сокращенном виде код программы управления сервоприводом постоянного вращения risdk_servo_mg996r.py.

Листинг 6. https://raw.githubusercontent.com/AlexandreFrolov/ri-controller-001-risdk/main/risdk_servo_mg996r.py

…
i2c = c_int()
pwm = c_int()
mg996r = c_int()

lib = init(i2c, pwm)

rservo_add(lib, pwm, mg996r,"mg996r", 0)
print_rservo_state(lib, mg996r)

rservo_rotate_by_pulse(lib, mg996r, 1050, True)
print_rservo_state(lib, mg996r)
time.sleep(3) 

rservo_rotate_by_pulse(lib, mg996r, 2100, True)
print_rservo_state(lib, mg996r)
time.sleep(3) 

rservo_rotate_by_pulse(lib, mg996r, 1570, True)
print_rservo_state(lib, mg996r)
time.sleep(3) 

rservo_rotate_at_speed(lib, mg996r, 0, 50, True)
time.sleep(3) 

rservo_rotate_at_speed(lib, mg996r, 1, 100, True)
time.sleep(3) 

stop_rservo(lib, mg996r)
print_rservo_state(lib, mg996r)

cleanup_servo(lib, mg996r)
cleanup(lib)

После инициализации функцией init программа добавляет сервопривод постоянного вращения с помощью функции rservo_add . Она вызывает функции RI_SDK_CreateModelComponent и RI_SDK_LinkRServodriveToController.

Чтобы добавить сервопривод вращения типа mg996r программа вызывает функцию rservo_add:

rservo_add(lib, pwm, mg996r,"mg996r", 0)

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

В качестве четвертого параметра функции передается тип сервопривода, а в качестве пятого — номер порта ШИМ контроллера Robointellect Controller 001, к которому подключен этот сервопривод.

Для управления скоростью и направлением вращения вала сервопривода используется функция rservo_rotate_by_pulse, вызывающий функцию RI_SDK_exec_RServoDrive_RotateByPulse библиотеки RISDK:

rservo_rotate_by_pulse(lib, mg996r, 1050, True)
rservo_rotate_by_pulse(lib, mg996r, 2100, True)
rservo_rotate_by_pulse(lib, mg996r, 1570, True)

В качестве третьего параметра методу передается длительность управляющего импульса в мс.

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

Чтобы включить асинхронные режим, программа risdk_servo_mg996r.py передает значение True функции rservo_rotate_by_pulse.

Также обратите внимание на использование функции print_rservo_state, которая выводит на консоль текущее состояние серводвигателя. Она вызывает функцию RI_SDK_exec_RServoDrive_GetState, возвращающую одно из двух значений:

  • 0 — сервопривод ожидает вызова действий, ничего не делает и готов к работе;
  • 1 — компонент выполняет действие. Сервопривод в данный момент осуществляет движение

Функция rservo_rotate_at_speed, основанная на вызове функции библиотеки RISDK с именем RI_SDK_exec_RServoDrive_RotateWithRelativeSpeed, позволяет запустить вращение вала сервопривода в заданном направлении и с заданной скоростью:

rservo_rotate_at_speed(lib, mg996r, 0, 50, True)
rservo_rotate_at_speed(lib, mg996r, 1, 100, True)

Третий параметр функции rservo_rotate_at_speed задает направление вращения. Нулевое значение соответствует вращению по часовой стрелке, значение единицы — вращению против часовой стрелки.

Четвертый параметр задает скорость вращения в процентах от максимальной.

Для остановки вала сервопривода вызывается функция stop_rservo:

stop_rservo(lib, mg996r)

Она обращается к функции RI_SDK_exec_RServoDrive_Stop библиотеки RISDK.

Синхронные функции для сервоприводов вращения

В листинге 7 приведен код программы risdk_servo_mg996r_sync.py (в сокращенном виде), управляющая сервоприводами вращения в синхронном режиме.

Листинг 7. https://raw.githubusercontent.com/AlexandreFrolov/ri-controller-001-risdk/main/risdk_servo_mg996r_sync.py

…
rservo_rotate_by_pulse_over_time(lib, mg996r, 1050, 2000, False)
rservo_rotate_by_pulse_over_time(lib, mg996r, 2100, 2000, False)
rservo_rotate_by_pulse_over_time(lib, mg996r, 1570, 2000, False)
 
rservo_rotate_at_speed_over_time(lib, mg996r, 0, 50, 2000, False)
rservo_rotate_at_speed_over_time(lib, mg996r, 1, 100, 2000, False)
…

Эта программа вызывает функции rservo_rotate_by_pulse_over_time и rservo_rotate_at_speed_over_time, аналогичные по назначению только что рассмотренным функциям rservo_rotate_by_pulse и rservo_rotate_at_speed.

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

Нестандартные сервоприводы

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

Пример программы risdk_servo_ds04-nfs.py, подключающей сервопривод DS04-NFS, показан в листинге 8 (в сокращенном виде).

Листинг 8. https://raw.githubusercontent.com/AlexandreFrolov/ri-controller-001-risdk/main/risdk_servo_ds04-nfs.py

…
i2c = c_int()
pwm = c_int()
ds04_fns = c_int()

lib = init(i2c, pwm)

ds04_fns = add_custom_rservo(lib, pwm, 1050, 2100, 1050, 2100, 0)        
print_rservo_state(lib, ds04_fns)

rservo_rotate_by_pulse(lib, ds04_fns, 1050, True)
print_rservo_state(lib, ds04_fns)
time.sleep(3) 

rservo_rotate_by_pulse(lib, ds04_fns, 2100, True)
print_rservo_state(lib, ds04_fns)
time.sleep(3) 

rservo_rotate_by_pulse(lib, ds04_fns, 1570, True)
print_rservo_state(lib, ds04_fns)
time.sleep(3) 

rservo_rotate_at_speed(lib, ds04_fns, 0, 50, True)
time.sleep(3) 
rservo_rotate_at_speed(lib, ds04_fns, 1, 100, True)
time.sleep(3) 

stop_rservo(lib, ds04_fns)
print_rservo_state(lib, ds04_fns)

cleanup_servo(lib, ds04_fns)
cleanup(lib)

Добавление сервопривода осуществляется функцией add_custom_rservo:

ds04_fns = add_custom_rservo(lib, pwm, 1050, 2100, 1050, 2100, 0)

Эта функция вызывает функции RI_SDK_CreateDeviceComponent, RI_SDK_exec_RServoDrive_CustomDeviceInit и RI_SDK_LinkRServodriveToController.

Здесь в качестве третьего и четвертого параметров функции add_custom_rservo передаются минимальная и максимальная длительность управляющих импульсов для поворота вала сервопривода по часовой стрелке.

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

И, наконец, седьмой параметр задает номер канала ШИМ контроллера Robointellect Controller 001, к которому подключен сервопривод.

Напомним, что, если подать управляющие импульсы длительностью 1 мс, вал сервопривода DS04-NFS будет вращаться с полной скоростью в направлении против часовой стрелки.

Импульсы длительностью 2 мс вызовут вращение вала сервопривода с полной скоростью в направлении по часовой стрелке.

И, наконец, для того чтобы остановить вращение вала, нужно подать на его управляющий контакт импульсы длительностью 1.5 мс.

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

Установка RISDK для Repka OS на микрокомпьютере Repka Pi

До сих пор мы работали с контроллером Robointellect Controller 001, подключив его через USB к ноутбуку или настольному компьютеру с ОС Microsoft Windows. Теперь подключите его к Repka Pi, на которой установлена Repka OS (рис. 16).

Рис. 16. Подключение контроллера Robointellect Controller 001 к Repka Pi

Также подключите к контроллеру сервоприводы.

Загрузите Repka OS и с помощью команды lsusb проверьте, что в системе определяется контроллер QinHeng Electronics CH341, установленный на плате Robointellect Controller 001:

# lsusb
Bus 008 Device 002: ID 1a86:5512 QinHeng Electronics CH341 in EPP/MEM/I2C mode, EPP/I2C adapter
Bus 008 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 005 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 007 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Если подключение через USB выполнено правильно и контроллер CH341 виден, продолжайте установку.

Вам нужно открыть страницу и скачать файл установки robointellect_sdk_linux_arm64.deb (рис. 17).

Рис. 17. Скачивание файла установки пульта управления

Для этого скопируйте ссылку Скачать для архитектура arm64.

Скачайте файл robointellect_sdk_linux_arm64.deb:

# wget --no-check-certificate https://download.robointellect.ru/robointellect_sdk_linux_arm64.deb

Далее установите необходимые пакеты:

# apt install gcc libgtk-3-0 libayatana-appindicator3-1 make i2c-tools dkms python3

После установки сделайте текущим каталог, в который был загружен файл robointellect_sdk_linux_arm64.deb и установите его следующим образом:

# dpkg -i robointellect_sdk_linux_arm64.deb

Через некоторое время на консоли появится запрос подтверждения установки драйвера:

…
/lib/systemd/system/ri_translator.service.
Хотите ли установить драйвер для CH341 y/n

Подтвердите установку, для чего введите символ «y» с клавиатуры.

Проверьте, что после установки появился файл библиотеки RISDK /usr/local/robointellect_sdk/ri_sdk/librisdk.so.

Далее установите репозиторий с классами RiController, RiServo, RiRotateServo, RiLed и примеров программ на Python:

# git clone https://github.com/AlexandreFrolov/ri-controller-001-risdk

После установки библиотеки RISDK в Repka OS испытайте работу всех приведенных в этом репозитории программ.

Итоги

Из этой статьи вы узнали о том, как устроен контроллер Robointellect Controller 001, плата которого объединяет сразу четыре устройства. Вы научились управлять серводвигателями с удержанием угла, серводвигателями постоянного вращения, подключенными к контроллеру, а также светодиодом RGB, установленным на плате контроллера.

Вы изучили библиотеку RISDK и научились работать с ней из программ, составленных на языке Python.

Примеры программ вы запускали на компьютере с Microsoft Windows, а также на микрокомпьютере Repka Pi с установленной Repka OS. В обоих случаях контроллер Robointellect Controller 001 был подключен через USB.

Также приводим ссылки на ресурсы проекта Repka Pi:


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

Для участия в обсуждении вы должны быть авторизованным пользователем

Навигация

ВойтиРегистрацияТемы