[Из песочницы] Работаем с коботом Dobot M1

Год назад на Хабре выходил обзор настольного робота Dobot Magician. В этой статье я предлагаю оценить его старшего брата Dobot M1 в действии. Также я попытаюсь объяснить, почему для своего проекта выбрал именно данную модель, опишу процесс разработки демки в Qt/c++, а также некоторые неприятные моменты, с которыми столкнулся в процессе разработки.
В этом году я работал в одном самом обычном исследовательском институте. Поначалу все было довольно буднично, и год не обещал ничего интересного, пока однажды на митинге не началось обсуждение как сделать черепичную сборку солнечных элементов (shingled solar cells). В этом подходе нет ничего нового, говорят, это один из первых способов сборки панелей для космических спутников.

Я не знаю насколько это правда, потому что статей или патентов я не видел. Но интерес к этому решению у публики подогрелся, отчасти еще и потому что пару лет назад SunPower выкатила свои новые панели, которые назвала П-серией. Так уж вышло в мире фотовольтаики, что раз уж санпавер что-то делает, то всем надо обязательно это дело тоже поделать. Вот так и мы. Все просто: готовим подложки, режем их на полоски и собираем. Я в тот момент отметил, что вручную лично я собирать точно ничего не буду, потому что руки у меня особенной ровностью не отличаются. Иначе в результате может выйти что-то наподобие одного видео на ютубе. А мы же как никак целый институт, если уж что-то делать, то не так колхозно. Я предложил прикупить роботов и настроить их на нужный процесс, и мне дали добро.

Надо отметить, что мы решили начать попроще и собирать мини-панели. Мини-панель это любая солнечная панель, которая меньше стандартной. Мы так делаем для отработки техпроцессов. Я ориентировался сначала на панели размером в 1 стандартный солнечный элемент с планами масштабирования до панели размером 2×2. Размер стороны одного солнечного элемента это 16 см. Соответственно, роботы нужны были с полем доступа 32×32 см. Точность хотелось поточнее, а цену подешевле. Так, вооружившись поисковой строкой, я начал изучать предложения. Решил, что 6 осей для проекта не нужно, хватит и 4-х, поэтому выбор сузился до роботов типа скара. Выяснил, что покупка промышленных роботов сопровождается дополнительными тратами, как проектирование безопасного рабочего пространства и выездом application engineer на место установки, который программирует робота под вашу задачу. Хорошие промышленные роботы в принципе дорогие, а услуги инженеров повысят ценник еще больше, более того, было интересно реализовать проект самому. Посему выбор сузился до коботов, коллаборативных роботов, с пониженными требованиями по безопасности и более дружелюбными для самостоятельного прототипирования. Так, я довольно быстро нашел компанию Добот. Dobot Magician я сразу же отбросил из-за размеров и точности, которая 100 мкм. Написал им запрос, чтобы дали спеки и сказали, где купить. Я выяснил что с доботами идут АПИ и их можно программировать на с++. Меня это устроило, но просто напрямую купить не вышло. К счастью, я нашел одного поставщика в Голландии, который продал мне 2 штуки за 8700 евро с ндс, с доставкой из Китая и при том взял на себя все таможенное оформление.


eqliv_l21t5_qnph5vis0kjci9m.jpeg

Добот М1 позиционируется как доступный профессиональный 4-х осевой коллаборативный робот. Он может выдержать до 1.5 кг нагрузки (не проверял), имеет радиус действия до 400 мм (не везде) и точность до 20 мкм (проверял). Рабочее пространство добота видно из рисунка ниже. Не сложно заметить, что из-за особенностей дизайна есть слепая зона с радиусом чуть меньше 15 сантиметров спереди. Более того, данная карта пространства не учитывает ориентацию руки. Добот может быть или правшой или левшой, я так и не разобрался, как это можно переключать на ходу без проведения дополнительной калибровки. По умолчанию добот правша, а это значит что правая зона ограничена областью доступа 2-го сустава, когда первый сустав направлен вправо. Так что реальная область рабочего пространства что-то около 2/3 от того, что представлено на официальном рисунке.

qfbvdpisi2sc2pbc62vg38miyko.png

У добота есть порты ввода-вывода: цифровые входы и выходы с уровнями на 24 В (по умолчанию уровни высокие), а также аналоговые входы. Что там за АЦП я не знаю. Порты доступны на задней панели в подставке и на самой руке для работы с насадками. На самой руке разъем я сфотографировать забыл, но он типа ВГА. Также имеется интерфейс расширения, под который можно докупить дополнительную плату. Добот подключается к ПК посредством РС-232 или по сети.

wxjptxd8lxcgzk7z7orkbmi-hri.png

Материалы изготовления корпуса: поликарбонат, судя по всему, плюс металлическая подставка, покрашенная в черный цвет. Ощущение премиальности данная конструкция не вызывает, но и ощущения кустарности тоже. Я покупал один в полной комплектации, а один в базовой. Лазерную насадку и 3д печать я не тестировал.

Для проверки работоспособности добота я использовал программу М1Студия под виндовс, которая загружается с сайта компании. Но на этом всё. Дальше, вооружившись апи, Qt и рабочей станцией с элементари ос, я сел писать демку для работы с несколькими доботами.


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

Первым делом был использован метод SearchDobot () из апи, который ничего не выдавал под линухами, а в виндовс работал только если доботы были подключены по РС-232. Странно, потому что М1Студия прекрасно определяет доботов по сети. При известных ип-адресах метод ConnectDobot () работает прекрасно. Ничего страшного, подумал я, настрою роутер и да будет ип-адрес привязан к маку. На следующий день я удивился тому факту, что доботы не реагируют. Выяснилось, что при включении добот имеет рандомный мак-адрес. Это такая фича прошивки, которую пофиксили в новой версии в мае, но которую я, правда, побоялся устанавливать.

Вооружившись программой для анализа сетевых пакетов, я выяснил, что доботы отзываются на удп запрос в порт 6000 с текстом «WhoisDobotM1» удп ответом с текстом, содержащим модельный номер добота. На основе этого был написан собственный метод, который отправляет запросы по всей подсети, и в случае ответа инициализирует добота.

void MainWindow::on_buttonSearch_clicked()
{
    QHostAddress host;

    QList list = QNetworkInterface::allAddresses();
    for (int i=0; i
void MainWindow::readUdpData()
{
    while (udpSocketGet.hasPendingDatagrams()) {
        QNetworkDatagram data = udpSocketGet.receiveDatagram();
        QByteArray ip = data.senderAddress().toString().toUtf8();
        QString name = QString(data.data()).section('_',0,0); // cuts "dobotM1" from "dobotM1_Dobot M1_0033\x00"
        if (name == "dobotM1") {
            MyDobot* a = new MyDobot();
            dobot->push_back(a);
            dobot->last()->initDobot(ip);
            ui->listDobots->addItem(ip);
            on_listDobots_activated(ui->listDobots->currentIndex());
        }
    }
}


Интерфейс демки довольно прост и представлен на картинке ниже.

p1t1ym-u5v1th1j0qwacwvrc3w8.png

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

class MyDobot : public QObject
{
    Q_OBJECT

public:
    explicit MyDobot(QObject *parent = nullptr);
    ~MyDobot();
    void initDobot(QByteArray IPaddress);
    Pose getCurrentPosition();

    void goHomeSafe();
    void goHome();
    void goSafe();
    void goPosition(float x, float y, float z, float r);
    void goPositionStraight(float x, float y, float z, float r);
    void goJog(int index);

    void setAirPump(int status, int direction); //status 1 off 0 on; direction 0 suck 1 push
    void setOutput(uint8_t address, uint8_t level);
    void setMotor(int velocity, int acceleration);

    alarmState getAlarms();
    void clearAlarms();
    void clearQueue();

    QString getName();

public slots:

private:
    char deviceSN[64];
    char deviceName[64];
    QByteArray thisDobotIP;
    Pose currentPosition, futurePosition;
};


Я решил использовать вектор с элементами моего класса для управления доботами, когда их число больше 1. В каждом элементе вектора есть переменная, которая хранит ип-адрес добота. Т.к. библиотека одна, а доботов несколько, то в каждом методе класса сначала необходимо вызывать метод подключения к конкретному доботу. Возможно, выглядит кривовато, но это все под капотом, и потом с этим классом работать очень удобно.

void MyDobot::setAirPump(int status, int direction)
{
    ConnectDobot(thisDobotIP, 115200, nullptr, nullptr);
...


Движение от точки к точке в АПИ добота реализовано несколькими методами: по прямой, по кривой, и еще каким-то, который я не помню. На рисунке ниже видны две траектории движения между 2 точками. Одна это прямая линия, которая при должной калибровке устройства не раздваивается при движении взад-вперед. Вторая траектория это кривая линия, которую вы получите, потому что прошивка добота последовательно подстраивает моторы, для достижения нужной координаты. Надо отметить, что движение по прямой возможно не всегда, некоторые положения суставов не дают возможности достичь из точки А в точку Б по прямой.

pxi2keh8edb25v9jos_b508trha.jpeg

Мониторинг положения добота реализован с помощью класса Qtimer, чей сигнал Qtimer: timeout привязан к моему методу MainWindow: on_getPoseTimer. Надо признаться, это так себе решение, потому что отзывчивая работа приложения с доботами возможна только если поставить таймаут 1000 мс. При более коротких таймингах начинают чувствоваться подлагивания при управлении доботами. Я заметил, что иногда добот может некоторое время потупить при получении команды, и если отправлять команды довольно часто, то вероятность попасть на потупить повышается. Возможно, виной всему постоянный вызов ConnectDobot, который может показаться излишним в данной демке, но демка писалась параллельно с основным проектом, а в основном проекте мне такая реализация очень нужна. Тем не менее, для мониторинга подключение не вызывается каждый раз, а проблема с подвисанием остается. Таймаут в 1 с, к сожалению, не дает такого плавного измерения положения добота в пространстве, которое, например, реализовано в М1Студия, но, с, другой стороны, это не важно.

В том же методе происходит и запрос на наличие ошибок. В апи добота передача кода ошибки реализована через структуру AlarmState.

struct alarmState {
    uint8_t value[32];
};


Эта структура есть массив 8-битных элементов, и код ошибки закодирован в двоичном представлении одного из или нескольких элементов массива. Чтобы вычислить код ошибки нужно найти »1» в элементе и прибавить номер ее (единицы) разряда к 8*н элементов (содержащих или не содержащих другие ошибки) до него в массиве value. Да, ошибок одновременно может быть больше одной. Далее код ошибки нужно найти в документе, который доступен для загрузки на сайте производителя. Содержание пдфки было скопировано в текстовый файл, который прикручен к проекту как ресурс. Если код ошибки отличается от 8×32 (т.е. нет ошибки), то этот код появляется в поле ошибок и по нажатию на кнопку Alarm его описание парсится в файле, после чего выводится в текстовое поле. Кстати раскодировка ошибок и парсинг сделаны вне класса для управления доботами. Что сейчас мне кажется не совсем правильной идеей.

В комплекте с доботом идет воздушный насос, который в руководстве по эксплуатации предлагают подключить к цифровым выходам 17 и 18. Один выход управляет включением/выключением насоса, а второй — направлением движения воздуха. Таким образом, например, приводится в действие пневматический захват, который также поставляется в комплекте.


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

  1. Отверстия в подставке не сделаны под шаг отверстий в стандартной оптической макетке. С другой стороны, один раз, когда по ошибке мои доботы столкнулись друг с другом, один из них просто прокрутился вокруг оси крепежного болта, что, я думаю, позволило избежать сильных повреждений.
  2. Из 1-го пункта вытекает второй. После столкновения доботов, их оси сместились. Это было не страшно сначала, т. к. я написал метод калибровки осей доботов относительно камеры. Страшное произошло в пункте 3.
  3. Вертикальная ось тоже сместилась и теперь нормаль у меня не нормальная. Это можно задетектить камерой, если начертить доботами два перпендикуляра. Можно убедиться что из-за скошенной вертикали добот чертит ручкой теперь не его истинные оси Х и У, а их проекции. А проекции эти могут иметь угол меньше 90 градусов. В реальности это приводит к небольшой ошибке совмещений. С другой стороны, это не так страшно, потому что ошибка линейна.
  4. Прошивка доботов в принципе ок. Был небольшой косяк с мак-адресом, есть некоторые проблемы с ожиданием ответов на удп запросы, но в остальном нормально работать получается.
  5. В конструкции предусмотрена батарейка, которая нужна для сохранения последних координат после выключения добота. Когда батарейка дохнет, напряжение на ней понижается, что приводит к стиранию этих данных. Из-за этого добот загружается в состояние с ошибками. Чтобы вывести из этих состояний, нужно сначала вызвать метод очистки сообщений ошибок, а потом вызвать метод поиска «домашнего» положения. Можно заменить батарейку, благо таковая имеется в комплекте. Однако батарейка спрятана в подставке, и чтобы добраться до нее нужно было открутить 4 болта. Шлиц одного из них сбит.
  6. В своем проекте я использовал 3 листа стекла, чтобы повысить уровень столика. Так себе решение, потому что стекла кривые. Дело в том что добот начинает выдавать ошибки, когда его вертикальная ось имеет значение меньше 15 мм, и вроде бы не все функции доступны. Так что нужно, чтобы рабочее пространство было расположено чуть выше плоскости крепления доботов.


Добот позволил мне реализовать проект по сборке солнечных элементов в черепичную мини панельку, что можно наблюдать на видео. Точность позиционирования проверялась на тех же кремниевых пластинках и была в пределе 1 пикселя по одной оси и 10 пикселей в другой. Камера в этом проекте использовалась с разрешением 20 МП, а поле охвата камеры по длинной стороне было около 17 см, не сложно посчитать что 1 пиксель соответствовал линейному размеру примерно в 30 мкм. Так уж вышло, что используемая оптика, пусть и качественная, позволяет наблюдать четко кремниевые пластины только в их центре, тогда как края пластин становятся сильно размытыми, что приводит к неопределенности в определении их граней вдоль коротких сторон, и соответственно к неопределенности определения центра пластинки. После калибровки осей я выставлял фокус камеры именно на короткие стороны пластин. Надо отметить, что там в принципе нельзя было сфокусироваться как в центре поля зрения, но все же. Из-за этого эффекта ошибка позиционирования по оси вдоль длинной стороны солнечного элемента была в пределе 10 пикселей, зато по короткой стороне всего 1 пиксель. Что соответствует примерно 300 и 30 мкм. Это позволило мне убедиться в честности спеков по точности.

© Habrahabr.ru