Table of Contents

Introducere

Proiectul a presupus realizarea unui robot care să urmărească o linie neagră pe un fundal alb.

Pentru că este foarte ușor de trasat linia de ghidaj acest tip de robot poate fi folosit la automatizări care necesită o flexibilitate foarte mare. Un astfel de sistem ar putea, de exemplu, ghida un robot care depozitează/sortează/găsește diferite pachete într-un depozit aparținând unei companii de distribuție.

Am vrut să realizez un robot compact care să urmărească linia cu viteză mare. Specificațiile inițiale erau:

Descriere generală

Schemă bloc

Controller (ATmega16)

Am folosit placa realizată la prima etapă a proiectului. Schema acesteia se găsește aici. Senzorii și puntea H se conectează pe pinii de extensie ai plăcii.

Senzori

Robotul dispune de 5 senzori asemănători celor folosiți la Pololu 3pi.

Senzorii se bazează pe posibilitatea de a schimba direcția pinilor uC-ului (input/output) și pe durata diferită de descărcare a unui condensator în funcție de nivelul încărcării. Un terminal al condensatorului este conectat în permanență la +5V, iar celălalt este conectat atât pe un pin al ADC-ului cât și la masă printr-un fototranzistor. Pentru a încărca condensatorul se setează pinul ca output și valoarea logică 1 (+5V). Pentru a citi valoarea se setează pinul ca input și se măsoara timpul până când tensiunea măsurată de ADC ajunge la 0. Dacă fototranzistorul este ”deschis” condensatorul se va descărca rapid, fiind conectat ”aproape” la masa. Pentru a controla fototranzistorul suprafața este luminată de un LED. În funcție de câtă lumină se reflectă (mai multă pe suprafețe dechise la culoare - de exemplu alb) fototranzistorul va descărca condensatorul mai repede sau mai lent.

Control motoare (punte H)

Pentru că motoarele au nevoie de un curent relativ mare (~500mA cu sarcină normală; peste 1,5A când e blocat) am folosit o punte H L298N care suportă maxim 2A pe fiecare motor. Pinii ENABLE sunt comandați de uC folosind un semnal PWM pentru a regla turația. Pentru direcție se trimit valori 0/1 pe pinii INPUT.

Alimentare

Se folosesc 2 surse separate de alimentare:

Senzorii și logica punții H sunt alimentate cu +5V de la stabilizatorul care alimentează și uC-ul.

Motoare, șasiu

Nu știu ce caracteristici au motoarele, dar am măsurat un curent de ~500mA cu sarcină normală (inclusiv reductorul și roata) și peste 1,5A când e blocat la o tensiune de 6V. Pentru a crește cuplulul am montat un reductor ~4:1.

Șasiul este construit din plexiglass și placa de sticlotextolid.

Hardware Design

Listă piese

Scheme electrice

Senzor

Punte H

Software Design

Pinii folosiți

Algoritmi și implementare

Urmărirea liniei

Algoritmul de urmărire a liniei este unul banal pentru că nu am avut cu ce să testez altul (s-a ars puntea H; vezi Concluzii). Se iau decizii in funcție de intervalul rezultatului întors de funcția get_position() (mai multe detalii la descrierea acestei funcții).

/* ... */
 
	/* loop forever */
	while (1) {
		position = get_position();
 
		if (position > 1000 && position < 3000) {
			/* centered */
			SET_RIGHT_SPEED(200);
			SET_LEFT_SPEED(200);
		} else if (position < 1000) {
			/* turn left */
			SET_RIGHT_SPEED(200);
			SET_LEFT_SPEED(0);
		} else {
			/* turn right */
			SET_RIGHT_SPEED(0);
			SET_LEFT_SPEED(200);
		}
	}
 
/* ... */

Citirea unui senzor

Pentru a citi un senzor se urmează următoarea procedură:

Îmbunătățiri:

#define MAX_SENSOR_TIME 1500
#define SENSOR_RESET_TIME 50 /* ms */
 
typedef enum {
	SENSOR_LLEFT = PA7,
	SENSOR_LEFT = PA6,
	SENSOR_CENTER = PA5,
	SENSOR_RIGHT = PA4,
	SENSOR_RRIGHT = PA3
} sensor;
 
/* ... */
 
static inline void discharge_sensors()
{
	/* set sensors ports as input (discharge capacitors) */
	DDRA &= ~(_BV(PA7));
	PORTA &= ~(_BV(PA7));
 
	DDRA &= ~(_BV(PA6));
	PORTA &= ~(_BV(PA6));
 
	DDRA &= ~(_BV(PA5));
	PORTA &= ~(_BV(PA5));
 
	DDRA &= ~(_BV(PA4));
	PORTA &= ~(_BV(PA4));
 
	_delay_ms(SENSOR_RESET_TIME);
}
 
uint16_t read_sensor(sensor s)
{
	uint16_t last_time, adc = 0, time = 0, i = 0;
 
	discharge_sensors();
 
	/* +5V to charge the capacitor */
	DDRA |= _BV(s);
	PORTA |= _BV(s);
 
	/* give it time to charge */
	_delay_ms(SENSOR_RESET_TIME);
 
	/* turn port to input */
	DDRA &= ~(_BV(s));
	PORTA &= ~(_BV(s));
 
	/* count discharge time */
	last_time = TCNT1;
	adc = get_adc(s);
	while (adc > 30 && time < MAX_SENSOR_TIME) {
		uint16_t delta_time = TCNT1 - last_time;
 
		last_time += delta_time;
		time += delta_time;
		adc = get_adc(s);
		i++;
	}
 
	return time;
}

