Глава 2. Обзор программной составляющей
Подготовка среды разработки
Универсальный вычислительный контроллер DXL-IoT является подобной Arduino Mega 2560 платой. Среда программирования Arduino IDE, позволяет реализовать возможность разработки программного кода, для платы DXL-IoT, применяя на практике те же подходы и библиотеки, что и для Arduino Mega 2560. Для работы с контроллером рекомендуется использовать среду разработки Arduino IDE версии не ниже 1.8, которую необходимо скачать с сайта Arduino.cc и инсталлировать в операционную систему штатными средствами. Поскольку на контроллере имеется модуль беспроводной связи Wi-Fi / Bluetooth, необходимо добавить в среду разработки поддержку данного модуля. Для этого нужно перейти в меню «Файл» - «Настройки» - «Дополнительные ссылки для Менеджера плат» и в открывшемся окне (Рис. 2.1) вставить следующую ссылку: https://dl.espressif.com/dl/package_esp32_index.json

Рис. 2.1. Добавление ссылки для менеджера плат
В обоих окнах нажать кнопку «ОК».
После указания дополнительной ссылки для менеджера плат требуется перейти в раздел «Инструменты» - «Платы» - «Менеджер плат», в строке поиска ввести esp32 и установить найденный пакет «esp32 by Espressif Systems» (Рис. 2.2).

