суббота, 14 февраля 2015 г.

Радиоуправляемая машинка

В этой статье рассказывается о том, как сделать радиоуправляемую платформу. Роль пульта дистанционного управления будет выполнять передатчик HobbyKing HK6S Transmitter. Приемником в данном случае может быть только HobbyKing HK6DF 6 Channel 2.4GHz Receiver. Однако, есть основания полагать, что подойдет любая RC аппаратура (с соответствующим приемником разумеется). Распознавание сигналов приемника и управление драйвером двигателей, в данном проекте, осуществляется ардуино. В статье приведено подробное описание всего железа и скетч.


Начну с краткого описания аппаратуры.


Продается передатчик вместе с приемником. Но если приемник сломается, то сильно горевать не стоит. Приемник можно купить отдельно.

В продаже есть два типа таких передатчиков: Mode1 и Mode2. Аппаратура Mode2 более распространена и я купил её. В дальнейшем я не буду специально указывать тип аппаратуры, но будет подразумеваться, что речь идет именно о Mode2.

В этом проекте я использовал Arduino Nano с USB UART преобразователем на микросхеме CH340.


Компактная, недорогая и живучая :)

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

Напряжение подаваемое на электрические двигатели будет в районе 5 вольт. Этот параметр зависит скорее от источника тока (аккумулятор).

Максимальный ток использованных мной моторов составляет около 0.6 ампера. Этот параметр измеряется амперметром при заблокированном вале моторчика. Подключаете амперметр последовательно с мотором. Подаете питание. И руками осторожно останавливаете вал. Смотрите показания прибора. Долго блокировать вал моторчика нельзя.

Описанная методика подходит для маломощных коллекторных моторчиков. Прежде чем производить какие либо манипуляции с моторами смотрите документацию на них.

Сразу скажу, что аккумулятор должен быть способен отдать необходимый моторам ток. Иначе просядет напряжение и ардуино перезагрузится.

И ещё. В реальной эксплуатации до блокировки моторы лучше не доводить. Это может привести к их выходу из строя.

Подходящий под мои требования драйвер двигателей изображен на картинке ниже.


Этот драйвер изготовлен на двух микросхемах L9110S. Работает в пределах от 2.5 вольт до 12 вольт. Максимальный допустимый ток для этой микросхемы составляет 0.8 ампера.

Источником тока у меня служат четыре последовательно соединенных NiMH аккумулятора AAA. В полностью заряженном состоянии они выдают немного больше 5 вольт. Поэтому в проекте не должно быть деталей не способных пережить это. Для ATmega328, CH340 и L9110S проблем быть не должно.

Сама платформа та же, что и в статье http://justforduino.blogspot.ru/2014/02/blog-post_23.html

В собранном виде это все выглядит как то так.


Подключается все просто.

Левый электродвигатель платформы подключаются к клеммам драйвера MOTOR-A. Правый электродвигатель платформы подключается к клеммам драйвера MOTOR-B.

Arduino --- Motor Driver

GND --- GND
VCC --- VCC
D8 --- A-IA
D9 --- A-IB
D10 --- B-IA
D11 --- B-IB

Arduino --- Receiver

GND --- GND
VCC --- VCC
D6 --- CH1
D7 --- CH2

Для управления платформой достаточно двух каналов. Я выбрал aileron и elevator. Это правый стик моей аппаратуры. У приемника aileron соответствует первый канал (CH1), а elevator соответствует второй канал (CH2).

Значение управляющего импульса на первом канале (aileron) изменяется при перемещении стика по горизонтали. В крайнем левом положении управляющий импульс составляет 1914 миллисекунд. В среднем положении управляющий импульс составляет 1525 миллисекунд. В крайнем правом положении управляющий импульс составляет 1119 миллисекунд.

Значение управляющего импульса на втором канале (elevator) изменяется при перемещении стика по вертикали. В крайнем верхнем положении управляющий импульс составляет 1905 миллисекунд. В среднем положении управляющий импульс составляет 1507 миллисекунд. В крайнем нижнем положении управляющий импульс составляет 1114 миллисекунд.

Эти цифры получены с помощью приборов описанных в http://justforduino.blogspot.ru/2015/01/blog-post.html и http://justforduino.blogspot.ru/2014/12/blog-post.html

