Управление колёсными роботами
Управление колёсными роботами

Это перевод, оригинал находится здесь

Обзор

Общим для многих роботов является наличие двухколёсной тележки с независимо управляемыми моторами. Такая схема использует независимо управляемые моторы. Этот механизм использует дифференциальное руление и может разворачиваться на месте. Есть другие возможные варианты устройства, которые управлюятся другими классами. Классы, которые контролируют таких колёсных роботов используют несколько уровней абстракции На самом нижнем - это моторы, которые вращают колёса. Моторы контролируются классом NXTRegulatedMotor class. Класс DifferentialPilot использует моторы чтоб контролировать элементарные движения: повороты на месте, движение по прямой линии или движение по дуге. На следующем уровне класс Navigator использует DifferentialPilot чтоб двигать робота по сложному пути на плоскости. Чтоб осуществлять навигацию, Navigator-у нужны местоположение робота и его направление ориентации. Он использует класс OdometeryPoseProvider чтоб поддерживать эту информацию актуальной. Отношения между этими классами показаны на следующей таблице.

Класс Использует
Navigator DifferentialPilot
OdometryPoseProvider
OdometryPoseProvider DifferentialPilot
DifferntialPilot RegulatedMotor

Управление идёт сверху вниз: навигатор управляет пилотом который, в свою очередь, управляет моторами. Но поток информации идёт в обратном направлении. Пилот использует информацию от моторов, чтоб контролировать их. Pose provider использует информацию об одометрии от пилота, чтоб пересчитать текущую оценку позы робота. Поза состоит из координат робота (x и y) и угла азимута (направления, куда он "смотрит"). Навигатор использует эти данные чтоб рассчитать дистанцию и направление на точку назначения.

Поток информации использует модель слушателей и событий. Пилот регистрируется как слушатель по отношению к моторам, которые информируют его когда вращение начинается или заканчивается. Pose provider регистрируется как слушатель по отношению к пилоту, который информирует его о начале и завершении каждого движения. Такой управляемый событиями поток информации работает автоматически. В дополнение к управляемому событиями потоку навигатор может запрашивать оценку позы у класса pose provider в любое время, даже когда робот движется. Такая цепочка слушателей устанавливается в момент создания экземпляров классов DifferentialPilot и Navigator.

DifferentialPilot

Класс DifferentialPilot управляет тележкой, у которой есть два ведущих колеса, каждое со своим мотором. Он рулит тележкой путём изменения скорости и направления вращения моторов. Это один из нескольких классов контроллеров движения, которые отличаются использованием разных механизмов, но механизм дифференциального руления является самым простым.
Объект пилота должен знать как подсоединены моторы к портам, как направление вращения мотора соотносится с направлением движения робота. Ему также надо знать диаметр колёс и размер колеи (расстояние между двумя ведущими колёсами). Класс DifferentialPilot использует диаметр колеса чтоб расчитать расстояние, которое прошёл робот. Размер колеи он использует чтоб расчитать, насколько он повернул. Очевидно, оба параметра должны быть в одних и тех же единицах, но эти единицы могут быть любыми, какими вы захотите. При правильной настройке этих параметров ошибки в измерении пройденного расстояния и угла поворота должны быть в пределах 2% или даже меньше. Эта информация передаётся в конструкторе пилота. Класс DifferentialPilot находится в пакете lejos.robotics.navigation. документация на этот класс находится здесь (на английском языке).

Конструкторы:
  • DifferentialPilot(float wheelDiameter, float trackWidth, Motor leftMotor, Motor rightMotor)
  • DifferentialPilot(float wheelDiameter, float trackWidth, Motor leftMotor, Motor rightMotor, boolean reverse)
    Если моторы закреплены на роботе так, что он движется назад, когда моторы крутятся вперёд, используйте этот конструктор с параметром reverse равным true. В этом случае метод pilot.forward()
    приведёт к движению робота вперёд.
  • DifferentialPilot(float leftWheelDiameter, float rightWheelDiameter(),float trackWidth, Motor leftMotor, Motor rightMotor, boolean reverse)
    Если робот не будет двигаться по прямой на ровной поверхности, возможно, есть небольшая разница в диаметрах колёс. Вы можете использовать этот конструктор чтоб компенсировать эту неточность.
    Все эти конструкторы регистрируют DifferntialPilot как слушатель для обоих моторов.
Движение по прямой

Чтоб упрвлять роботом при движении по прямой линии, используйте:

  • void setTravelSpeed(double travelSpeed)
    устанавливает скорость вращения моторов в единицах расстояния в секунду (единица расстояния та же, которая использовалась при задании диаметра колеса).
  • void forward()
    запускает движение робота вперёд
  • void backward()
  • void stop()