Рис. 2.2. Установка пакета поддержки плат семейства ESP32
В результате этих действий, в меню «Инструменты» - «Плата» появится раздел «ESP32 Arduino», в котором для работы со встроенным модулем беспроводной связи Wi-Fi / Bluetooth необходимо выбрать «ESP32 Dev Module».
Отличительной особенностью контроллера DXL-IoT является поддержка интерфейса Dynamixel, для работы с которым разработаны специальные библиотеки. Библиотеки для работы с Dynamixel-протоколом можно скачать с сайта appliedrobotics.ru из раздела «Учебные материалы» – «Программное обеспечение». Существует 2 способа работы контроллера в цепи Dynamixel – совместимых устройств: в качестве
«Master-устройства» (ведущий контроллер) и «Slave-устройства» (ведомое Dynamixel-совместимое устройство).
Работа с Dynamixel – совместимыми устройствами, библиотеки DxlMaster и DxlMaster2
Dynamixel – совместимые устройства - сервоприводы и датчики, работающие на протоколе Dynamixel, подключаются к интерфейсу на основе последовательного порта UART в режиме полудуплекса. Для управления устройствами Dynamixel с универсального вычислительного контроллера DXL-IoT. Необходимо использовать библиотеку «DxlMaster» для работы по протоколу Dynamixel 1.0, а для работы по протоколу Dynamixel 2.0 – библиотеку «DxlMaster2».
Загрузить библиотеки можно с сайта ООО «Прикладная робототехника» (appliedrobotics.ru) или по ссылке: https://github.com/AppliedRobotics/. Библиотеки при скачивании поставляются в виде zip-библиотеки Arduino и устанавливаются штатными средствами Arduino IDE (Меню «Скетч» -> «Подключить библиотеку» -> «Добавить .ZIP библиотеку»).
Библиотеки “DxlMaster” и “DxlMaster2” содержат следующие примеры (меню «Файл» -> «Примеры»):
- blink_led – включение и выключение светодиодов сервоприводов, работающх по протоколу Dynamixel.
- change_baudrate – изменение скорости передачи данных на шине Dynamixel.
- console – терминал, позволяющий из командной строки найти и настроить Dynamixel – совместимые устройства в сети.
- joint_mode – управление сервоприводами по положению (режим шарнирного сочленения).
- wheel_mode – управление сервоприводами по скорости (режим колеса).
- wheel_mode_low_level – управление сервоприводами на низком уровне (изменением значений регистров).
Инициализация библиотеки
Во всех случаях использования библиотек “DxlMaster” и “DxlMaster2” необходимо добавить в программу и инициализировать. Для добавления библиотеки необходимо добавить следующий заголовочный файл в начале программы:
#include “DxlMaster.h” или #include “DxlMaster2.h”
При добавлении заголовочного файла библиотеки становится доступен глобальный объект DxlMaster, который необходимо инициализировать следующим образом:
DxlMaster.begin(int baudrate);
где baudrate – скорость обмена данными с устройствами на шине Dynamixel (см. документацию к конкретной модели сервопривода). Чаще всего используются скорости 57 600 бод и 1 000 000 бод. Скорость обмена данными с Dynamixel – совместимым устройством может быть изменена через программу ROBOTIS Dynamixel Wizard, либо используя пример change_baudrate.
Подключение произвольных устройств, класс DynamixelDevice
Обмен данными с Dynamixel – совместимыми устройствами происходит путем записи и чтения регистров в памяти такого устройства. Для этого предназначен класс DynamixelDevice библиотек “DxlMaster” и “DxlMaster2”.
Общие методы класса DxlMaster:
DxlMaster(uint8_t id)– конструктор, id – уникальный номер устройства
Dynamixel в сети (см. документацию на устройство). Может быть изменен в программе ROBOTIS Dynamixel Wizard.
DynamixelStatus init()– поиск и инициализация устройства. Если найдено устройство с адресом id и скоростью обмена, соответствующей текущим настройкам шины, возвращает 0, иначе – код ошибки.DynamixelStatus changeId(uint8_t id)- изменение id устройства (для этого устройство все равно должно быть инициализировано с исходным id).uint16_t model()– возвращает номер модели устройства.uint8_t firmware()– возвращает версию прошивки устройств.void communicationSpeed(uint32_t aSpeed)– изменение скорости обмена данными с устройством.
Обмен данными:
Все методы для обмена данными возвращают статус выполнения команды DynamixelStatus, принимающий значение «0» в случае успеха или выдающий номер стандартной ошибки шины Dynamixel, в случае сбоя обмена данными.
Методы чтения данных выполнены в виде шаблонов для чтения регистров разной длины (чаще всего в Dynamixel – совместимых устройствах применяются регистры типов uint8_t, uint16_t), например:
DynamixelStatus read(uint8_t aAddress, T& aData) – шаблон метода для чтения регистра произвольного типа T по адресу aAdress.
Пример использования:
uint16_t pos; device.read(DYN_ADDRESS_CURRENT_POSITION, pos);
После успешного выполнения команды в переменной pos окажется значение 16-ти разрядного регистра DYN_ADDRESS_CURRENT_POSITION, который занимает в памяти сервопривода 2 байта.
Методы для обмена данными:
DynamixelStatus read(uint8_t aAddress, T& aData)– чтение регистра типа T по адресу aAddress с записью результата в переменную aData.DynamixelStatus read(uint8_t aAddress, uint8_t size, uint8_t *ptr)– чтение массива из регистров, в количестве size по адресу aAddress с записью результата в массив по адресу ptr.DynamixelStatus write(uint8_t aAddress, const T& aData)– запись в регистр типа T по адресу aAddress значения aData.DynamixelStatus write(uint8_t aAddress, uint8_t size, const uint8_t *ptr)- запись в массив регистров, длиной size, по адресу aAddress значений из локального массива с указателем ptr.DynamixelStatus regWrite(uint8_t aAddress, const T& aData)– команда отложенной записи в регистр типа T по адресу aAddress значения aData. Значение передается на устройство, но применяется к регистру только после команды action().DynamixelStatus regWrite(uint8_t aAddress, uint8_t size, const uint8_t
*ptr) - команда отложенной записи в массив регистров, длиной size, по адресу aAddress значений из локального массива по указателю ptr.
DynamixelStatus action()– команда, получив которую все устройства в сети применяют значения регистров, полученные с командой отложенной записи. Функция применяется для организации синхронной работы сервоприводов: вначале всем сервоприводам рассылаются целевые значения (положение, скорость), а затем широковещательной командой «action» приводы запускаются.DynamixelStatus ping()– команда для проверки наличия устройства в сети. В случае, если устройство найдено, возвращает значение «0», иначе – код ошибки.
Примечание. Для отправки широковещательных команд необходимо создать абстрактное устройство DynamixelDevice с широковещательным адресом.
Пример низкоуровнего управления сервоприводом по протоколу Dynamixel 2.0:
#include “DxlMaster.h”
// Добавление библиотеки
DynamixelDevice device(1);
// Создание объекта управления с адресом
id = 1
void setup() {
DxlMaster.begin(1000000);
// Инициализация шины со скоростью 1000000 бод device.init();
// Поиск в сети и инициализация устройства
device.write(DYN_ADDRESS_ENABLE_TORQUE, 1);
// Включение момента
uint16_t zero_limit = 0;
device.write(DYN_ADDRESS_CW_LIMIT, zero_limit);
// Очистка ограничения вращения по часовой стрелке
device.write(DYN_ADDRESS_CCW_LIMIT, zero_limit);
// Очистка ограничения вращения против часовой стрелки
}
void loop() {
int16_t speed = 1000; device.write(DYN_ADDRESS_GOAL_SPEED, speed);
// Установка скорости
}
Подключение сервоприводов, класс DynamixelMotor
Для более удобного управления сервоприводами, работающими по протоколу Dynamixel, необходимо применять ориентированный на такое использование класс DynamixelMotor, наследующий класс DynamixelDevice вместе со всеми представленными в предыдущем разделе методами. Дополнительно к методам класса DynamixelDevice в классе DynamixelMotor реализованы следующие специальные методы.
void wheelMode()– настраивает регистры привода для работы в режиме колеса.void jointMode(uint16_t aCWLimit=0, uint16_t aCCWLimit=0x3FF)– настраивает сервопривод для управления по положению (режим шарнирного сочленения) с ограничениями углов поворота по и против часовой стрелки (aCWLimit и aCCWLimit, соответственно).void enableTorque(bool aTorque=true)– включает момент, регистр целевого положения принимает значение текущего положения, и сервопривод удерживает текущее положение.void speed(int16_t aSpeed)– задает скорость вращения сервопривода в режиме колеса, либо максимальную скорость вращения в режиме сочленения.void goalPosition(uint16_t aPosition)– задает целевое положение при управлении по положению.void led(uint8_t aState)– управляет светодиодом сервопривода: aState = 0 – выключение, aState = 1 – включение.uint16_t currentPosition()– опрашивает и возвращает показания о текущем угловом положении сервопривода.DynamixelStatus getCurrentPosition(uint16_t &pos)– метод аналогичен предыдущему, но возвращает статус выполнения команды для построения более надежных систем управления.
Пример управления сервоприводом по протоколу Dynamixel 1.0:
#include ”DxlMaster.h” // Добавление библиотеки
DynamixelMotor motor(1);
// Создание объекта класса сервопривода с адресом 1
void setup() {
DxlMaster.begin(1000000);
// Инициализация шины Dynamixel со скоростью 1000000 бод
motor.enableTorque();
// Включение момента привода
motor.jointMode(204, 820);
// Включение режима сочленения с угловыми ограничениями
motor.speed(100);
// Задание максимальной скорости вращения сервопривода
}
void loop() {
motor.goalPosition(123); // Задание положения сервопривода
}
Пример управления сервоприводом по протоколу Dynamixel 2.0:
#include “DxlMaster2.h“
// Добавление библиотеки
DynamixelMotor motor(1);
// Создание объекта класса сервопривода с адресом 1
void setup() {
DxlMaster.begin(1000000);
// Инициализация шины Dynamixel со скоростью 1000000 бод
motor.protocolVersion(2.0); // выбор протокола обмена данными
motor.enableTorque(1); // Включение момента привода
motor.write(DYN2_ADDR_OPERATION_MODE, (uint8_t)3);
// установка режима работы сервопривода в качестве шарнира = 3
motor.speed(100);
// Задание максимальной скорости вращения сервопривода
}
void loop() {
delay(1000);
motor.goalPosition(4000); // Задание положения сервопривода
}
Работа модуля в качестве Dynamixel - совместимого устройства, библиотеки DxlSlave и DxlSlave2
Модуль подключается в сеть устройств Dynamixel в качестве ведомого, отвечает на запросы по протоколам Dynamixel 1.0 и Dynamixel 2.0 и передает данные на управляющий контроллер более высокого уровня через интерфейс на основе последовательного порта UART в режиме полудуплекса (Рис.1.1, позиция 3).
Для работы интерфейса и предоставления доступа к данным для опроса по протоколам Dynamixel 1.0 и 2.0 необходимо использовать библиотеки «DxlSlave» и «DxlSlave2.0», которые можно загрузить с сайта компании ООО «Прикладная робототехника» (из раздела «Учебные материалы»). Библиотеки поставляются в виде zip-библиотек Arduino и устанавливаются штатными средствами Arduino IDE (Меню «Скетч» -> Подключить библиотеку -> Добавить .ZIP библиотеку).
Библиотека содержит следующие примеры (меню «Файл» -> Примеры):
- allRegs – организация Dynamixel-совместимого устройства с произвольным набором данных.
- setEepromDefault – очистка энергонезависимой памяти, используемой для хранения значений регистров Dynamixel.
-
ax12a (только в DxlSlave) – эмуляция сервопривода ROBOTIS DYNAMIXEL AX-12A.
-
xl320 (только в DxlSlave2) – эмуляция сервопривода ROBOTIS DYNAMIXEL XL-320.
| Область | Адрес (HEX) | Имя | Описание | Доступ | Начальное значение |
|---|---|---|---|---|---|
| 0 (0x00) | Model Number(L) | Младший байт номера модели устройства | R* | 12 (0x0C) | |
| 1 (0x01) | Model Number(H) | Старший байт номера модели устройства | R | 0 (0x00) | |
| 2 (0x02) | Version of Firmware | Версия программного обеспечения устройства | R | - | |
| 3 (0x03) | ID | Адрес устройства в сети Dynamixel | RW | 1 (0x01) | |
| 4 (0x04) | Baud Rate | Скорость обмена данными (делитель). 1 – 1000000 бод | RW | 1 (0x01) | |
| 5 (0x05) | Return Delay Time | Задержка ответа на запросы для повышения надежности передачи данных | RW | 250 (0xFA) | |
| EEPROM | 6 (0x06) | Произвольные данные | RW | 0(0x00) | |
| … | ... | ... | ... | ... | |
| 15 (0x0F) | Произвольные данные | RW | 0(0x00) | ||
| 16 (0x10) | Status Return Level | Настройка протокола: будет ли устройство отвечать на запросы | RW | 2 (0x02) | |
| 17 (0x11) | Произвольные данные | RW | 0(0x00) | ||
| … | ... | ... | ... | ... | |
| 23 (0x17) | Произвольные данные | RW | 0(0x00) | ||
| 24 (0x18) | Произвольные данные | RW | 0(0x00) | ||
| RAM | … | ... | ... | ... | ... |
| 255 (0xFF) | Произвольные данные | RW | 0(0x00) |
Таблица 1. Назначение регистров Dynamixel
- R – внешним устройствам доступно только чтение регистра, RW – чтение и запись.
Стандартная организация адресного пространства Dynamixel
В библиотеках организовано регистровое управление, аналогичное устройствам Dynamixel – данные, доступные внешним устройствам, необходимо хранить в выделенной области памяти библиотеки в 255 байт (регистров), каждый из которых имеет адрес от 0 до 255.
Некоторые регистры по стандартным адресам должны содержать типовые для Dynamixel-совместимых устройств значения и заполняются библиотекой автоматически, другие отведены для пользовательских задач. Адресное пространство и назначение регистров приведено в таблице 1.
Инициализация библиотеки
Во всех случаях использования библиотеку необходимо добавить в программу и инициализировать. Для добавления библиотеки необходимо подключить заголовочный файл в начале программы.
Для работы с протоколом Dynamixel 1.0:
#include “DxlSlave.h“
Для работы с протоколом Dynamixel 2.0:
#include “DxlSlave2.h“
При добавлении заголовочного файла библиотеки становится доступен глобальный объект DxlSlave (одинаковый для любой версии протокола), который необходимо инициализировать следующим образом:
DxlSlave.begin(uint16_t model_number, uint8_t fw_version);
где model_number – номер модели устройства и fw_version – версия программного обеспечения, которые будут показаны внешним устройствам.
Работа с интерфейсом, класс DxlSlave
После инициализации библиотека работает в фоновом режиме и автоматически отвечает на запросы внешних устройств, отправляя данные из регистров или устанавливая их новые значения. Изменение значений системных (стандартных) регистров с настройками параметров передачи данных (ID устройства, baud rate, return delay time, status return level) автоматически приводит к изменению этих параметров. Изменение значений
пользовательских регистров фиксируется в виде их адресов в кольцевом буфере (очереди). Таким образом, обмен данными необходимо выполнять в следующей последовательности:
- необходимо записать, данные для передачи управляющему устройству, в регистр. Они автоматически будут отправлены управляющему устройству по запросу;
- проверить наличие необработанных изменений в очереди (polling), для того чтобы узнать об изменениях регистров, выполненных управляющим устройством, и произвести соответствующие действия.
- чтобы узнать об изменениях регистров, выполненных управляющим устройством, и произвести соответствующие действия, необходимо проверить наличие необработанных изменений в очереди (polling).
Для работы с интерфейсом необходимо использовать класс DxlSlave, предоставляющий следующие методы.
void begin(uint16_t model_number, uint8_t fw_version)- инициализация библиотеки. Значения model_number и fw_version записываются в регистры Model Number и Version of Firmware в соответствии с таблицей стандартных регистров. После вызова метода, устройство начинает отвечать и автоматически (в фоновом режиме) реагировать на запросы Dynamixel.void set_id(uint8_t id)- записывает ID в соответствии с таблицей регистров. После этого устройство автоматически меняет адрес доступа.void set_baud(uint8_t baud)- записывает Baud Rate в соответствии с таблицей регистров. После этого устройство меняет скорость передачи данных (baud rate).void set(uint8_t addr, uint8_t val)- записывает значение val в регистр по адресу addr. Значение не перезаписывается в EEPROM, если равно уже записанному. При изменении значений регистров, отвечающих за настройки протокола, автоматически меняются соответствующие параметры передачи данных.uint8_t get(uint8_t addr)- возвращает значение, записанное в регистре по адресу addr.void set_mode(uint8_t addr, uint8_t mode)- настраивает режим доступа mode (0 – R (чтение), 1 - RW (чтение и запись)) к регистру по адресу addr.void set_mode16(uint8_t addr, uint8_t mode)- настраивает режим доступа к 2-м регистрам подряд начиная с адреса addr.void set16(uint8_t addr, uint16_t val)- записывает 16-ти разрядное значение в 2 регистра, расположенных по адресу addr. Последовательность байт: младший байт первый. Значение не должно перезаписываться в EEPROM, если равно уже записанному.uint16_t get16(uint8_t addr)- возвращает 16-ти разрядное значение, записанное в 2-х регистрах (первый байт младший).-
uint8_t poll()- возвращает 1, если со стороны ведущего устройства через протокол Dynamixel было изменено значение какого-либо регистра (очередь измененных регистров не пуста); 0, если изменений нет (очередь пуста). -
uint8_t scan()- возвращает адрес первого в очереди (кольцевом буфере) измененного через протокол Dynamixel регистра и удаляет его из очереди (смещает указатель кольцевого буфера на следующий измененный регистр).
Примеры работы с библиотеками DxlSlave и DxlSlave2
В следующем примере показана организация Dynamixel-совместимого устройства с произвольным набором данных. В этом примере при изменении ведущим устройством регистра разрабатываемого ведомого устройства, ведомое устройство выводит в терминал адрес и значение измененного регистра.
#include <DxlSlave.h> // Подключение библиотеки
#define IS_INITED_REG 23 // Адрес регистра для проверки 1-го запуска
void setup() {
DxlSlave.begin(1234, 12); // Инициализация интерфейса (номер модели 1234, версия 12)
Serial.begin(9600); // Инициализация отладочного последовательного порта для вывода
if(!DxlSlave.get(IS_INITED_REG)) { // Инициализация EEPROM при 1-м запуске
for(int i = 6; i < 23; i++) {
DxlSlave.set(i, i); // Заполнение регистров EEPROM произвольными значениями
DxlSlave.set(IS_INITED_REG, 1); // Установка флага “1-я инициализация выполнена“
}
}
for(int i = 24; i < 255; i++) {
DxlSlave.set(i, i); // Заполнение регистров RAM произвольными значениями
}
}
void loop() {
while(DxlSlave.poll()) { // Проверка наличия изменений регистров ведущим устройством
uint8_t addr = DxlSlave.scan(); // Получение адреса измененного регистра из очереди
Serial.print(“ The value at address “); // Вывод информации
Serial.print(addr);
Serial.print(“ was changed by master. New value is “); Serial.println(DxlSlave.get(addr));
}
}
Подробнее о возможностях библиотеки представлены в примерах (Меню «Файл» -> Примеры -> DxlSlave).
Для очистки EEPROM устройства, в том числе, для очистки флага и вызова 1-й инициализации EEPROM при изменении программы, необходимо использовать пример setEepromDefault.
Организация произвольного Dynamixel-совместимого устройства с использованием более разнообразных команд приведена в стандартном примере allRegs библиотеки.
Возможности библиотеки DxlSlave продемонстрированы также в примере «ax12a», где эмулируется сервопривод ROBOTIS DYNAMIXEL AX-12, распознаваемый стандартными утилитами ROBOTIS.
Аналогичный пример эмуляции сервопривода XL-320 с протоколом Dynamixel 2.0 приведен в библиотеке DxlSlave2.