Измерение длительности управляющего импульса в скетче будет осуществляться стандартной функцией ардуино pulseIn(pin, value, timeout) У этой функции три параметра. Первый это ножка ардуино на которой производится измерение. Для ардуино нано значение этого параметра может быть от 0 до 19. Второй параметр определяет измеряемый логический уровень. Возможно всего два варианта. Это HIGH или LOW. Последний параметр не обязателен. Функцию можно вызвать без указания этого параметра. timeout определяет в течении какого времени функция pulseIn пытается измерить длительность импульса. По умолчанию это одна секунда. Функция возвращает значение длительности измеренного импульса в миллисекундах (unsigned long int).

Как именно реализована эта функция можно посмотреть в файле wiring_pulse.c У меня этот файл находится по адресу D:\arduino-1.0.6\hardware\arduino\cores\arduino\wiring_pulse.c

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

Для левого мотора.

D8 (A-IA)D9 (A-IB)
0 (LOW)0 (LOW)мотор не крутится
1 (HIGH)1 (HIGH)мотор не крутится
0 (LOW)1 (HIGH)мотор крутится в одну сторону
1 (HIGH)0 (LOW)мотор крутится в другую сторону

Для правого мотора.

D10 (B-IA)D11 (B-IB)
0 (LOW)0 (LOW)мотор не крутится
1 (HIGH)1 (HIGH)мотор не крутится
0 (LOW)1 (HIGH)мотор крутится в одну сторону
1 (HIGH)0 (LOW)мотор крутится в другую сторону

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

Первый скетч для левого мотора.

////////////////////////
//
// Arduino
//
////////////////////////
//
// Sketch:
//

const unsigned char A_IA = 8;
const unsigned char A_IB = 9;
const unsigned char B_IA = 10;
const unsigned char B_IB = 11;

void forward_stop( void )
{
    digitalWrite(A_IA, LOW);
    digitalWrite(A_IB, HIGH);
    digitalWrite(B_IA, LOW);
    digitalWrite(B_IB, LOW);
}

void setup()
{
    pinMode(A_IA, OUTPUT);
    pinMode(A_IB, OUTPUT);
    pinMode(B_IA, OUTPUT);
    pinMode(B_IB, OUTPUT);

    forward_stop();
}

void loop()
{

}

//
// End
//
////////////////////////

После загрузки этого скетча левый мотор должен начать вращаться вперед. Однако у меня он начал крутится назад. Эту проблему также можно решить множеством способов. Я просто переписал скетч.

Второй скетч для левого мотора.

////////////////////////
//
// Arduino
//
////////////////////////
//
// Sketch:
//

const unsigned char A_IA = 8;
const unsigned char A_IB = 9;
const unsigned char B_IA = 10;
const unsigned char B_IB = 11;

void forward_stop( void )
{
    digitalWrite(A_IA, HIGH);
    digitalWrite(A_IB, LOW);
    digitalWrite(B_IA, LOW);
    digitalWrite(B_IB, LOW);
}

void setup()
{
    pinMode(A_IA, OUTPUT);
    pinMode(A_IB, OUTPUT);
    pinMode(B_IA, OUTPUT);
    pinMode(B_IB, OUTPUT);

    forward_stop();
}

void loop()
{

}

//
// End
//
////////////////////////

С этим скетчем все заработало как надо. Мотор крутится вперед.

Теперь проверочный скетч для правого мотора.

////////////////////////
//
// Arduino
//
////////////////////////
//
// Sketch:
//

const unsigned char A_IA = 8;
const unsigned char A_IB = 9;
const unsigned char B_IA = 10;
const unsigned char B_IB = 11;

void stop_forward( void )
{
    digitalWrite(A_IA, LOW);
    digitalWrite(A_IB, LOW);
    digitalWrite(B_IA, HIGH);
    digitalWrite(B_IB, LOW);
}

void setup()
{
    pinMode(A_IA, OUTPUT);
    pinMode(A_IB, OUTPUT);
    pinMode(B_IA, OUTPUT);
    pinMode(B_IB, OUTPUT);

    stop_forward();
}

void loop()
{

}

//
// End
//
////////////////////////

У меня все получилось с первого раза. Правый мотор крутится вперед.

Теперь известна вся необходимая информация и можно написать скетч радиоуправляемой платформы.

////////////////////////
//
// Arduino
//
////////////////////////
//
// Sketch: RC car
//

const unsigned char A_IA = 8;
const unsigned char A_IB = 9;
const unsigned char B_IA = 10;
const unsigned char B_IB = 11;

const unsigned char CH1 = 6; // Aileron
const unsigned char CH2 = 7; // Elevator

unsigned long int ail = 0;
unsigned long int ele = 0;

void forward_forward( void )
{
    digitalWrite(A_IA, HIGH);
    digitalWrite(A_IB, LOW);
    digitalWrite(B_IA, HIGH);
    digitalWrite(B_IB, LOW);
}

void backward_backward( void )
{
    digitalWrite(A_IA, LOW);
    digitalWrite(A_IB, HIGH);
    digitalWrite(B_IA, LOW);
    digitalWrite(B_IB, HIGH);
}

void forward_stop( void )
{
    digitalWrite(A_IA, HIGH);
    digitalWrite(A_IB, LOW);
    digitalWrite(B_IA, LOW);
    digitalWrite(B_IB, LOW);
}

void stop_forward( void )
{
    digitalWrite(A_IA, LOW);
    digitalWrite(A_IB, LOW);
    digitalWrite(B_IA, HIGH);
    digitalWrite(B_IB, LOW);
}

void backward_stop( void )
{
    digitalWrite(A_IA, LOW);
    digitalWrite(A_IB, HIGH);
    digitalWrite(B_IA, LOW);
    digitalWrite(B_IB, LOW);
}

void stop_backward( void )
{
    digitalWrite(A_IA, LOW);
    digitalWrite(A_IB, LOW);
    digitalWrite(B_IA, LOW);
    digitalWrite(B_IB, HIGH);
}

void stop_stop( void )
{
    digitalWrite(A_IA, LOW);
    digitalWrite(A_IB, LOW);
    digitalWrite(B_IA, LOW);
    digitalWrite(B_IB, LOW);
}

void setup()
{
    pinMode(CH1, INPUT);
    pinMode(CH2, INPUT);

    pinMode(A_IA, OUTPUT);
    pinMode(A_IB, OUTPUT);
    pinMode(B_IA, OUTPUT);
    pinMode(B_IB, OUTPUT);

    stop_stop();
}

void loop()
{
    ele = pulseIn(CH2, HIGH);
    ail = pulseIn(CH1, HIGH);

    if ( ail == 0 ) { ail = 1500; ele = 1500; }
    if ( ele == 0 ) { ail = 1500; ele = 1500; }

    if ( ail > 1700 && ele > 1700 )
        forward_stop();
    if ( ail < 1300 && ele > 1700 )
        stop_forward();
    if ( ail > 1700 && ele < 1300 )
        backward_stop();
    if ( ail < 1300 && ele < 1300 )
        stop_backward();
    if ( ail > 1700 && ele < 1700 && ele > 1300 )
        stop_stop();
    if ( ail < 1300 && ele < 1700 && ele > 1300 )
        stop_stop();
    if ( ail < 1700 && ail > 1300 && ele > 1700 )
        forward_forward();
    if ( ail < 1700 && ail > 1300 && ele < 1300 )
        backward_backward();
    if ( ail < 1700 && ail > 1300 && ele < 1700 && ele > 1300 )
        stop_stop();
}

//
// End
//
////////////////////////

Работать все должно так как описано ниже. Когда правый стик находится в центре, платформа неподвижна. При перемещении стика вперед, платформа должна двигаться вперед. При перемещении стика вперед и влево, левый моторчик двигает платформу вперед. При перемещении стика вперед и вправо, правый моторчик двигает платформу вперед. При перемещении стика назад, платформа должна двигаться назад. При перемещении стика назад и влево, левый моторчик двигает платформу назад. При перемещении стика назад и вправо, правый моторчик двигает платформу назад.

При повторении проекта возможно немного иное поведение платформы. Причина в том, что каждый мотор можно подключить к драйверу двумя способами. Тут следует либо поменять местами провода на "неправильном" моторе, либо подредактировать скетч. Последнее описано в статье достаточно подробно. Думаю проблем быть не должно.