CraftDuino v2.0
  • - это CraftDuino - наш вариант полностью Arduino-совместимой платы.
  • CraftDuino - настоящий конструктор, для очень быстрого прототипирования и реализации идей.
  • Любая возможность автоматизировать что-то с лёгкостью реализуется с CraftDuino!
Просто добавьте CraftDuino!

Кинетическое управление мобильной платформой на Arduino

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

Для этого нам понадобится:
1) 4 двигателя с редуктором и 4 колеса, например, отсюда


2) Драйвер двигателя L289


3) Плата Arduino 2 шт, можно использовать 2 Arduino Uno, но я использую Arduino Mega и Iskra Neo (аналог Leonardo)


4) NRF24L01 2 шт

5)Трехосный акселерометр(я использую LIS331DLH)


6) Преобразователь линейный понижающий на базе ams1117-3.3, 3.3V 1A

7) Arduino proto shield


8) Отсек для батареек на 6/8 шт
9) Соединительные провода

Итак, для начала необходимо собрать мобильную платформу по следующей схеме:


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

Драйвер двигателя подключается так:
Порт драйвераПорт Arduino
12V К «+» батарейного отсека/VIN
GND К «-» батарейного отсека/GND
ENA9
IN12
IN23
IN34
IN45
ENB10

Подключение преобразователя
ПреобразовательArduino
Vin5V
GNDGND
VoutВ питание NRF24L01

Подключение NRF24L01

NRF24L01Arduino
1GND
2Vout понижающего преобразователя
37
48
5SCK
6MOSI
7MISO
8Не подключаем

В итоге у меня это выглядит так:

Далее собираем «пульт» для управления нашей машинкой по следующей схеме:

NRF24L01 подключается аналогично, единственное отличие, в том что второй контакт NRF идет в восьмой порт Arduino, а третий – в девятый.
Акселерометр подключается по шине I2c
SDA SDA
SCL SCL
GND GND
5V 5V
Для того, чтобы закрепить акселерометр на руке можно собрать на отдельной плате этот модуль, а arduino закрепить, скажем, на поясе, либо же собрать схему на proto-шилде и крепить к руке вместе с arduino. Я выбрал второй вариант.


И общий вид:

После сборки нам необходимо соотнести положения руки и показания акселерометра. Я использовал следующую схему управления (Ваши показания могут отличаться):

Положение рукиДействия машинки
Рука горизонтальноНе двигаемся
Рука Вверх(от 3 до 6)/Вниз (от -6 до -3) по оси XДвижение вперед/назад
Рука Влево(от -10 до -3)/Вправо от (3 до 10)по оси YДвижение против/по часовой стрелки

Далее определим протокол обмена между пультом и платформой:
NRF модуль отправляет трехбайтовый массив
Data[0] – определяет направление движения 1 – вперед, 2 – назад, 0 – стоим.
Data[1] – определяет скорость движения вперед/назад
Data[2] – определяет направление движения по/против часовой стрелки: 0 – не крутимся, 1 – против часовой 2 – по часовой

Исходные коды скетчей и библиотеки (RF24-master, Troyka-IMU-master) можно найти на Github’е либо в конце статьи.

Код для определения показаний акселерометра:

//ТК я работаю с несколькими платами, и у некоторых из них
//аппаратный и программный  UART, отличаются я использую следующее "переименование"
#define SerialPC Serial
// библиотека для работы I²C
#include <Wire.h>
// библиотека для работы с модулями IMU
#include "troyka-imu.h"

Accelerometer accel;

void setup()
{
  // открываем последовательный порт
  SerialPC.begin(115200);
  delay(3000);
  // пока не появились данные с USB
  // ждём
  //while (!SerialUSB.available()) {}
  // выводим сообщение о начале инициализации
  SerialPC.println("Begin init...");
  // инициализация акселерометра
  accel.begin();
  // выводим сообщение об удачной инициализации
  SerialPC.println("Init completed");
}

void loop()
{
  /*
  	!!!ВАЖНО!!!
  	библиотека возвращает значение приблизительно от -10.00 до +10.00
  	Причем значения типа float(1.0, 1.53 9.87 и так далее)
  	у меня не было необходимости получать значения с сотыми,
  	Поэтому в данном коде используется тип int, дробная часть отбрасывается
  */
  // Считываем значение по оси X и выводим его в Serial
  int x = accel.readX_G();
  SerialPC.print(x);
  SerialPC.print("\t");
  // Считываем значение по оси Y и выводим его в Serial
  int y = accel.readY_G();
  SerialPC.print(y);
  SerialPC.print("\t");
  //Считываем значение по оси Z и выводим его в Serial
  int z = accel.readZ_G();
  SerialPC.print(z);
  SerialPC.print("\t\t");

  SerialPC.println("");
  delay(300);
}

