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:

  • ~400g (am reușit ~500g)
  • 0.5 - 0.9 m/s (cred ca atinge 0.5 m/s pentru că motoarele sunt supradimensionate)

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:

  • baterie de 9V pentru uC, senzori, punte H (VCC)
  • 4 baterii de 1,5V in serie pentru alimentarea de putere (VSS) a punții H

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

  • piesele pentru placa din prima etapă (lista completă aici)
  • control motoare:
    • punte H - L298N
    • 8 x diode - 1N5818
    • 2 x condensatori - 100nF
  • 1 senzor:
    • fototranzistor IR - OP522
    • LED IR - OP521
    • 2 x rezistențe - 220 ohmi
    • condensator - 2.2nF

Scheme electrice

Senzor

Punte H

Software Design

Pinii folosiți

  • PA[3..7] - senzori
  • PB3 - PWM motor dreapta
  • PD7 - PWM motor stânga
  • PC0,1 - control motor stânga (01 = înapoi, 10 = înainte)
  • PC2,3 - control motor stânga (01 = înapoi, 10 = înainte)

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ă:

  • se încarcă condensatorul (pinul corespunzător ca output +5V pentru SENSOR_RESET_TIME ms)
  • se schimbă pinul în input
  • se iau valori de la ADC și se contorizează timpul folosind Timer1

Îmbunătățiri:

  • nu trebuie descărcați toți senzorii la început, doar cel care se citește (discharge_sensors())
  • SENSOR_RESET_TIME este mult prea mare (vroiam să fiu sigur la teste)
#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:

  • am refăcut sașiul pentru că s-a crapat baza
  • s-a ars puntea H
  • a fost complicat de așezat motoarele în poziția bună

Ce a mers bine:

  • senzorii au ieșit din prima
  • motoarele asigură un cuplu și o viteză foarte bună și le alimentez doar de la 4 baterii de 1,5V, totuși sunt mult prea puternice pentru greutatea robotului
  • sașiul este compact (cu un soft bun ar putea să urmărească curbe strânse)

Ce am învățat:

  • să lucrez în Eagle
  • să lipesc SMD-uri
  • mi-am lămurit multe întrebări legate de arhitectura uC-ului și detalii legate de programarea lui

Recomandări:

  • prea complicat de făcut șasiul de la 0, totuși dacă îl faci:
    • plexiglass-ul este foarte casant (s-a crăpat în jurul piulițelor)
    • e nevoie de rulmenți
  • ar fi bună o protecție la supracurent pentru că se blochează roțile des (cel puțin în timpul testelor)
  • puteam să obțin aceleași performanțe cu niște micromotoare Pololu cu reductor 30:1 care ar fi simplificat multe probleme (reglat poziția - sunt dreptunghiulare, centrat axul roților, sunt mai ușoare, curent și tensiune mai mică)

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

Resurse software

pm/prj2010/mcarjaliu/linef.txt · Last modified: 2021/04/14 17:07 (external edit)
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0