Turtlebot — первые шаги


Сегодня мы решили начать новый проект: сборка и изучение Turtlebot.
Это устройство превосходно подходит для исследовательских задач в области робототехники, таких как SLAM, одометрия, PID-регулятор и т. п. Однако, как известно, IRobot Create, специально изготовленный для таких задач, в России достать невозможно. Поэтому приходится обходиться роботом-пылесосом — IRobot Roomba. Благо, для того, чтобы сделать из Roomba настоящий Turtlebot, потрудилось немало людей. Например, есть специальный стек под ROS для Roomba.

Однако, начнем мы не с ROS стека, а с прямого управления румбой с Arduino. Здесь подробно описано, как это сделать
Мы сделали подходящий кабель (про то, как его сделать, можно вычитать либо в SCI Roomba, либо здесь) для общения с румбой. Чтобы сделать такой провод, купите в магазине обычный Мin-Din кабель с 7-ю пинами и обрежьте один из его концов, а затем подключите их в Arduino так, как написано ниже.
У порта румбы 7 контактов — по два на питание и землю, обнаружение устройства (Device Detect — DD), RX и TX (см. на рисунке ниже). DD мы воткнули во 2й цифровой выход Ардуино, Rx и Tx — в первый и нулевой.
Однако позже, после обнаружения ошибки в считывании сенсоров, мы воткнули их в 10 и 11 пины (см. ниже). Питание подсоединили с питанием Arduino, а землю, соответственно, с землёй. Другой конец кабеля нужно подключить к соответствующему разъёму на Roomba (он находится под крышкой).

Однако, мы столкнулись с некоторыми проблемами:

  1. Код, который приведён здесь предназначен для старой версии Arduino. Он использует такую встроенную переменную как BYTE, которой больше нет в Arduino. Для того чтобы передавать по Serial байты, необходимо использовать команду Serial.write() вместо Serial.print(). При этом, когда мы пишем Serial.write(0), возникает странная ошибка: компилятор не знает в качестве переменной какого типа выступает ноль. Для исправления этой ошибки просто прикастуйте 0 к переменной типа byte: Serial.write((byte)0); . После этого у нас заработали актуаторы. Румба стала передвигаться, куда мы ей скажем.
  2. Однако сенсоры (бампер) так и не заработали. Стали вылавливать проблему дебагом. Поэтому пришлось перенести Rx и Tx для общения с Roomba на другие пины (10 и 11) при помощи библиотеки SoftwareSerial, чтобы по стандартным serial-пинам передавать информацию на Serial Monitor. Но, к моему удивлению, дебажить даже не пришлось, потому что это и оказалось решением проблемы.
  3. Пришлось также немного повозиться со скоростью передачи данных (baud rate). По умолчанию Roomba общается на скорости 57600. Однако она настраивается на скорость 19200. Наша румба была на скорости 19200.

Вот итоговый код, который работает на нашей Roomba:

    #include <SoftwareSerial.h>

    /*
     * RoombaBumpTurn
     * --------------
     * Implement the RoombaComm BumpTurn program in Arduino
     * A simple algorithm that allows the Roomba to drive around
     * and avoid obstacles.
     *
     * Arduino pin 10 (RX) is connected to Roomba TXD
     * Arduino pin 11 (TX) is connected to Roomba RXD
     * Arduino pin 2 is conencted to Roomba DD
     *
     * Updated 20 November 2006
     * - changed mySerial.writes() to use single write((int)v) calls instead of
     * character arrays until Arduino settles on a style of raw byte arrays
     *
     * Created 1 August 2006
     * copyleft 2006 Tod E. Kurt 
     * http://hackingroomba.com/
     */

    int ddPin = 2;
    int ledPin = 13;
    char sensorbytes[10];

    SoftwareSerial mySerial(10,11);

    #define bumpright (sensorbytes[0] & 0x01)
    #define bumpleft (sensorbytes[0] & 0x02)

    void setup() {
    // pinMode(txPin, OUTPUT);
      pinMode(ddPin,  OUTPUT);   // sets the pins as output
      pinMode(ledPin, OUTPUT);   // sets the pins as output
      mySerial.begin(19200);

      Serial.begin(9600);

      /*
     // [re]set baud rate to 57600
     mySerial.write((int)129); // BAUD
     mySerial.write((int)10); // 57600
     delay(100);
     */

      digitalWrite(ledPin, HIGH); // say we're alive

      // wake up the robot
      digitalWrite(ddPin, HIGH);
      delay(100);
      digitalWrite(ddPin, LOW);
      delay(500);
      digitalWrite(ddPin, HIGH);
      delay(2000);
      // set up ROI to receive commands
      mySerial.write((int)128);  // START
      delay(50);
      mySerial.write((int)130);  // CONTROL
      delay(50);
      digitalWrite(ledPin, LOW);  // say we've finished setup
    }

    void loop() {

      digitalWrite(ledPin, HIGH); // say we're starting loop
      updateSensors();
      digitalWrite(ledPin, LOW);  // say we're after updateSensors
      if(bumpleft) {
        spinRight();
        delay(1000);
      }
      else if(bumpright) {
        spinLeft();
        delay(1000);
      }
      goForward();

    }

    void stopRoomba() {
      mySerial.write((int)137);   // DRIVE
      mySerial.write((byte)0);   // 0x0000== 0
      mySerial.write((byte)0);
      mySerial.write((byte)0);
      mySerial.write((byte)0);
    }

    void goForward() {
      mySerial.write((int)137);   // DRIVE
      mySerial.write((byte)0);   // 0x00c8 == 200
      mySerial.write((int)200);
      mySerial.write((int)128);
      mySerial.write((byte)0);
    }
    void goBackward() {
      mySerial.write((int)137);   // DRIVE
      mySerial.write((int)255);   // 0xff38 == -200
      mySerial.write((int)56);
      mySerial.write((int)128);
      mySerial.write((byte)0);
    }
    void spinLeft() {
      mySerial.write((int)137);   // DRIVE
      mySerial.write((byte)0);   // 0x00c8 == 200
      mySerial.write((int)200);
      mySerial.write((byte)0);
      mySerial.write((int)1);   // 0x0001 == spin left
    }
    void spinRight() {
      mySerial.write((int)137);   // DRIVE
      mySerial.write((byte)0);   // 0x00c8 == 200
      mySerial.write((int)200);
      mySerial.write((int)255);
      mySerial.write((int)255);   // 0xffff == -1 == spin right
    }

    void updateSensors() {
      mySerial.write((int)142);
      mySerial.write((int)1);  // sensor packet 1, 10 bytes
      delay(100);

      // wipe old sensor data
      char i = 0;
      while (i < 10) {
        sensorbytes[i++] = 0;
      }
      i = 0;
      while(mySerial.available()) {
        //xbmySerial.writeln("rsd"); // reading sensor data
        int c = mySerial.read();
        if( c==-1 ) {
          for( int i=0; i<5; i ++ ) {   // say we had an error via the LED
            digitalWrite(ledPin, HIGH);
            delay(50);
            digitalWrite(ledPin, LOW);
            delay(50);
          }
        }
        sensorbytes[i++] = c;
      }

    }

В итоге, наша Roomba едет вперёд, пока не наткнётся на препятствие. После столкновения она оценивает, с какой стороны препятствие: если слева, поворачивает вправо; если справа, идет налево.

UPD: здесь также можно почитать о том, как запрограммировать Румбу при помощи Ардуино


0 комментариев на «“Turtlebot — первые шаги”»

    • Спасибо! Сейчас копаемся с ROS. При получении результатов, выложим новую статью 🙂

    • здорово, желаю успехов! 🙂
      по ROS, я тут уже много насобирал. если будут вопросы — обращайтесь 😉

Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Arduino Bluetooth CraftDuino DIY Google IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение