Продолжаю серию публикаций о создании простого колесного робота на микроконтроллере ATmega16A.
Во второй части моей публикации я опишу процесс создания и сборки своего робота. Начнем с изготовления печатной платы и закончим видео первых шагов (правильней сказать — прокручивания колес) нашего устройства. Также уделю внимание первому опыту программирования под PC в Qt, а именно созданию программы управления и обмена данными с роботом по Bluetooth.
Если хотите, можете ознакомится с первой публикацией и узнать с чего все началось, ну а всех остальных прошу под кат.
Прошлая часть закончилась на том, что перед нами красовалась готовая разводка печатной платы, а значит, пора творить и переносить нашу задумку из электронной версии в реальный осязаемый мир, разве не это желание теплится в голове у каждого радиолюбителя, инженера, изобретателя?! У меня на руках был только 1мм текстолит, так что я недолго думая начал процесс переноса на плату рисунка дорожек стандартным ЛУТом.
Извиняюсь за качество фотографий, но телефон у меня дешевый…
Следующим шагом было травление! Опробовал для себя новый способ травления с помощью перекиси водорода и лимонной кислоты, способ конечно безопасный и очень дешевый, но показался мне слишком чувствительным к пропорциям компонентов. После часа колдовских манипуляций над тарой с раствором процесс пошел с достойной силой (стоит сказать, что можно было изменить шаблон платы и тогда бы все вытравилось заметно быстрее).
80-90% дорожек на плате получились хорошо, но т.к. в основном дорожки были 0.2мм, попались протравы, которые я устранил с помощью припоя и тонких проводников, благодаря им же сделал переходные отверстия. Вообще весь процесс пайки занял 3 вечера.
Моторы пришлось перенести на нижнюю часть платы, так как понял, что мне нужен максимальный зазор между платой и поверхностью по которой нужно будет ездить, но и все равно робот получился с низкой посадкой.
И раз уж мы вспомнили про моторы, то вспомним и об идее с энкодером, так как магниты, которые я заказывал, были в диаметре буквально на пол миллиметра меньше пазов в колесе, то их нужно было как-то там зафиксировать, а как мы знаем все гениальное просто — я просто взял и загнал их туда, залив при этом из клеевого пистолета, не очень эстетично, зато быстро и практично. Главное не перепутать полярность, а то будут косяки с точностью, а она и так небольшая — 12 магнитов на колесо, т.е. 6 срабатываний за поворот, а с диаметром колес в 43мм получаем примерно 22мм пройденного колесом пути за одно срабатывание.
На плате, в свою очередь, с небольшим разносом по высоте поставил датчики Холла, что бы понимать в какую сторону движемся.
Для крепления аккумулятора, сервы и УЗ дальномера были распечатаны на 3D принтере простейшие детали.
Держатель аккумулятора.
Держатель сервы.
Держатель УЗ дальномера.
Немного доводки деталей напильником и все встало на свои места, в итоге робот получился вот таким.
И да, это просто бусина, бусина на толстой проволоке, и это пока единственная нерешенная «проблема» конструкции. На самом деле не брался за решение вопроса о третьем колесе, ибо загорелся подрыгать колесами, повертеть сервой и т.п.
Из за использования 1мм текстолита робот кажется хлюпким, но так как масса у нас небольшая это никак не влияет на его прочность и маневренность.
Ну и первым делом захотелось прокатиться по линии, так сказать первый опыт с автоматизированным управлением.
Сразу скажу, что я не программист по профессии и это наверно моя самая слабая точка в хобби, так что прошу не сильно критиковать код, в любом случае я уверен, что нужно будет переписывать всю программу в будущем.
Была написана тестовая программа, которая реализовывала PD алгоритм. Она состояла из обработчика прерываний АЦП и собственно основного цикла работы с данными.
#define LAST_ADC_INPUT 7
unsigned int real_adc[8]={0,0,0,0,0,0,0,0};
unsigned char sample_adc;
volatile unsigned char adc_ready = 0;
unsigned char leds[8]={0x21,0x41,0x61,0x63,0x23,0x43};
unsigned char adc_inputs[8]={0,1,2,4,6,7,3,5};
interrupt [ADC_INT] void adc_isr(void) //////////////ADC_INT
{
static unsigned char input_index=0;
if (adc_ready == 0){
if (sample_adc == 0) {
real_adc[input_index]=(signed int)(ADCW);
if (input_index < LAST_ADC_INPUT-FIRST_ADC_INPUT){
input_index++;
} else {
input_index = 0;
PORTB=leds[input_index];
sample_adc = 1;
}
ADMUX=(ADC_VREF_TYPE & 0xff)+adc_inputs[(FIRST_ADC_INPUT+input_index)];
ADCSRA|=0x40;
} else {
if (adc_ready==0) {
if (ADCW > real_adc[input_index]){
real_adc[input_index]=(signed int)(ADCW);
} else {
real_adc[input_index]=(signed int)(real_adc[input_index]);
}
if (input_index < (LAST_ADC_INPUT-FIRST_ADC_INPUT)){
input_index++;
PORTB=leds[input_index];
} else {
input_index = 0;
adc_ready = 1;
PORTB&=~(1<<0);
}
ADMUX=(ADC_VREF_TYPE & 0xff)+adc_inputs[(FIRST_ADC_INPUT+input_index)];
}
if (adc_ready == 0){
ADCSRA|=0x40;
}
}
}
}
adc_l = (real_adc[1]+real_adc[2]);
adc_r = (real_adc[3]+real_adc[4]);
error = (adc_l-adc_r);
delta_error = error — old_error;
//sum_error += error;
PID = Kp*error + Kd*delta_error + Ki*sum_error;
old_error = error;
if (PID > 0) {
pwr_l += (signed int)PID;
pwr_r -= (signed int)PID;
} else {
pwr_l += (signed int)PID;
pwr_r -= (signed int)PID;
}
for(i=0; i < (LAST_ADC_INPUT-FIRST_ADC_INPUT)+1; i++){
real_adc[i]=0;
}
ADCSRA |= 0x40;
adc_ready = 0;
sample_adc = 0;
}
Как же я был рад видеть, что робот выполняет возложенную на него функцию, не так конечно как на крутых видеороликах с абсолютно плавными и быстрыми linefolower'ами, но для меня даже такой результат был достижением.
После этого я немного успокоился с частью программирования самого микроконтроллера и понял, что в будущем нужно будет реализовать программу управления роботом с ПК и принялся за изучение Qt, а так как последнюю программу для ПК я писал только в университете (на паскале) и это была какая-то стандартная лабораторная по информатике, то мои знания были в районе нуля.
Программа должна была переключать режимы работы робота (следование по линии, управление с клавиатуры, автономный режим) и выдавать(задавать) данные со всех интересующих датчиков (сервы, датчики линии, УЗ дальномер, моторы, энкодеры).
После пары недель, была написана первая версия программы контрольной панели управления роботом. Очень понравилась работа с GUI в Qt — удобно.
Код программы лучше не буду вставлять, дабы меня не забанили за издевательство над Си. Вкратце скажу, что программа просто по сигналу наличия данных в QSerialPort берет их и рассовывает по лэйблам и виджетам, в ответ по таймеру, если нужно, отправляет задающие данные, такие как положение сервы и скорость моторов. Зеленое поле в программе это в мечтах — будущая карта локации перед роботом.
Программа принимает все доступные данные и временно выступает как дебаггер. Пока не реализовывал управление с клавиатуры, да и автономный режим очень сырой и робот пока просто видя препятствие, разворачивается на заданный угол и пытается уехать в новом направлении. В общем как я и говорил, проект в целом пока сыроват в программной части.
В будущем хочется реализовать грандиозные планы, которые немного стопорятся пониманием того что нужно переделывать обе программы на МК и ПК полностью, так как я сделал большую ошибку и начал писать обе программы как раздутый слоеный пирог где один тестовый модуль соседствует с другим, а не как строгую структурированную систему. Но главное не опускать руки и творить дальше, пока то, что ты задумал в голове не будет реализовано в материальном мире ведь в этом и есть смысл хобби.
П.С. В будущем постараюсь написать еще одну часть публикации, так сказать по мере продвижений в программной части.
Автор: AlexBolm