Как построить робота – тестирование аппаратной части
Как построить робота (от Travis Fagerness)
Третья часть из серии статей о создании робота, который может следовать за линиями или стенами и избегать препятствий.
Обзор
Это третья часть из серии статей о моем опыте создания робота, который может делать разные простые вещи. Я думал, что было бы замечательно создать робота, которого легко было бы собрать лишь с одним паяльником, и комплектующие к нему были бы доступны. Я составил следующий список требований к этому роботу:
- Многие наборы стоят дорого, поэтому он должен быть относительно недорог.
- Он должен быть простым в сборке, не требуя специального оборудования.
- Он должен быть простым в программировании, не требуя сложных IDE и программатора.
- Он должен быть мощным для расширяемости.
- Он должен запускаться от простого источника питания.
- Он должен быть способен следовать за линией или за стеной и избегать препятствий.
В данной статье я расскажу о том, как я собрал робота и написал библиотеку для тестирования его схемы.
Сбор компонентов
Я заказал все компоненты, и они пришли за достаточно быстрое время. Теперь всё готово для сборки.
Сборка механических компонентов
Шаровая опора
Шаровая опора пришла в виде комплекта, который еще нужно собрать. В нем предлагалось несколько различных вариантов размера. Это было не слишком сложно, поэтому во время сборки я не делал никаких фотографий.
Двигатель
Двигатель также пришел в виде набора и оказался для сборки намного сложнее. Набор двигателя предлагал 4 различных передаточных числа. В итоге я выбрал 38:1 для большого соотношения, потому что не хотел, чтобы робот был слишком медленным, но всё же справлялся с весом нагрузки. Более высокие передаточные отношения давали бы ненужное количество крутящего момента. Единственный реальный вес на роботе – это батарея. Передаточное отношение всегда можно отрегулировать после сборки, но это будет немного сложно. Скорость двигателя можно отрегулировать, изменяя коэффициент заполнения в ШИМ сигнале на контроллере двигателя, поэтому, если передаточное отношение будет слишком высоким, его можно снизить с помощью программы.
Ниже показаны все компоненты из набора. Для сборки я использовал разделочную доску, так как у маленьких винтов есть привычка скатываться со стола.
Полностью собранные двигатели и передачи для робота показаны ниже. Набор пришел со всем, что требуется, включая смазку. Единственный инструмент, который мне понадобился, – это отвертка.
Закрепление на печатной плате, всё отлично совпало с линиями шелкографии!
Пайка компонентов
Я использовал разъемы (мама), чтобы была возможность позже поменять компоненты. Пайка на DIP платы всех штырьковых разъемов (папа) была немного утомительной, но намного легче, чем паять все компоненты поверхностного монтажа. Чтобы датчик линии был достаточно близко к земле для эффективной работы, мне пришлось использовать кучу разъемов, соединенных вместе. Если бы я начал с нуля, то предпочел бы более аккуратный способ установки датчика линии.
Тестирование двигателей
Сначала я просто подключил двигатели непосредственно к батареям 2AA, чтобы убедиться, что шестеренки хорошо смазаны. Я также измерил ток, чтобы убедиться, что он соответствует техническому описанию. Убедившись, что двигатели могут крутиться вперед и назад, я подключил их обратно к драйверу. Я написал несколько тестовых программ, которые запускают двигатели вперед и назад в течение пяти секунд. Это сразу подтвердило связь между Teensy, контроллером двигателей и двигателями. Приятно, когда вам не нужно устранять проблемы с подключением на первой версии платы! Следующий код перемещает робота вперед в течение 5 секунд с половиной мощности, а затем назад в течение 5 секунд. Я написал драйвер под названием "robot.ino", который обрабатывает повороты и показания датчика. Код драйвера приведен в конце статьи. Чтобы запрограммировать Teensy, загрузите дополнение Teensyduino для платформы Arduino. Программирование выполняется точно так же, как и для Arduino.
Во время тестирования в качестве переключателя "вкл/выкл" я использовал перемычку.
Он ожил!
#include "robot.h"
void setup()
{
Serial.begin(38400);
Serial.println("Boot");
rbt_init();
rbt_move(FWD,100);
delay(5000);
rbt_move(REV,100);
delay(5000);
rbt_move(BRAKE,0);
}
void loop()
{
}
Тестирование датчиков
Чтобы проверить датчики, я написал программу, которая печатала необработанные значения с датчиков. Я узнал, что не мог использовать датчики стен одновременно, так как они будут срабатывать в течение 100% времени. Причина этого в том, что у ИК-приемников широкий угол, и они ловят сигналы от соседних передатчиков. Я написал библиотеку так, чтобы она поочередно считывала каждый датчик, пользуясь выводом включения датчика. Датчики стен также должны быть слегка наклонены, так как они могут воспринимать землю как объект.
Функция считывания датчика выполняется 1 мс, предлагая, если необходимо, скорость 1 кГц для обработки довольно сложных алгоритмов управления. Тестовый код также показал, что датчик линии должен быть очень близко к земле, чтобы эффективно определять разницу между цветами. Если робот находится в паре дюймов от земли, датчики линий выдают максимальные значения 1000. Это действительно может быть полезно для того, чтобы робот переставал двигаться, когда его переносят.
Датчик линии находится очень близко от земли, но ее не касается!
#include "robot.h"
void setup()
{
Serial.begin(38400);
Serial.println("Boot");
rbt_init();
}
uint16_t lleft,lmid,lright;
boolean wleft,wmid,wright;
void loop()
{
rbt_sns(&lleft,&lmid,&lright,&wleft,&wmid,&wright);
Serial.print("Line left: ");
Serial.print(lleft);
Serial.print("Line mid: ");
Serial.print(lmid);
Serial.print("Line right: ");
Serial.print(lright);
Serial.print("Wall left: ");
Serial.print(wleft);
Serial.print("Wall mid: ");
Serial.print(wmid);
Serial.print("Wall right: ");
Serial.println(wright);
}
Заключение
В данной статье я показал процесс разработки робота и тестирование его компонентов по отдельности. Это тестирование важно выполнить при получении первой версии печатной платы, так как вы не знаете, какие ошибки могли быть сделаны при ее разработке и изготовлении. Если вы сразу приступите к разработке программы, то можете пропустить что-то, что вызовет проблемы в будущем. В следующей статье я расскажу о том, как заставить робота следовать за линией, отредактировав простой алгоритм для того, чтобы робот оставался в центре черной линии.
Библиотека Robot
robot.h
#ifndef _ROBOT_H
#define _ROBOT_H
#include "Arduino.h"
/* DRV8835 */
const int BPHASE = 5;
const int APHASE = 3;
const int AEN = 4;
const int BEN = 6;
const int DRV_MODE = 2;
#define MOTOR_REV LOW
#define MOTOR_FWD HIGH
/* интерфейс датчика отражения */
const int OUT1 = 33;
const int OUT2 = 32;
const int OUT3 = 31;
/* интерфейс датчика стен */
const int WALL_LEFT_EN = 15;
const int WALL_LEFT = 14;
const int WALL_RIGHT_EN = 19;
const int WALL_RIGHT = 18;
const int WALL_MID_EN = 17;
const int WALL_MID = 16;
/* интерфейс робота */
typedef enum{
LEFT,
RIGHT,
FWD,
REV,
BRAKE,
}direction_t;
void rbt_move(direction_t new_dir, uint8_t speed);
void rbt_sns( uint16_t *line_left,
uint16_t *line_mid,
uint16_t *line_right,
boolean *wall_left,
boolean *wall_mid,
boolean *wall_right);
void rbt_init();
#endif /*_ROBOT_H*/
robot.ino
#include "robot.h"
void rbt_init()
{
pinMode(BPHASE, OUTPUT);
pinMode(APHASE, OUTPUT);
pinMode(AEN, OUTPUT);
pinMode(BEN, OUTPUT);
pinMode(DRV_MODE, OUTPUT);
pinMode(WALL_LEFT_EN, OUTPUT);
pinMode(WALL_MID_EN, OUTPUT);
pinMode(WALL_RIGHT_EN, OUTPUT);
pinMode(WALL_LEFT, INPUT);
pinMode(WALL_MID, INPUT);
pinMode(WALL_RIGHT, INPUT);
digitalWrite(WALL_LEFT_EN,LOW);
digitalWrite(WALL_MID_EN,LOW);
digitalWrite(WALL_RIGHT_EN,LOW);
/* упрощенный режим привода */
digitalWrite(DRV_MODE, HIGH);
}
void rbt_move(direction_t new_dir, uint8_t speed)
{
if(speed)
{
switch(new_dir)
{
case LEFT:
digitalWrite(BPHASE,MOTOR_FWD);
digitalWrite(APHASE,MOTOR_FWD);
analogWrite(AEN,speed);
analogWrite(BEN,speed-speed/2);
break;
case RIGHT:
digitalWrite(BPHASE,MOTOR_FWD);
digitalWrite(APHASE,MOTOR_FWD);
analogWrite(AEN,speed-speed/2);
analogWrite(BEN,speed);
break;
case FWD:
digitalWrite(BPHASE,MOTOR_FWD);
digitalWrite(APHASE,MOTOR_FWD);
analogWrite(AEN,speed);
analogWrite(BEN,speed);
break;
case REV:
digitalWrite(BPHASE,MOTOR_REV);
digitalWrite(APHASE,MOTOR_REV);
analogWrite(AEN,speed);
analogWrite(BEN,speed);
break;
default:
analogWrite(AEN,0);
analogWrite(BEN,0);
break;
}
}
else
{
analogWrite(AEN,0);
analogWrite(BEN,0);
}
}
/* функция выполняется за 1 мс */
#define LOOP_ITER_CNT 2
void rbt_sns( uint16_t *line_left,
uint16_t *line_mid,
uint16_t *line_right,
boolean *wall_left,
boolean *wall_mid,
boolean *wall_right)
{
*line_left=0;
*line_mid=0;
*line_right=0;
uint16_t usec_timer=0;
/* датчик линии */
/* зарядить линии */
pinMode(OUT1, OUTPUT);
pinMode(OUT2, OUTPUT);
pinMode(OUT3, OUTPUT);
digitalWrite(OUT1,HIGH);
digitalWrite(OUT2,HIGH);
digitalWrite(OUT3,HIGH);
delayMicroseconds(3);
/* установить высокоимпедансное (Hi-Z) состояние, чтобы позволить конденсатору разрядиться */
pinMode(OUT1, INPUT);
pinMode(OUT2, INPUT);
pinMode(OUT3, INPUT);
/* включить первый датчик стены */
digitalWrite(WALL_LEFT_EN,HIGH);
while(1)
{
/* каждый цикл составляет около 2 мкс при частоте 48 МГц */
usec_timer+=LOOP_ITER_CNT;
/* увеличить значения счетчиков для датчиков линии каждую микросекунду отслеживания разряда конденсатора */
if(digitalRead(OUT1) == 1)
{
(*line_left)+=LOOP_ITER_CNT;
}
if(digitalRead(OUT2) == 1)
{
(*line_mid)+=LOOP_ITER_CNT;
}
if(digitalRead(OUT3) == 1)
{
(*line_right)+=LOOP_ITER_CNT;
}
/* поочередно считывать датчики стен, потому что они мешают друг другу */
if(usec_timer == 300)
{
*wall_left = (digitalRead(WALL_LEFT) ? false:true);
digitalWrite(WALL_LEFT_EN,LOW);
}
if(usec_timer == 400)
{
digitalWrite(WALL_MID_EN,HIGH);
}
if(usec_timer == 700)
{
*wall_mid = (digitalRead(WALL_MID) ? false:true);
digitalWrite(WALL_MID_EN,LOW);
}
if(usec_timer == 700)
{
digitalWrite(WALL_RIGHT_EN,HIGH);
}
if(usec_timer>=1000)
{
*wall_right = (digitalRead(WALL_RIGHT) ? false:true);
digitalWrite(WALL_MID_EN,LOW);
return;
}
}
}