Код для пульта, все, что относится к Serial используется только для отладки

#define SerialPC Serial
//Минимальное и максимальное значения ШИМ для движения вперед/назад
#define MIN_PWM 100
#define MAX_PWM 250
// библиотека для работы I²C
#include <Wire.h>
// библиотека для работы с модулями IMU
#include <troyka-imu.h>
//Библиотеки для работы с NRF24
#include <SPI.h>
#include "RF24.h"
//Переменные, определяющие направление движения
bool left = false;
bool right = false;
bool top = false;
bool down = false;

// создаём объект для работы с акселерометром
Accelerometer accel;
/* Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 9 & 10 */
RF24 radio(9, 10);
const uint64_t pipe = 0xF0F1F2F3F4LL; // индитификатор передачи, "труба"
//Массив данных
byte data[] = {0, 0, 0};
void setup() {
  //Задержка перед включением пульта
  //Нужна была для отладки
  //delay(5000);
  //инициализация Serial соединения
  SerialPC.begin(115200);
  SerialPC.println("setup");

  // инициализация акселерометра
  SerialPC.println("Init accel");
  accel.begin();
  SerialPC.println("Accel init");
  delay(100);

  //Инициализация NRF24L01
  SerialPC.println("Init radio");
  radio.begin();
  SerialPC.println("radio init");
  delay(100);
  //Установка параметров NRF24L01
  SerialPC.println("Set radio param");
  // канал (0-127)
  radio.setChannel(9);
  // скорость, RF24_250KBPS, RF24_1MBPS или RF24_2MBPS
  // RF24_250KBPS на nRF24L01 (без +) неработает. 
  // меньше скорость, выше чувствительность приемника.
  radio.setDataRate(RF24_1MBPS);
  // мощность передатчика, RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_MED=-6dBM,
  radio.setPALevel(RF24_PA_HIGH);
  // открываем трубу на передачу.
  radio.openWritingPipe(pipe);
  SerialPC.println("end set radio param");
  delay(100);

  SerialPC.println("end setup");
}
//В этих переменных будем хранить значения от акселерометра
int x, y, z;
//Скорость
int speed = 0;

void loop() {
  SerialPC.println("loop");

  //Читаем значения от акселерометра
  SerialPC.print("read x: ");
  x = accel.readX_G();
  SerialPC.println(x);
  SerialPC.print("read y: ");
  y = accel.readY_G();
  SerialPC.println(y);
  SerialPC.print("read z: ");
  z = accel.readZ_G();
  Serial.println(z);
  SerialPC.println("end read.");

  //Устанавливаем значения направления
  //и скорости "по умолчанию"
  //ТЕ нет поворота, нет движения, скорость = 0

  //Нет поворота
  left = false;
  right = false;

  //Нет движения
  down = false;
  top = false;
  speed = 0;
  //Определяем есть ли поворот руки
  //Поворот направо
  if ( y > 3) {
    right = true;
  }//Поворот налево
  else if ( y < -3) {
    left = true;
  }

  //Определяем наклоняется ли рука
  //Рука вниз
  if ( x <= -3) {
    down = true;
    //Приводим значение x в диапазон от -6 до -3, затем
    //отображаем это значение на диапазон от MIN_PWM до MAX_PWM
    speed = map(constrain(x, -6, -3), -3, -6, MIN_PWM, MAX_PWM);
  }//рука вверх
  else if (x >= 3) {
    top = true;
    //Приводим значение x в диапазон от 3 до 6, затем
    //отображаем это значение на диапазон от MIN_PWM до MAX_PWM
    speed = map(constrain(x, 3, 6), 3, 6, MIN_PWM, MAX_PWM);
  }
  //Заносим значения в массив, который будем отправлять
  //Направление вперед/назад
  //конструкция a = (b) ? 1 : 2; равносильна
  // if (b) { a = 1; } else {a = 2;}
  data[0] = (top) ? 1 : (down) ? 2 : 0;
  //Скорость
  data[1] = speed;
  //Движение по/против часовой стрелки
  data[2] = (left) ? 1 : (right) ? 2 : 0;

  //Выводим значения перед отправкой в Serial
  //Для отладки
  for (int i = 0; i < 3; i++) {
    SerialPC.print(data[i]);
    SerialPC.print(" ");
  }
  SerialPC.println("");
  SerialPC.println("clear buff");
  //Отчищаем буфер перед отправкой (необходимо на некоторых модулях)
  radio.flush_tx();
  SerialPC.println("sending...");
  //Пытаемся отправить
  if (!radio.write(data, sizeof(data))) {
    //Если не удалось отправить выводим в Serial сообщение об ошибке
    SerialPC.println("fail");
  }

  delay(100);
  SerialPC.println("end loop");
}


