Общие сведения
Создать машинку, движущуюся по линии - классическая задача для соревнований. Да и, пожалуй, каждому было бы интересно заставить ехать машинку по трассе, которую можно создать практически на любой поверхности, даже с помощью изоленты (разумеется, можно воспользоваться и специально предназначенными для этого трассами).
В этом уроке мы покажем свой вариант такой машинки, и соберём её из наших новых модулей.
Нам понадобится:
- 2х Мотор-редуктор с энкодером N20-100rpm-12V с управляющим контроллером
- 1х Бампер с 9 датчиками линий с шагом 14мм или с шагом 7мм
- 1х Piranha Uno R3 (или другой контроллер такого же формата)
- 1х Trema Shield
- 1х Источник питания 5В, 5-12В (Li-ion 14500)
- 1х Аккумулятор 14500 Li-ion, 3.7V, 900mah, с защитой
- 2х i2C Hub (один из них мы используем для подключения питания моторов, чтобы обойтись без пайки)
- 2х Колесо для Micro Metal Gearmotor
- 1x Шаровая опора (12 мм)
- 1х Пластина большая (конструктор ПВХ)
- 1х Крепления Arduino (конструктор ПВХ) для Arduino
- 1х 4-проводной шлейф «мама-мама» 20 см
- 4х Шлейф питания, МАМА-МАМА
- 2х Стойки М3*12 Nylon-black (4 штуки в комплекте)
- 1х Стойки М3*6 Nylon-black (4 штуки в комплекте)
- 1х Винт М3х12, (10 шт в комплекте)
- 1х Винт М3х6 (10 шт в комплекте)
- 1х Гайка М3 (10 шт в комплекте)
- 4х Шайба М3 Nylon (5 шт в комплекте)
- iarduino_I2C_Motor
- iarduino_I2C_Bumper
При необходимости, ознакомьтесь с нашей инструкцией по установке библиотек в Arduino IDE.
Видео
Сборка корпуса
- Из большой пластины ПВХ при помощи канцелярского ножа вырезаем основание машинки. Также вырезаем боковые стойки для контроллера.

2. Контроллер крепим на маленькой пластине ПВХ для Arduino.

3. Приклеиваем (например с помощью двухстороннего скотча) шаровую опору на основание, вырезанное из ПВХ.

4. Крепим бампер на основание (шайбы подкладывайте с двух сторон ПВХ).

5. Прикручиваем стойки на моторы в соответствии с изображением ниже.

6. Прикручиваем моторы к основанию, но не затягиваем (шайбы подкладывайте с двух сторон ПВХ).

7. Устанавливаем на стойки моторов I2C Hub'ы, после чего выравниваем моторы и затягиваем крепящие их винты.

8. Прикручиваем источник питания, устанавливаем ВПХ-элементы.
Установите напряжение 12В на колодке 5-12В источника питания. Для этого выкрутите движок подстроечного резистора на плате по стрелке max в крайнее положение.

9. Устанавливаем I2C-адреса моторам (0х0А - левый и 0х0В - правый) и бамперу (0х0С). Подробно о настройке адресов можно почитать в статье о бампере и в статье о моторах
10. Подключаем провода питания и I2C шины согласно схеме

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

11. Устанавливаем Trema Shield на Arduino, после чего крепим на основу.
12. Устанавливаем колёса

13. Откалибруйте бампер. Подробно о калибровке можно почитать в этой статье
14. Загрузите скетч в контроллер
Скетч проекта
// Подключаем библиотеки: //
#include <Wire.h> // * Подключаем библиотеку для работы с аппаратной шиной I2C.
#include <iarduino_I2C_Motor.h> // Подключаем библиотеку для работы с мотором I2C-flash.
#include <iarduino_I2C_Bumper.h> // Подключаем библиотеку для работы с бампером I2C-flash.
//
// Создаём объекты: //
iarduino_I2C_Motor mot[2]={0x0A,0x0B}; // Объявляем массив mot состоящий из двух объектов для работы с моторами I2C-flash, указав адреса модулей моторов на шине I2C (0x0A-левый, 0x0B-правый).
iarduino_I2C_Bumper bum (0x0C); // Объявляем объект bum для работы с бампером I2C-flash, указав адрес модуля на шине I2C.
//
// Определяем константы: //
const float val_Radius = 21.0f; // Радиуса колеса в мм.
const float val_Speed = 0.06f; // Скорость движения машины в м/сек.
const float pid_KP = val_Speed * 0.15f; // Коэффициент пропорциональной обратной связи ПИД регулятора.
const float pid_KI = val_Speed * 0.005f; // Коэффициент интегральной обратной связи ПИД регулятора.
const float pid_KD = val_Speed * 0.15f; // Коэффициент дифференциальной обратной связи ПИД регулятора.
//
// Объявляем переменные: //
float arr_ERR[10] = {0,0,0,0,0,0,0,0,0,0}; // Массив последних ошибок для формирования интегральной составляющей ПИД регулятора.
uint8_t i, j; // Переменные для работы с массивом последних ошибок.
//
void setup(){ //
delay(500); // * Ждём завершение переходных процессов связанных с подачей питания.
// ИНИЦИАЛИЗАЦИЯ МОДУЛЕЙ: //
mot[0].begin(); // Инициируем работу с левым мотором I2C-flash.
mot[1].begin(); // Инициируем работу с правым мотором I2C-flash.
bum. begin(); // Инициируем работу с бампером I2C-flash.
// НАСТРОЙКА МОДУЛЕЙ: //
mot[0].radius = val_Radius; // Указываем радиус колеса левого мотора в мм.
mot[1].radius = val_Radius; // Указываем радиус колеса правого мотора в мм.
mot[0].setDirection(false); // Указываем левому мотору, что его вращение должно быть обратным (против часовой стрелки при положительных скоростях).
mot[1].setDirection(true); // Указываем правому мотору, что его вращение должно быть прямым ( по часовой стрелке при положительных скоростях).
mot[0].setStopNeutral(false); // Указываем не освобождать ротор левого мотора при его остановке (если указать true, то ротор остановленного мотора можно вращать).
mot[1].setStopNeutral(false); // Указываем не освобождать ротор правого мотора при его остановке (если указать true, то ротор остановленного мотора можно вращать).
bum.setTurnPeriod(BUM_TURN_800); // Задать период мигания поворотников равен 800 мс.
bum.setTurnSignal(BUM_TURN_AUTO); // Работа поворотников в автоматическом режиме
bum.settingsTurnAuto(BUM_AUTO_ON_2, BUM_AUTO_OFF_ANY, false); // поворотники работают в автоматическом режиме.
} //
//
void loop(){ //
if( bum.getLineSum() > 4 ){ // Если больше 4 датчиков бампера фиксируют линию, то ...
bum.setLineType(BUM_LINE_CHANGE); // Меняем тип линии трассы.
}
if (!bum.getLineSum()){} // Если датчик не фиксирует линии, ничего не делаем
else{
i++; i%=10; j=10; // Определяем значения переменных работы с массивом ошибок.
// Получаем текущую ошибку центрирования линии:
arr_ERR[i] = bum.getErrPID(); // Функция getErrPID() возвращает ошибку от 0 до ±4.5, где 0 - линия по центру, ±4.0 - линия на крайнем датчике, ±4.5 - линия потеряна.
// Вычисляем все составляющие ПИД регулятора:
float pid_P = arr_ERR[i]; // Пропорциональная составляющая «pid_P» представлена величиной текущей ошибки «ARR_ERR[i]».
float pid_I = 0.0f; while(j--){pid_I+=arr_ERR[j-1];} // Интегральная составляющая «pid_I» представлена суммой последних ошибок взятых из массива «arr_ERR».
float pid_D = arr_ERR[i]-arr_ERR[(i+9)%10]; // Дифференциальная составляющая «pid_D» представлена разницей между текущей «ARR_ERR[i]» и предыдущей «arr_ERR[(i+9)%10]» ошибкой.
float PID = pid_P*pid_KP+pid_I*pid_KI+pid_D*pid_KD; // Вычисляем результат ПИД регулирования.
// Устанавливаем скорость вращения колёс:
mot[0].setSpeed( val_Speed + PID , MOT_M_S ); // Устанавливаем скорость левого мотора в м/сек.
mot[1].setSpeed( val_Speed - PID , MOT_M_S ); // Устанавливаем скорость правого мотора в м/сек.
}
}

Обсуждение