Общие сведения
В одном из прошлых уроков мы собирали машинку из ВПХ-конструктора и учили её ездить по линии. Сегодня мы усовершенствуем её и оснастим новым модулем для захвата предметов. Так она сможет перевозить грузы.
Видео
Нам понадобится
- 1х Захват для роботов из ПВХ
- 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 (конструктор ПВХ)
- 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.
Видео
Редактируется...
Сборка корпуса
Сборка корпуса описана в предыдущей статье о ВПХ-машинке. Всё остаётся без изменений, кроме установки большей площадки под Arduino для возможности крепления захвата.


Сборка захвата
Подробный процесс сборки захвата описан в инструкции (в комплекте) и на странице товара.
Для установки сервопривода на угол 90 градусов, используйте следующий скетч:
#include <Servo.h> // Подключение библиотеки
Servo servo; // Создание объекта сервопривода
void setup(){
servo.attach(5); // Сервопривод подключен к 5 пину
servo.write(90); // Установить вал сервопривода в угол 90 градусов
}
void loop(){
}
Подключение

Скетч проекта
// Подключаем библиотеки: //
#include <Wire.h> // Подключаем библиотеку для работы с аппаратной шиной I2C.
#include <iarduino_I2C_Motor.h> // Подключаем библиотеку для работы с мотором I2C-flash.
#include <iarduino_I2C_Bumper.h> // Подключаем библиотеку для работы с бампером I2C-flash.
#include <Servo.h>
// Создаем объекты
iarduino_I2C_Motor mot[2]={0x0A,0x0B}; // Объявляем массив mot состоящий из двух объектов для работы с моторами I2C-flash, указав адреса модулей моторов на шине I2C (0x0A-левый, 0x0B-правый).
iarduino_I2C_Bumper bum (0x0C); // Объявляем объект bum для работы с бампером I2C-flash, указав адрес модуля на шине I2C.
Servo servo;
const float val_Speed = 0.05f; // Скорость движения машины в м/сек.
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; // Переменные для работы с массивом последних ошибок.
/////////////////////НАСТРОЙКИ////////////////////////////
uint8_t servoOpen = 40; // Угол сервопривода, при котором захват будет в открытом положении
uint8_t servoClose = 85; // Угол сервопривода, при котором захват будет в закрытом положении
const float val_Radius = 17.0f; // Радиуса колеса в мм.
#define backTime 1500 // Время отъезда назад, мс
//////////////////////////////////////////////////////////
void setup(){
Serial.begin(9600); // Открываем связать по Serial порту на скорости 9600 бод
delay(500); // Ждём завершение переходных процессов связанных с подачей питания.
mot[0].begin(); // Инициируем работу с левым мотором I2C-flash.
mot[1].begin(); // Инициируем работу с правым мотором I2C-flash.
bum.begin(); // Инициируем работу с бампером I2C-flash.
servo.attach(5); // Сервопривод подключен к 5 пину
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, то ротор остановленного мотора можно вращать).
}
void loop(){
servo.write(servoOpen); // Открываем захват
while(bum.getLineDigital(BUM_LINE_ALL)){ // Пока хотя бы один датчик находится над линией
direct(); // Двигаемся вперёд
}
stay();
servo.write(servoClose); // Закрываем захват
delay(500); // Задержка
back(1500); // Отъезжаем назад 1с
rotation(); // Разворот
while(bum.getLineDigital(BUM_LINE_ALL)){ // Пока хотя бы один датчик находится над линией
direct(); // Двигаемся вперёд
}
stay(); // После - останавливаемся
drop(); // Отпускаем груз
back(1500); // Отъезжаем назад 1с
rotation(); // Разворот
}
void stay(){ // Функция остановки
mot[0].setSpeed(0, MOT_M_S); // Выключаем оба двигателя
mot[1].setSpeed(0, MOT_M_S);
}
void rotation(){ // Функция разворота
mot[0].setSpeed(0.07, MOT_M_S); // Задаём скорости моторам
mot[1].setSpeed(-0.07, MOT_M_S);
delay(2000); // Задержка, чтобы машинка успела повернуться
while(!bum.getLineDigital( 5 )){ // Поворот пока под 5 датчиком линии нет линии
mot[0].setSpeed(0.07, MOT_M_S); // Задаём скорости моторам
mot[1].setSpeed(-0.07, MOT_M_S);
}
stay(); // Остановка
}
void drop(){ // Функция плавной постановки груза
for (uint8_t i=0; i<5; i++){ // Повторяем 5 раз
servo.write(servoClose-i); // Открываем захват на угол, с каждым разом всё больший и больший
delay(200); // Задержка
servo.write(servoClose-15); // Приоткрываем захват
delay(5); // Небольшая задержка, чтоб груз успел опуститься вниз
}
servo.write(servoOpen); // Полностью открываем захват
delay(200); // Задержка
}
void back(uint32_t timer){ // Функция отъезда назад
uint32_t timeStart = millis(); // Запоминаем время старта отъезда
while(timeStart + timer > millis()){// Пока от момента старта прошло меньше времени чем timer
mot[0].setSpeed(-0.07, MOT_M_S); // Задаем моторам скорость
mot[1].setSpeed(-0.07, MOT_M_S);
}
stay(); // Остановка
}
void direct(){ // Функция движения вперёд
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 ); // Устанавливаем скорость правого мотора в м/сек.
}
Алгоритм работы
- Открываем захват;
- Едем до конца линии;
- Закрываем захват;
- Отъезжаем назад;
- Разворачиваемся;
- Едем до конца линии;
- Аккуратно ставим предмет;
- Отъезжаем назад;
- Разворачиваемся.
Движение вперед до конца линии производится с помощью постоянного отслеживания сигналов с датчика линии. Как только линия пропадает — прекращаем движение.
Разворот производится тоже благодаря сигналам с датчиков — разворачиваемся до тех пор, пока линия не окажется под центральным.
Движение вперёд происходит по уже рассмотренному нами принципу ПИД-регулирования в статье Установка бампера с 9 датчиками линии на робота "Малыш".
Для того, чтобы аккуратно поставить предмет, воспользуемся возможностью быстрого открытия и закрытия захвата. Будем приоткрывать его с каждым разом на чуть бо́льший угол, таким образом, предмет не сразу упадёт вниз, а более-менее плавно опустится на поверхность.
Ссылки
- Захват для роботов из ПВХ
- Мотор-редуктор с энкодером N20-100rpm-12V с управляющим контроллером
- Бампер с 9 датчиками линий с шагом 14мм.
- Источник питания 5В, 5-12В (Li-ion 14500)
- Калибровка бампера
- Библиотеки:

Обсуждение