В последнее время роботы активно входят в нашу жизнь. Их сфера применения уже не ограничивается производством, они широко используются в военной сфере, при разборе завалов, так же все больше появляется роботов «для людей», например: роботы пылесосы, роботы-друзья, и т.д.
В этом посте я хотел бы рассказать о своем опыте создания робота(мобильной платформы способной выполнять заложенную в него программу).
Начнем
Голова
В качестве управляющего центра робота была выбрана Arduino Uno. Почему? Только потому, что это уже готовая плата с выведенными пинами и удобной прошивкой контроллера. Саму программу под микроконтроллер atmega328p я писал в AtmelStudio на Си с использованием библиотек производителя.
Моторы
Для управления колесами мобильной платформы мне потребовался драйвер двигателя. Я остановился на Pololu TB6612FNG Dual Motor Driver Carrier(этом), потому что он компактен, легко устанавливается на макетную плату, удовлетворяет все мои потребности.
Два комплекта мотор-колесо-энкодер были приобретены там же. В нем кстати говоря очень интересно работает энкодер.
Питание
В качестве источника питания используется Li-ion аккумулятор, подключенный через преобразователь, кстати говоря той же фирмы.
Ну и датчики
Помимо двух энкодеров о которых говорилось выше, на роботе установлен ультразвуковой дальномер hc-sr04. Как он работает я считаю тут рассказывать смысла нет.
Ну вот, думаю я описал основные элементы робота. Я не стал детально описывать все в деталях, потому что пост получился бы достаточно объемным, а тот кому это действительно интересно может написать мне на почту.
Напишем программу
Изначальной задачей, стоявшей передо мной написать программу по выходу из простенького лабиринта. Для того что бы реализовать эту задачу мне понадобилось использовать такой встроенный функционал контроллера:
- ШИМ — для регуляции скорости моторов
- Внешние прерывания — для получения информации о препятствиях от датчика(hc-sr04)
- 16 битный таймер — для измерения длинны импульса с Echo порта дальномера
- Ну и управление портами ввода-вывода — здесь объяснять я думаю не стоит
Из программных функций мне потребовались лишь задержки и переменные. Много? Нет! Все просто!
Вот собственно код, который получился в итоге:
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define motor1_0 0
#define motor1_1 1
#define motor2_0 4
#define motor2_1 7
//#define F_CPU 16000000L
void work_func();
int getDist();
volatile int readTrue = 0;
volatile unsigned int d = 0;
int main()
{
DDRD = 255;
//Настройка ШИМ для управления скоростью двигателей
TCCR0A = (0<<COM0A0)|(1<<COM0A1)|(0<<COM0B0)|(1<<COM0B1)|(1<<WGM01)|(1<<WGM00);
TCCR0B = (1<<FOC0A)|(1<<FOC0B)|(0<<WGM02)|(1<<CS00)|(0<<CS01)|(0<<CS02);
OCR0A = 255; //2 двигатель
OCR0B = 248; //1 двигатель
// Прерывания
DDRD &= ~(1 << DDD2);
PORTD |= (1 << PORTD2);
EICRA = (0 << ISC01) | (1 << ISC00); // set INT0 to trigger on ANY logic change
EIMSK |= (1 << INT0); // Turns on INT0
cli(); //Возможно не нужно
//Настройка TRIG
DDRD |= (1 << PORTD3);
// Настройка LED
DDRB = 0b11111111;
while(1)
{
work_func();
}
}
void work_func()
{
if(getDist() <= 10)
{
//Если препятствие ближе чем 8 см, то поврот на 90 град врпаво
PORTB |= (1 << PORTB5);
PORTD &= ~((1 << motor1_0) | (1 << motor2_0));
PORTD |= (1 << motor1_0) | (1 << motor2_1);
_delay_ms(250);
PORTD &= ~((1 << motor1_0) | (1 << motor2_1));
if(getDist() <= 10)
{
//Если препятствие ближе чем 10 см, то поврот на 180 град влево
PORTD |= (1 << motor1_1) | (1 << motor2_0);
_delay_ms(515);
PORTD &= ~((1 << motor1_1) | (1 << motor2_0));
if(getDist() <= 10)
{
//Если препятствие ближе чем 10 см, то поврот на 90 град влево
PORTD |= (1 << motor1_1) | (1 << motor2_0);
_delay_ms(250);
PORTD &= ~((1 << motor1_1) | (1 << motor2_0));
}
}
}
else
{
// Если препятсвия нет, то едем вперед.
PORTB &= ~(1 << PORTB5);
PORTD |= (1 << motor1_0) | (1 << motor2_0);
}
//PORTB &= ~(1 << PORTB2);
_delay_ms(120);
}
int getDist()
{
PORTD |= (1 << PORTD3);
_delay_us(10);
PORTD &= ~(1 << PORTD3);
sei();
while(readTrue == 0);
cli();
readTrue = 0;
short int cm =(short) d/2/58;
//
return cm;
}
ISR (INT0_vect) // Обработчик прерывания (используется для получения данных с датчика)
{
if((PIND & 4) != 0)
{
TCCR1B = 2;
}
else
{
d = TCNT1;
TCCR1B = 0;
TCNT1 = 0;
readTrue = 1;
}
}
Таким образом робот едет вперед пока не увидит препятствие. Как только он увидит препятствие находящееся ближе 10 см., он поворачивается вправо на 90 градусов. Далее если после поворота препятствия не обнаружено, то он едет вперед до следующего препятствия, а если там тоже препятствие то робот делает разворот на 180 градусов влево и опять проверяет есть там препятствие или нет. Если и там препятствие то робот поворачивается еще на 90 градусов влево(назад от начального направления) и едет назад.
Заключение
С помощью данного поста я хотел поделится опытом и показать, что это не такая сложная задача. Тем более, что я для написания программы пользовался AtmelStudio. Если же использовать Arduino Sketch то количество кода и сложность программы сократятся в разы.
P.S. Это мой первый пост, поэтому прошу сильно не пинать и если где-то найдете ошибки/опечатки писать лично.
Автор: user4291