Determinarea poziției

Funcția întoarce un întreg care are semnificația descrisă în comentariu. Constantele *_WHITE vor fi determinate rulând funcția calibrate_sensors() (ATENȚIE: trebuie conectată placa la un terminal de serială și trebuie recompilat codul cu noile constante). Nu ar trebui să se modifice cele implicite, dar nu se știe.

/** 
 * sensor max values for WHITE 
 * (use calibration function to get these) 
 */
#define SENSOR_LLEFT_WHITE      500
#define SENSOR_LEFT_WHITE       500
#define SENSOR_CENTER_WHITE     500
#define SENSOR_RIGHT_WHITE      500
#define SENSOR_RRIGHT_WHITE     500
 
/* ... */
 
/**
 * 0 - 4000 interval
 * 0    - 1000 = on the left side
 * 1000 - 3000 = on the line
 * 3000 - 4000 = on the right side
 */
unsigned int get_position()
{
	static unsigned old_pos = 0;
	uint16_t s1, s2, s3, s4, s5;
	int sp, s;
 
	s1 = (read_sensor(SENSOR_LLEFT) > SENSOR_LLEFT_WHITE);
	s2 = (read_sensor(SENSOR_LEFT) > SENSOR_LEFT_WHITE);
	s3 = (read_sensor(SENSOR_CENTER) > SENSOR_CENTER_WHITE );
	s4 = (read_sensor(SENSOR_RIGHT) > SENSOR_RIGHT_WHITE);
	s5 = (read_sensor(SENSOR_RRIGHT) > SENSOR_RRIGHT_WHITE);
 
	sp = (s1 * 0 + s2 * 1000 + s3 * 2000 + s4 * 3000 + s5 * 4000);
	s = (s1 + s2 + s3 + s4 + s5);
 
	if (sp == 0 && s1 == 0) {
		return old_pos;
	} else {
		old_pos = s == 0 ? 0 : (sp / s);
		return old_pos;
	}
}

Controlul punții H

#define SET_DIR_LEFT_FORWARD \
			do { \
				PORTC |= _BV(PC0); \
				PORTC &= ~(_BV(PC1)); \
			} while(0)
#define SET_DIR_LEFT_BACKWARD \
			do { \
				PORTC |= _BV(PC1); \
				PORTC &= ~(_BV(PC0)); \
			} while(0)
#define SET_DIR_RIGHT_FORWARD \
			do { \
				PORTC |= _BV(PC3); \
				PORTC &= ~(_BV(PC2)); \
			} while(0)
#define SET_DIR_RIGHT_BACKWARD \
			do { \
				PORTC |= _BV(PC2); \
				PORTC &= ~(_BV(PC3)); \
			} while(0)
 
#define MOTOR_RIGHT_DISABLE	\
			do { \
				TCCR0 &= ~(_BV(COM01)); \
				PORTD &= ~(_BV(PD7)); \
			} while(0)
#define MOTOR_RIGHT_ENABLE 	TCCR0 |= _BV(COM01)
#define MOTOR_LEFT_DISABLE	\
			do { \
				TCCR2 &= ~(_BV(COM21)); \
				PORTD &= ~(_BV(PD7)); \
			} while(0)
#define MOTOR_LEFT_ENABLE 	TCCR2 |= _BV(COM21)
 
#define SET_RIGHT_SPEED(x)	OCR0 = x
#define SET_LEFT_SPEED(x)	OCR2 = x

Alte detalii

Mediu de dezvoltare: AVR Studio 4

Rezultate Obţinute

Robotul a funcționat ~30s după care s-a ars jumătate din puntea H (sau cel puțin așa cred; PWM-ul și semnalele de comandă ajung la pinii L298N, dar la motor nu mai ajunge nimic).

Nu am reușit să testez dacă urmărește linia, dar am testat individual senzorii și par să funcționeze bine.

Poze

PCB Senzor

vladum-pic-sensor-bottom.jpg vladum-pic-sensor-top.jpg

PCB punte H

vladum-pic-hb-bot.jpg vladum-pic-hb-top.jpg

Șasiu (în timpul construcției)

22052010039.jpg 22052010040.jpg 22052010041.jpg 22052010045.jpg 25052010048.jpg 25052010049.jpg 27052010051.jpg

Robotul la final

08062010070.jpg 08062010072.jpg 08062010075.jpg 08062010077.jpg 08062010079.jpg 08062010085.jpg

Concluzii

Ce nu a mers bine:

Ce a mers bine:

Ce am învățat:

Recomandări:

Download

Codul sursă și proiectele Eagle pentru puntea H și senzori se găsesc în următoarea arhivă: 334cb_dumitrescu_vlad_pm2010_linef.zip

Bibliografie/Resurse

Resurse hardware

ATmega16 Datasheet

L298N Datasheet

OP522 Datasheet

OP251 Datasheet

Resurse software

Pololu 3pi User Guide

AVR Libc Home Page