Создание автономного робота Frank. Часть третья

от автора

С последней статьи прошло достаточно много времени. Frank очень сильно изменился. Он, конечно, не стал более самостоятельным, но, можно с уверенностью сказать, что платформа для экспериментов почти готова, и я больше времени теперь буду проводить за написанием кода, нежели чем за конструктором Lego или паяльником.

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

«Создание автономного робота Frank. Часть первая»
«Создание автономного робота Frank. Часть вторая»

Во-первых, полностью изменился принцип работы Френка. С четырех колес он пересел на четыре гусеницы, что значительно облегчило управление и написание кода. Из-за того, как был собран корпус и шасси, внутри просто не оставалось места для того, чтобы поместить туда электронику. Единственное, что помещалось внутрь — была 6v батарея и 4ре серво-мотора, которые вращали колеса и отвечали за угол поворота. Вторая попытка заключалась в том, чтобы полностью избавиться от поворотного механизма и перейти на управления за счет дифференцирования скорости колес. Эта затея тоже провалилась, так как трение между колесами и полом не позволяло колесам немного проскальзывать, и все, чего я добился при помощи дополнительных редукторов — были проскальзывающие шестеренки, которые в конце эксперимента стесались и сразу же оказались в мусорном ведре.

С гусеницами все оказалось проще. Не смотря на то, что резиновые гусеницы в Лего достаточно плохого качества, редуктор сделал свое дело. В гонках Френк участвовать, конечно, не сможет, но тащить за собой груз у него явно получится.

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

В голове я закрепил две камеры, которые пока никак не связаны с микросхемами. Так же туда залез ультразвуковой сенсор и серво-мотор, который позволяет голове вращаться.

Так как я обзавелся батареей, он стал полностью автономным, в том смысле, что он уже не должен быть подключен к ноутбуку по USB кабелю. Все общение происходит через последовательный беспроводной порт — XBee, о котором я писал раньше. Все это и многое другое заставило меня изменить микросхемы, которых теперь три, если считать Arduino Due.

Вот изменения, который произошли.

1) Я наконец-то припаял XBee на схему. Теперь она часть устройства.
2) Для интереса я засунул туда температурный датчик.
3) Я сделал входы для 4х серво-моторов, ультразвукового сенсора, аккумулятора и трех солнечных батарей (до них очередь еще не дошла)
4) Вывел один интрефейс NXT наружу если мне вдруг понадобится подключить какой-то сенсор для Lego (компас или гироскоп, например)
5) Так как теперь все работает от одной батареи, то я сделал внешний выключатель, и так же припаял конденсатор, чтобы избавиться от «шума» серво-двигателей.
6) Так как подключение аккумулятора и солнечных батарей находится на внешней плате, а сама разводка питания (которая еще не закончена) на средней, то я сделал дополнительные соединения между ними
7) Питание от аккумулятора я завел на один из аналоговых входов, чтобы мерять напряжение.

Естественно изменился и софт!

Как и в прошлый раз, его можно скачать с моего сайта Frank — Autonomous Vehicle

Что же изменилось в ПО?

То, как я пытался общаться с Arduino через XBee, используя стороннюю библиотеку, было в корне не верно. Все оказалось гораздо проще. Используя встроенную библиотеку Serial, все начало работать и посылаться. Есть небольшие нюансы, но о них позже.

Теперь я посылаю все данные, которые на данный момент мне доступны в iFrank, а именно:
1) Заряд батареи
2) Расстояние до ближайшего объекта (с ультразвукового сенсора)
3) Температура
4) Текущие переменные по управлению серво-двигателями.

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

Как вы увидите в коде iFrank и Arduino, в конце сигнала я посылаю символ "*" (в случае arduino->iFrank) и "\n" (в случае iFrank->arduino). Так как при чтении из последовательного порта информация приходит кусками, то в iFrank я организовал буфер, в который сваливается информация. Асинхронный таймер периодически считывает оттуда информацию и обрабатывает кусками, разделенными "*".

Ниже находится код Arduino:

Код Arduino

