С последней статьи прошло достаточно много времени. 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:
#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/
Добавить комментарий