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

в 15:28, , рубрики: arduino, arduino due, diy или сделай сам, lego technic, qt, robotics, xbee, робототехника, метки: , , , , , ,

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Как и в прошлый раз, его можно скачать с моего сайта 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);

}

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

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

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

Автор: dsedov

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js