Код для мобильной платформы:

//Serial для соединения с ПК
#define SerialPC Serial
//Время в мс, в течение которого должна придти хотя бы 1 команда
#define TIMEOUT 1000
//Библиотеки для работы с NRF24
#include <SPI.h>
#include "RF24.h"
/* CE и CSN на 7 и 8 пинах */
RF24 radio(7, 8);
//Адрес трубы
const uint64_t pipe = 0xF0F1F2F3F4LL;
//Пины для управления моторами In1, In2, ENA(PWM)
const byte motor1[] = {2, 3, 9};
//Пины для управления моторами In3, In4, ENB (PWM)
const byte motor2[] = {4, 5, 10};
//Формат передаваемых данных
byte data[] = {0, 0, 0};
//Время прихода последней команды
unsigned long lastCommand;
void setup() {
  //Выставляем пины на выход
  for (int i = 0; i < 3; i++) {
    pinMode(motor1[i], OUTPUT);
    pinMode(motor2[i], OUTPUT);
    digitalWrite(motor1[i], LOW);
    digitalWrite(motor2[i], LOW);
  }

  //Инициализация модуля NRF24
  radio.begin();
  delay(20);
  //Настройки модуля
  // канал (0-127)
  radio.setChannel(9);
  // скорость, RF24_250KBPS/RF24_1MBPS/RF24_2MBPS.
  radio.setDataRate(RF24_1MBPS);
  //мощность передатчика RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_MED=-6dBM,
  radio.setPALevel(RF24_PA_HIGH);
  //Открываем трубу на чтение
  radio.openReadingPipe(1, pipe);
  //Начинаем слушать эфир
  radio.startListening();
  //устанавливаем время прихода последней команды
  lastCommand = millis();
}

void loop() {
  //Если время прошедшее с момента последней команды больше чем TIMEOUT,
  //считаем, что произошел обрыв связи и останавливаем машинку
  if (millis() - lastCommand > TIMEOUT) {
    stoped();
  }

  //Если что-то пришло по радио
  if (radio.available()) {
    //Читаем данные
    radio.read(data, sizeof(data));
    //Если 1 - едем вперед, если 2 - едем назад, иначе останавливаемся
    (data[0] == 1) ? forward() : (data[0] == 2) ? back() : stoped();
    //Если разворачиваемся 1 - налево, 2 - направо, 0 ничего не меняем
    if (data[2] == 1) {
      left();
    }
    else if (data[2] == 2) {
      right();
    }

    //Фиксируем время прихода команды
    lastCommand = millis();
  }

}
//Останавливаемся и блокируем моторы
void stoped() {
  setMotorParam(false, false, false, false, 0, 0);
}

void forward() {
  setMotorParam(true, false, true, false, data[1], data[1]);
}

void back() {
  setMotorParam(false, true, false, true, data[1], data[1]);
}

void left() {
  setMotorParam(true, false, false, true, 255, 255);
}

void right() {
  setMotorParam(false, true, true, false, 255, 255);
}
/* d11 - EN1, d12 - EN2
 * d21 - EN3, d22 - EN4
 * s1 - ENA, s2 - ENB 
 */
void setMotorParam(bool d11, bool d12, bool d21, bool d22, int s1, int s2) {
  //Устанавливаем направление движения моторов
  digitalWrite(motor1[0], d11);
  digitalWrite(motor1[1], d12);

  digitalWrite(motor2[0], d21);
  digitalWrite(motor2[1], d22);

  //Устанавливаем скорость вращения моторов
  analogWrite(motor1[2], s1);
  analogWrite(motor2[2], s2);
}


Исходные коды
Библиотеки
  • 0
  • 29 мая 2016, 16:26
  • TelaDi

Комментарии (0)

RSS свернуть / развернуть

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.