#include <Servo.h> #include <math.h>  //Sensors int dataTemperature = 0; int dataBattaryVoltage = 0; int dataSolarVoltage = 0; int dataMotor1 = 0; int dataMotor2 = 0; int dataSteering1 = 0; int dataSteering2 = 0; int dataCamPan = 0; int dataCamTilt = 0; int dataGyrX = 0; int dataGyrY = 0; int dataGyrZ = 0; int dataAccX = 0; int dataAccY = 0; int dataAccZ = 0; int dataGPSTime = 0; int dataGPSLong = 0; int dataGPSLat = 0; int dataGPSSat = 0; float dataDistance = 0; int dataTemp2 = 0; int dataTemp3 = 0; int dataTemp4 = 0; int dataTemp5 = 0; int dataTemp6 = 0; int dataTemp7 = 0; int dataTemp8 = 0; #define trigPin 31 #define echoPin 30  //Voltage const float referenceVolts = 3.3; const float resistor1 = 20000; const float resistor2 = 2200; const float resistorFactor = resistor2/(resistor1+resistor2); const float boardVoltage = 3.3; const float analogVoltMult = boardVoltage/1024; const int batteryPin = 1;  // Servo Code Servo steering1; Servo steering2; Servo motor1; Servo motor2;  // Steering initial states float st1_start = 0.0; float st1_end = 180.0; float st1_step = (st1_end-st1_start)/256.0; float st2_start = 0.0; float st2_end = 180.0; float st2_step = (st2_end-st2_start)/256.0; // Steering function 0-256 float steer1(float p){ float v=st1_start+st1_step*p; steering1.write(v); return v;} float steer2(float p){ float v=st2_start+st2_step*p; steering2.write(v); return v;}  int pos = 0;   // XBEE Code /** XBee xbee = XBee(); unsigned long start = millis(); uint8_t payload[] = { 'H', 'i','t','h','e','r','e' };; Tx16Request tx = Tx16Request(0x4000, payload, sizeof(payload)); TxStatusResponse txStatus = TxStatusResponse(); */ void setup() {   // Serial   Serial.begin(19200);      // Sensors   pinMode(trigPin, OUTPUT);   pinMode(echoPin, INPUT);      // Servos   steering1.attach(2);   steering2.attach(3);   motor1.attach(4);   motor2.attach(5);   dataMotor1 = 94;   dataMotor2 = 94;   dataSteering1 = 130;   dataSteering2 = 130;    }  void loop() {   // Read serial input   while (Serial.available() > 0) {     int st1 = Serial.parseInt();     int st2 = Serial.parseInt();     int mt1 = Serial.parseInt();     int mt2 = Serial.parseInt();     if (Serial.read() == '\n') {       dataSteering1 = st1;       dataSteering2 = st2;       dataMotor1 = mt1;       dataMotor2 = mt2;     }   }        // Read temperature   dataTemperature = analogRead(0);delay(15);   dataTemperature = analogRead(0);   dataBattaryVoltage = analogRead(1);delay(15);   dataBattaryVoltage = analogRead(1);     Serial.print(dataTemperature);Serial.print("|");   Serial.print(dataBattaryVoltage);Serial.print("|");   Serial.print(dataSolarVoltage);Serial.print("|");   Serial.print(dataMotor1);Serial.print("|");   Serial.print(dataMotor2);Serial.print("|");   Serial.print(dataSteering1);Serial.print("|");   Serial.print(dataSteering2);Serial.print("|");   Serial.print(dataCamPan);Serial.print("|");   Serial.print(dataCamTilt);Serial.print("|");   Serial.print(dataGyrX);Serial.print("|");   Serial.print(dataGyrY);Serial.print("|");   Serial.print(dataGyrZ);Serial.print("|");   Serial.print(dataAccX);Serial.print("|");   Serial.print(dataAccY);Serial.print("|");   Serial.print(dataAccZ);Serial.print("|");   Serial.print(dataGPSTime);Serial.print("|");   Serial.print(dataGPSLong);Serial.print("|");   Serial.print(dataGPSLat);Serial.print("|");   Serial.print(dataGPSSat);Serial.print("|");   Serial.print(dataDistance);Serial.print("|");   Serial.print(dataTemp2);Serial.print("|");   Serial.print(dataTemp3);Serial.print("|");   Serial.print(dataTemp4);Serial.print("|");   Serial.print(dataTemp5);Serial.print("|");   Serial.print(dataTemp6);Serial.print("|");   Serial.print(dataTemp7);Serial.print("|");   Serial.print(dataTemp8);   Serial.print("*");      steer1(dataSteering1);   steer2(dataSteering2);   motor1.write(dataMotor1);   motor2.write(dataMotor2);    delay(100);      // Read echo   long duration, distance;   digitalWrite(trigPin, LOW);   delayMicroseconds(2);   digitalWrite(trigPin, HIGH);   delayMicroseconds(10);   digitalWrite(trigPin, LOW);   dataDistance = pulseIn(echoPin, HIGH);  } 

Еще немного фотографий Френка

ссылка на оригинал статьи http://habrahabr.ru/post/193824/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *