Приветствуем вас, уважаемые хабравчане!
Наш научный коллектив, который носит название Студенческого конструкторского бюро кафедры СУиИ Университета ИТМО, продолжает разработку курсов по робототехнике, и хочет поделиться одним из последних проектов на Lego NXT.

Ранее мы публиковали курс «Практическая робототехника»на NXT. Сейчас этот курс используется для обучения студентов на кафедре, и на площадке «Открытое образование». Так же публиковались фрагменты этого курса с подробным описанием действий для идентификации модели двигателя и расчета регулятора для робота Segway.
В этот раз было решено реализовать объезд препятствий роботом с дифференциальным приводом. Конструкция робота достаточно простая: два колеса с двигателями, гироскоп и пара ультразвуковых датчиков. Для оценки пройденного расстояния используются энкодеры на валу двигателя, для ориентации робота, измеряется гироскопом его угловая скорость и рассчитывается угол поворота, а расстояние до препятствия измеряется ультразвуковыми дальномерами.


Стоит отметить, что на рисунке указано — расстояние до целевой точки; — азимут, угол между осью OX и направлением на цель; — курс робота; — курсовой угол, разность между курсом и азимутом; v — линейная скорость робота. Робот перемещается за счет движения двух отдельно управляемых колес. А такая кинематическая схема имеет определенное математическое описание:

где – соответствующие угловые скорости вращения колес, x, y – соответствующие координаты, R – радиус колеса, B – расстояние между колесами.

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


Задача ставится следующим образом: робот должен достигнуть заданных координат .
При решении задач навигации мобильных роботов используются два основных подхода.
  • Глобальный – определение абсолютных координат устройства при движении по длинным маршрутам. Траектория выбирается еще до начала движения на основе полученной информации.
  • Локальный – определение координат устройства по отношению к некоторой (обычно стартовой) точке. Планирование задает лишь небольшой отрезок траектории, в конечной точке которого выбирается дальнейшая траектория.

Существует множество методов локальной навигации, например, такие как:

Остановимся подробнее на последнем методе. Поискав в сети информацию о способах управления подобным роботом, можно найти статью профессора кафедры теоретической кибернетики СПбГУ Матвеева А.С. и профессора Савкина А.В. из Университета Нового Южного Уэльса, Сидней, Австралия об алгоритме навигации робота среди движущихся и деформируемых препятствий. В статье авторы утверждают, что наиболее подходящим методом для решения нашей задачи является, собственно, метод тангенциального избегания, описанный профессором Федерального универститета Эспиринту-Санту, Витория, Бразилия Марио Сарцинелли, с которым мы имели честь познакомится во время поездки нашей команды в Бразилию.

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


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


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


Выразив производную через математическую модель, предложенную выше, получаем:


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


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

где – коэффициент пропорциональной составляющей курсового угла, – расстояние до препятствия, – минимальное расстояние до препятствия.

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

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

Ниже приведен пример итоговой программы на си-подобном языке NXC:
Код программы
#define RAD_WHEEL 0.028 // радиус колес
#define DEG2RAD PI / 180
#define LEFT OUT_C
#define RIGHT OUT_B
#define GYRO S1
#define LIMIT 20 // предельное растояние до препятствия
#define K_I 50 // коэффициент интегральной составляющей линейной скорости
#define K_P 0.1 // коэффициент пропорциональной составляющей курсового угла
#define ERROR 0.03 // точность достижения целевой точки

task main() {
    int     rotA, rotB, // показания энкодеров
            pwmLeft, pwmRight, // ШИМ для управления
            distS3, distS2, distMax, kDist, // ошибки расстояния до препятствия
            currentTime, previousTime, dt, // время
            gyroSpeed, gyroOffset, // угловая скорость с гироскопа и ее калибровка
            fileSize = 30640;

    float   course, courseAngle, bearing, // курс (psi), курсовой угол (alpha), пеленг (theta)
            xCoord, yCoord, // координаты точки между колесами
            delthaX, delthaY, // отклонения
            length, intLength, // длина оставшегося пути, интеграл длины
            path, prevPath, delthaPath, // путь пройденный точкой между колесами
            xRef = -1, yRef = 0, // координаты точки назначения
            baseSpeed = 0, control = 0, // линейная и угловая скорость
            k = 20;

    string  s; // строка для формирования вывода

    byte    handle;

    // инициализация датчиков
    SetSensorLowspeed(S2);
    SetSensorHTGyro(GYRO);

    // создание файла для записи данных, задержка
    DeleteFile("data.txt");
    CreateFile("data.txt", fileSize, handle);
    Wait(50);

    // калибровка гироскопа, угловая скорость (град/сек)
    gyroOffset = SensorHTGyro(GYRO);

    course = 0;
    previousTime = CurrentTick();

    while (true) {
        // измерение ошибки расстояния до препятствия
        distS3 = LIMIT - SensorUS(S3);
        distS2 = LIMIT - SensorUS(S2);

        // определения положения препятствия (с какой стороны робота, далеко ли от него)
        if (distS3 < 0 ) distS3 = 0;
        if (distS2 < 0 ) distS2 = 0;
        if (distS2 > distS3) distMax = distS2;
        else distMax = distS3;
        kDist = sign(distS2 - distS3);

        currentTime = CurrentTick();
        dt = currentTime - previousTime;
        previousTime = currentTime;

        // получение угловой скорости (град/сек)
        gyroSpeed = SensorHTGyro(GYRO) - gyroOffset;
        // расчет курса
        course = course + gyroSpeed * PI * dt / 1000.0 / 180.0;

        // снятие показаний энкодеров с двигателей
        rotA = MotorRotationCount(LEFT);
        rotB = MotorRotationCount(RIGHT);

        // расчет пути пройденного точкой между колесами
        path = (rotA + rotB) * DEG2RAD * RAD_WHEEL / 2;

        // путь пройденный за один цикл программы
        delthaPath = path - prevPath;

        // сохранение предыдущего значения длины пути
        prevPath = path;

        // вычисление координаты X
        xCoord = xCoord + delthaPath * cos(course);

        // вычисление координаты Y
        yCoord = yCoord + delthaPath * sin(course);

        // вычисление отклонения по X
        delthaX = xRef - xCoord;

        // вычисление отклонения по Y
        delthaY = yRef - yCoord;

        // расчет пеленга
        bearing = atan2(delthaY, delthaX);

        // расчет курсового угла
        courseAngle = bearing - course - K_P * kDist * distMax;

        // разворот по кратчайшему углу
        if (abs(courseAngle) > PI) courseAngle = courseAngle - sign(courseAngle) * 2 * PI;

        // расчет расстояния до точки
        length = sqrt (delthaY * delthaY + delthaX * delthaX);
        intLength = intLength + length * dt / 1000;
        if (intLength > 10) intLength = 10;

        // расчет линейной скорости
        baseSpeed = 100 * tanh (length) * cos (courseAngle) + K_I * intLength;
        if (abs(baseSpeed) > 40) baseSpeed = sign (baseSpeed) * 40;

        // расчет угловой скорости
        control = k * courseAngle + sin(courseAngle) * baseSpeed / length;

        // насыщение управляющего воздействия
        if (abs(control) > 30) control = sign(control)*30;

        // преобразование управления из float в integer, ШИМ в процентах
        pwmLeft = baseSpeed + control;
        // преобразование управления из float в integer, ШИМ в процентах
        pwmRight = baseSpeed - control;

        if (abs(pwmLeft) > 100) pwmLeft = sign (pwmLeft) * 100;
        if (abs(pwmRight) > 100) pwmLeft = sign (pwmRight) * 100;

        // подача управления
        OnFwd(LEFT, pwmLeft);
        OnFwd(RIGHT, pwmRight);
        ClearScreen();

        // вывод значений на экран NXT
        NumOut(0, 8, distS3);
        NumOut(0, 0, distS2);

        // создание строки с данными
        s = NumToStr(xCoord) + " " + NumToStr(yCoord) + " " + NumToStr(bearing) + " " + NumToStr(course);

        // запись в файл
        WriteLnString(handle, s, fileSize);

        // выход при достижении области точки
        if(abs(length) < ERROR) {
            Off(OUT_BC);
            break;
        }
        Wait(5);
    }
}


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

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


  1. Stronix
    24.02.2016 21:03
    +2

    Небольшое замечание: база — расстояние между осями передних и задних колёс, а на рисунке обозначена колея.


    1. kap2fox
      24.02.2016 21:49
      +1

      Возьмем на заметку. Спасибо.


  1. Dimchansky
    25.02.2016 10:31

    Можно ли получить доступ к материалам (видео) этого курса?


    1. kap2fox
      25.02.2016 13:46

      Отправил прямые ссылки на лекции.


  1. eL_FaRMaZoN
    25.02.2016 11:15

    У Вас на картинке модели опечатка — в правом нижнем углу "Линейсная"


    1. kap2fox
      25.02.2016 13:46

      Спасибо за замечание.


  1. Sterpa
    25.02.2016 16:25
    +4

    Такая замечательная тема, и статья, и так изложено… У меня профильное ракетостроительное образование, но я ни хрена не могу понять в статье без справочника.
    Добавьте немного логики, плеаз, для тех, кто не готов сначала выкачать и подробно изучить статью Матвеева-Савкина, и для тех, кто по эпической случайности, ну не сталкивался в ежедневно с полярными координатами, и, как бы это… теряется в них немного.

    1. Что означает эта запись image, словами!?
    2. Про то, что "ро" и "альфа" это "расстояние до цели и курсовой угол" я узнал только в середине статьи… почему в середине??
    3. Что есть "пси" и "тета" в первой и второй системе уравнений, дайте определение?


    1. kap2fox
      25.02.2016 21:13

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


      1. Sterpa
        25.02.2016 21:23

        Спасибо за подробный ответ.
        Как все-таки читается п.1? "Ро" и "альфа" стремятся к нулю?
        Как тогда следует понимать фразу робот должен достигнуть заданных координат image?
        Как заданные координаты могут стремиться к нулю?


        1. kap2fox
          25.02.2016 21:46

          \rho — это расстояние до целевой точки, то есть \rho = (x_ref-x)^2+(y_ref-y)^2. Эта сумма квадратов отклонений по координатам должна стремиться к нулю, робот должен стремиться в окружность с центром в заданных координатах и радиусом равным допустимой погрешности. В программе это 3 сантиметра. \alpha — это разность между курсом робота и углом на цель, она тоже стремится к нулю, чтобы робот был направлен прямо на цель.


  1. dude_sam
    26.02.2016 10:00

    Интересная статья.
    А что думаете про наборы Lego WeDo? Особенно интересует в контексте работы с программой Scratch?
    Или есть какие-то альтернативы для ребёнка 8 лет (в scracth уже год пробует себя)?
    Можно кратко просто ссылками, если как-то касались такой тематики.
    Спасибо.


    1. kap2fox
      27.02.2016 15:10
      +1

      Для начала, нормальный выбор. Я не сталкивался с комбинированием WeDo+Scratch, хотя есть примеры.
      Советую ознакомиться с курсом моего коллеги не Лекториуме. Очень подходит для старта, особенно по механическим передачам и конструированию. Так же у Лего есть наборы для изучения физики.
      Очень интересный американский проект MOOS.
      На будещее можно взять ТРИК.