Чтоб управлять расстоянием, проходимым роботом, используйте:

  • void travel(double distance)
  • void travel(double distance, boolean immediateReturn)
    distance расстояние измеренное в тех же единицах длины, что и задавался диаметр колеса; отрицательное расстояние означает движение назад.
  • getMovement().getDistanceTraveled()

    возвращает расстояние, которое прошёл робот с момента последнего вызова метода resetTachoCount()

Пример:

import lejos.nxt.Button;
import lejos.nxt.Motor;
import lejos.nxt.SensorPort;
import lejos.nxt.TouchSensor;
import lejos.robotics.navigation.DifferentialPilot;

/**
 * Robot that stops if it hits something before it completes its travel.
 */
public class TravelTest {
  DifferentialPilot pilot;
  TouchSensor bump = new TouchSensor(SensorPort.S1);

  public void go() {
    pilot.travel(20, true);
    while (pilot.isMoving()) {
      if (bump.isPressed()) pilot.stop();
    }
    System.out.println(" "+pilot.getMovement().getDistanceTraveled());
    Button.waitForAnyPress();
  }

  public static void main(String[] args) {
    TravelTest traveler = new TravelTest();
    traveler.pilot = new DifferentialPilot(2.25f, 5.5f, Motor.A, Motor.C);
    traveler.go();
  }
}

Можно заставить робота начать вращаться на месте, используя

  • void rotateLeft() or
  • void rotateRight()
Если вы хотите, чтоб вращение остановилось на определённом угле, используйте:
  • void rotate(double angle) or
  • void rotate(double angle, boolean immediateReturn )

Если angle положительное число, робот начнёт поворачиваться влево. Параметр immediateReturn имеет то же значение, что и в методах мотора – позволяя вызывающему потоку делать другую работу, пока выполняется поворот. Если другой метод вызывается в пилоте пока поворот в процессе выполнения, поворот прекращается. Значения wheelDiameter и trackWidth должны быть очень точными для того, чтоб этот метод работал правильно.

Программа SquareTracer (объезжатель квадрата)

Напишите программу, которая использует класс DifferentialPilot чтоб отследить квадрат, используя методы travel и void rotate(double degrees).

Решение

Программа SquareTracer2 (объезжатель квадрата2)

Напишите программу, которая описывает квадрат с возрастанием азимутального угла при поворотах и затем описывает тот же путь, но в обратном направлении. Модифицируйте метод traceSquare программы DifferentialPilot 1 так, чтоб он мог описывать квадрат в любом направлении и используйте его в этой программе. Это строгий тест на точность диаметра колёс и ширины колеи, которые вы передали классу pilot.

Solution

Езда по кругу.

Класс DifferentialPilot может также управлять роботом так, чтоб он ехал по кругу, используя такие методы:

  • void steer(double turnRate) – следовать по круговой траекториии пока другой метод не будет выполнен
  • void steer(double turnRate, int angle)
  • void steer(double turnRate, int angle, boolean immediateReturn)

Параметр turnRate определяет радиус закругления пути. Положительное число означает, что центр окружности будет слева от робота (так что левый мотор будет крутить внутреннее колесо). Отрицательное число означает, что левый мотор будет крутить наружное колесо. Абсолютное значение числа должно быть в пределах от 0 до 200 и оно означает отношение скоростей мотора, крутящего внешнее и внутреннее колёса. Внешний мотор крутит со скоростью, соответствующей установленной скорости движения робота. Внутренний мотор замедляется так, чтоб заставить робота поворачивать. При turn rate равном 0, соотношение скоростей колёс будет строго 1.0 и робот будет двигаться по прямой. При turn rate = 200 соотношение скоростей моторов будет -1 и робот будет разворачиваться на месте. Turn rate = 100 даёт соотношение скоростей моторов = 0, т.е. внутренний мотор не будет крутиться. Формула такая: соотн_скоростей_вращения_двигателей = 100 - abs(turnRate).

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

Программа SteerTester

Напишите программу, которая использует ButtonCounter чтоб ввести значение переменных turn rate и angle и затем вызывает метод steer(). Он делает это в цикле так, что вы можете попробовать разные значения этих параметров, чтоб управлять траекторией робота.

Решение

Методы, начинающие движение робота по круговой траектории:
  • void arcBackward(double radius)
  • void arcForward(double radius)

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

  • void arc(double radius, double angle )
  • void arc(double radius, double angle , boolean immediate return )
Сообщение сOdometryPoseProvider

Класс OdometryPoseProvider отслеживает положение робота и его направление. Для этого ему необходимо знать о каждом движении, которое делает DifferetnialPilot. Таким образом, pose provider-у нужно регистрироваться как слушателю пилота, путём вызова метода addListener После того, как пилот добавляет pose provider-а как слушателя, он будет автоматически вызывать метод moveStarted каждый раз, когда в pose provider начинается движение и moveStopped когда движение заканчивается. Если pose provider-у нужно узнать состояние движения продолжается оно или нет он вызывает метод пилота getMovement().

Другие методы класса DifferentialPilot

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

  • void setRotateSpeed(double speed )
    Параметр speed задаётся в градусах в секунду
  • void setTravelSpeed(double speed )
    Параметр speed в тех же единицах, что диаметр колеса в секунду
  • void setAcceleration(double acceleration)
  • void resetTachocount()
    Сбрасывает счётчики для обоих моторов и, скорее всего, приводит к ошибкам в рассчётах класса OdometryPoseProvider.
  • boolean isMoving()
    Возвращает истину, если любой из моторов вращает. Полезен, если вы использовали метод с параметром immediateReturn и хотите знать, продолжает ли работать двигатель.
  • boolean stalled()
    возвращает true если реальная скорость любого из моторов равна нулю. Имейте в виду, что реальная скорость вычисляется каждые 100ms, поэтому stalled() вернёт true через первые 100 ms после того, как робот начнёт двигаться.

Наверх страницы

Compass Pilot

Класс CompassPilot - это расширение класса DifferentialPilot. В нём реализованы те же методы, но используется Compass Sensor для того, чтоб пилот не отклонялся от правильного угла ориентации.

Необходимо подключить HiTechnic или Mindsensors compass сенсор в один из портов для сенсоров. Конструкторы класса CompassPilot аналогичны конструкторам класса DifferentialPilot, но с дополнительной информацией о порте compass sensor-а.

Конструкторы:
  • CompassPilot(SensorPort compassPort, float wheelDiameter, float trackWidth,Motor leftMotor, Motor rightMotor)
  • CompassPilot(SensorPort compassPort, float wheelDiameter, float trackWidth,Motor leftMotor, Motor rightMotor, boolean reverse)
Дополнительные методы CompassPilot:

  • void calibrate()
    калибровать compass sensor; медленно поворачивает робота на 360 degreees.
  • setHeading(int angle)
    устанавливает желаемое направление робота в градусах в Декартовых координатах (поворот влево увеличивает угол)
  • int getHeading()
    возвращает желаемое (видимо уже установленное) направление
  • int getAngle()
    возвращает Декартов угол компаса (азимут). Также реальное направление робота, исходя из того, что сенсор закреплён на роботе так, что смотрит всегда вперёд.
Дополнительные методы класса CompassPilot:

Напишите программу, делающую следующие шаги:

  1. Калибровка компаса.
  2. Поворот робота на 90 градусов по отношению к исходной позиции
  3. Обнуление направления компаса (так, что текущее направление становится нулевым).
  4. Движение робота вперёд на фиксированное расстояние.
  5. Поворот на 90 градусов влево.
  6. Движение робота на такое же расстояние назад.
  7. Вывод на экран показателей компаса и расстояния пройденного в конце каждого движения.

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

Решение

Наверх страницы

OdometryPoseProvider

Этот класс отвечает за поддержание текущей позиции робота и направления, в котором он ориентирован.. Для указания направления робота используются Декартовы координаты, угол задаётся в градусах; 0 градусов - это положительное направление оси x, 90 градусов - положительное направление оси y. Направление и координаты x и y хранятся в объекте Pose. API для Pose описан здесь. Документация на OdometryPoseProvider здесь
Единственный конструктор этого класса:

  • OdometryPoseProvider(MoveProvider mp) )
    Он регистрирует новый объект pose provider как слушатель объекта MoveProvider.

Единственные методы, которые вам, скорее всего, пригодятся:

  • void setPose(Pose aPose)
    для установки начальной позиции.
  • Pose getPose()
    Если этот метод вызван, то пока робот в движении, pose provider будет вызывать getMovement() move provider-а. Это гарантирует, что робот постоянно отслеживает своё положение.

Если вам интересно узнать о внутренней "кухне" классов - читайте дальше.
Когда движение начинает завершаться, необходимая для обновления информация о положении и ориентации поставляется автоматически Move Controller-ом путём вызова таких методов:

  • void moveStarted(Move move, MoveProvider mp)
  • void moveStopped(Move move, MoveProvider mp)

Одометрические данные содержатся в объекте Move. API этого класса-носителя данных описан здесь.

Наверх страницы

Navigator

Класс Navigator использует класс pilot чтоб контролировать движения робота и класс PoseProvider чтоб отслеживать положение робота. Навигатор следует по маршруту, представленному последовательностью локаций(точек). Каждая локация(точка) - экземпляр класса Waypoint. API класса WayPoint здесь. Путь - это экземпляр класса Path, API класса Path здесь. Путь ведёт себя как очередь объектов. Когда очередная точка пути достигнута, она удаляется из объекта пути и робот идёт к следующей. Новые точки могут быть добавлены в конец пути в любой момент. Документация на Navigator находится здесь.
Навигатор может использовать любой из классов классов-пилотов, реализующих интерфейс MoveController. Есть два способа создать экземпляр этого класса:

Конструкторы:
  • Navigator(MoveController aPilot)
    Чтоб использовать этот конструктор, вы должны сконструировать pilot и использовать его как параметр. Конструктор создаст свой собственный OdometryPoseProvider
  • Navigator(MoveController pilot, PoseProvider poseProvider
    Используйте этот конструктор, если вы хотите использовать другой класс PoseProvider

Оба конструктора зарегистрируют pose provider как слушатель MoveListener у pilot-а.

Методы навигации

Все методы (за одним исключением) в этом классе неблокирующие, т.е. возвращают управление немедленно.

  • void setPath(Path aPath)
    Есть три варианта этого метода которые добавляют точку пути в конец существующего пути. Они могут быть вызваны в любое время.
  • void addWaypoint(Waypoint aWaypoint)
  • void addWaypoint(float x, float y )

    Создаёт новый Waypoint и добавляет его
  • void addWaypoint(float x, float y , heading )
    Создаёт новый Waypoint и добавляет его ; используйте этот метод, чтоб указать направление робота в момент, когда он приходит в этот waypoint.
  • void followRoute(Path aRoute )
    Запускает движение робота по маршруту
  • void followRoute()
    Запускает движение робота вдоль существующего маршрута. Если робот уже в движении, то команда ничего не делает.
  • goTo(WayPoint destination)
    Добавляет destination (точку назначения) в конец маршрута и начинает движение робота
  • void goTo(double x,double y)
    Ещё один способ задать точку назначения. Действует так же как и предыдущий метод.
  • void goTo(double x,double y , double heading)
    Третий способ задать точку назначения. Действует как предыдущий метод.
  • void stop()
    Останавливает робота но сохраняет маршрут. Чтоб возобновить маршрут, вызовите followRoute().
  • boolean isMoving()
    Возвращает true если робот движется в сторону waypoint-а.
  • boolean pathCompleted()
    Возвращает true если робот достиг последнего waypoint-а.
  • void rotateTo(double direction)
    Поворачивает в направлении угла direction на Декартовой плоскости. Ось X это 0, ось Y
    это 90 градусов.
  • void waitForStop()
    Единственный блокирующий метод в этом классе. Возвращает управление, когда робот прекращает движение.
  • void singleStep(boolean yes)
    Если вы хотите, чтоб робот останавливался в каждом waypoint-е, вызовите этот метод с параметром true. Вы можете начать движение до следующего waypoint-а вызвав followRoute()

Наверх страницы

класс ShortestPathFinder (искатель кратчайшего пути)

Допустим, ваш робот в известном месте и "хочет" добраться до места назначения. Но есть препятствия на его пути. Препятствия обозначены на карте. Тогда какой маршрут он должен выбрать? Этот класс может найти кратчайший путь до точки назначения и сгенерировать маршрут (коллекцию WayPoint-ов) которая будет использована NavPathController-ом. Карта, которая нужна этому классу это LineMap которая, как подсказывает её имя, состоит из прямых линий. Полная документация на этот класс здесь. Использование этого класса очень просто. Конструктор:

  • ShortestPathFinder(LineMap map)

После того, как вы сконструировали path finder, вы можете получить маршрут используя любой из методов поиска пути. Они оба используют Pose как начальную точку, которая может быть возвращена pose provider-ом, и WayPoint как точку назначения. Оба могут выбросить DestinationUnreachableException если маршрут не может быть найден.

  • Path findRoute(Pose start,WayPoint finish)
  • Path findRoute(Pose start, WayPoint finish, LineMap theMap)
    Если вы не хотите использовать map (карту) указанную в конструкторе, вы можете использовать другой из этих методов

Кратчайший путь, если это не прямая линия, будет содержать точки концов отрезков, такие как углы препятствий на карте. Но реальный робот это не точка, поэтому если центр робота попытается пройти через угол, будет авария. Одно решение этой трудности - преувеличить размеры препятсвий чтоб дать зазор для робота. Однако, это может быть нудно делать это вручную и сложно делать это программно. Более простая схема расширить все линии на оригинальной карте, что углы теперь будут представлены двумя точками, каждый на определённом расстоянии от реального угла. Чтоб модифицировать таким образом карту, используйте метод:

  • void lengthenLines(float delta)

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

Есть несколько других путей поиска классов которые используют разные представления карт (maps). Смотрите ссылки в классах в пакете pathfinding здесь.

Наверх страницы

Hosted by uCoz