-         
Seite 2 von 14 ErsteErste 123412 ... LetzteLetzte
Ergebnis 11 bis 20 von 137

Thema: Minimallösung: Kamera für den RP6

  1. #11
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    57
    Beiträge
    5.796
    Blog-Einträge
    8
    Anzeige

    Praxistest und DIY Projekte
    Hallo

    Es wächst und gedeiht, hier mein einfacher Linienfolger:

    Code:
    #include "RP6RobotBaseLib.h"
    
    #define mpower 50
    #define spur 20
    
    // Achtung! Die PWM-Werte werden hier OHNE Rampe verändert!
    void setMotorPWM(uint8_t power_links, uint8_t power_rechts)
    {
    extern uint8_t mleft_ptmp, mright_ptmp;
    
    	if(power_links > 210) power_links = 210;
    	if(power_rechts > 210) power_rechts = 210;
    	mleft_power=mleft_ptmp=power_links;
    	mright_power=mright_ptmp=power_rechts;
    
    	OCR1BL = power_links;
    	OCR1AL = power_rechts;
    
    	if(power_links || power_rechts)
    		TCCR1A = (1 << WGM11) | (1 << COM1A1) | (1 << COM1B1);
    	else
    		TCCR1A = 0;
    }
    
    uint8_t get_line(uint16_t delay, uint8_t zeilen)
    {
    	uint8_t pixel[256],*pixelzeiger;
    	uint8_t i, step, lines, h_step, h_sync, h_delay;
    	uint16_t gamma[16],g_hell, g_dunkel, g_sprung;
    	uint16_t strich_links, strich_rechts, strich_mitte, strich_breite;
    
    	step=(260-35)/zeilen; // 30-260 sind die sichtbare Zeilen
    	lines=zeilen;   // Anzahl der einzulesenden Zeilen
    	pixelzeiger=&pixel[0]; // Zeiger auf Start Pixelspeicher
    
    	cli();
    	do // vsync abwarten
    	{
    		h_sync=0; while (ADCH > 20); while (ADCH < 30) h_sync++;
    	} while (h_sync < 40);
    
    	h_step=35;
    	while (h_step) // 30 Zeilen Austastzeit überlesen
    	{
    		while (ADCH > 20); while (ADCH < 30); h_step--;
    	}
    
     	while (lines--)
    	{
    		h_step=step;
    		while (h_step) // auf die nächste gültige Zeile warten
    		{
    			while (ADCH > 20); while (ADCH < 30); h_step--;
    		}
    		h_delay=delay;
    		while (h_delay--); // auf richtiges Pixel warten
    		*pixelzeiger=ADCH; // letzten ADC-Wert auslesen und wegwerfen
    		*pixelzeiger++=ADCH;  // aktuellsten ADC-Werte speichern
    	}
    	sei();
    
    	for (i=0; i<16; gamma[i++]=0); // 16 gammawerte zählen
    	pixelzeiger=&pixel[0];
    	for (lines=0; lines < zeilen; lines++) gamma[*pixelzeiger++ >> 4]++;
    
    	g_sprung=5;
    	g_dunkel=0;
     	for (i=1; i<16; i++)
    	{
    	   if (gamma[i] > gamma[i-1])
    			if ((gamma[i] - gamma[i-1]) > g_sprung)
    				if (!g_dunkel) g_dunkel=i;
    	}
    	g_hell=0;
     	for (i=14; i; i--)
    	{
    	   if (gamma[i] > gamma[i+1])
    			if ((gamma[i] - gamma[i+1]) > g_sprung)
    				if (!g_hell) g_hell=i;
    	}
    
    	strich_links=0;
    	strich_rechts=0;
    	for (i=0; i<zeilen; i++)
    	{
    		if (((pixel[i] >> 4) == g_dunkel) && ((g_hell-g_dunkel)>1))
    		{
    			if (!strich_links) strich_links=i; // wenn linker Rand gefunden,
    				else strich_rechts=i; // weitersuchen bis rechter Rand
    		}
    	}
    	strich_mitte=(strich_links+strich_rechts)/2;
    	strich_breite=strich_rechts-strich_links;
    	return(strich_mitte);
    }
    
    int main(void)
    {
    	uint8_t i;
    	uint8_t pow_l, pow_r, dir_l, dir_r;
    	uint8_t strich, keine_strich, out_l, out_r, abweich;
    
    	initRobotBase();
    	extIntOFF(); // schaltet den E_INT1-Port auf Eingang für den ADC
    	//powerON();
    // ADC interne Referenz 2,56V, Ergebniss linksbündig, Kanal ADC4 (E_INT1)
    	ADMUX = (1<<REFS1) | (1<<REFS0)  | (1<<ADLAR) | 4;
    // setzte free running triggern
    	SFIOR = (0<<ADTS2) | (0<<ADTS1) | (0<<ADTS0);
    // kein interupt, Wandler einschalten, prescaller /2
    	ADCSRA = (0<<ADIE) | (1<<ADEN) | (0<<ADPS2) | (0<<ADPS1)  | (1<<ADPS0);
    // Autotriggern bedeutet jetzt free running aktivieren, altes Flag löschen
    	ADCSRA |= (1<<ADATE) | (1<<ADIF);
    // Initialisierung starten
    	ADCSRA |= (1<<ADSC);
    // und noch die wohl eher unnötige Initiallesung
    	while (!(ADCSRA & (1<<ADIF)));
    	ADCSRA |= (1<<ADIF);
    	
    	out_l=true;
    	out_r=false;
    	keine_strich=0;
    	
    	dir_l=dir_r=BWD;
    	setMotorDir(dir_l,dir_r);
    	pow_l=pow_r=mpower;
    	setMotorPWM(pow_l/2,pow_r/2);
    	mSleep(mpower);
    	setMotorPWM(pow_l,pow_r);
    
    	do
    	{
    		strich=get_line(50,48);
    		abweich=2*abs(spur-strich);
    
    	if (strich)
    	{
    	   out_l=out_r=false;
    		if (strich < (spur-15)) out_l=true;
    		if (strich > (spur+15)) out_r=true;
    
    		if (strich < spur)
    		{
    			pow_l=mpower+abweich;
    			pow_r=mpower-abweich;
    		}
    		else
    		{
    			pow_l=mpower-abweich;
    			pow_r=mpower+abweich;
    		}
    	}
    	else
    	{
    	   if (out_l) {pow_l=mpower; pow_r=0;}
    	   if (out_r) {pow_l=0; pow_r=mpower;}
    	}
    
    		setMotorPWM(pow_l,pow_r);
    
    		writeString_P("\n\r");
    		writeInteger(strich, DEC);
    		writeString_P("-");
    		writeInteger(abweich, DEC);
    		mSleep(100);
    	} while (1);
    	return(0);
    }
    und ein paar Videos der Tests. Zuerst die RN-Teststrecke:


    (http://www.youtube.com/watch?v=txMYl7aKTBA)

    Dann ein USB-Kabel, das am Boden liegt:

    http://www.youtube.com/watch?v=NFQz49vDDfg

    Selbes Kabel auf einem anderen Untergrund:

    http://www.youtube.com/watch?v=paXpHySWn7g
    http://www.youtube.com/watch?v=qVBPT5tGpjg

    Ganz nett, wenn man anschaut, wie es mit der KI dieses Linienfolgers bestellt ist. Zudem scanne ich den Strich nur von einer Seite...


    Ich kapier das nicht richtig.
    kannst du den code mal so abspecken das er das bild in ein array schreibt?
    Euch ist hoffentlich klar, dass dies alles hier keine "Ready to use"-Lösung ist. Die Kamera ist nur das sehr einfache Auge. Wie der Roboter das Gesehene interpetiert, ist eure Aufgabe:

    Code:
    #include "RP6RobotBaseLib.h"
    
    uint8_t bildspeicher[1024], *bildzeiger; // 32*32=1KB * 8Bit Bildspeicher bereitstellen
    
    void bild_einlesen(void)
    {
    	uint8_t pixel[32],*pixelzeiger;
    	uint8_t i, zeilen, step, lines, rows, h_step, h_sync, h_delay;
    
    	zeilen=32; // Das fertige Bild soll 32 Zeilen haben
    	step=7; // sichtbares TV-Bild ist ca. 30-260=230/32 ergibt Zeilensprung=7
    	rows=0; // Anzahl der Spalten (32x32, rechengünstig,aber verzerrt)
    
    	do
    	{
    		lines=zeilen; // Anzahl der einzulesenden Zeilen
    		pixelzeiger=&pixel[0]; // Zeiger auf Start Pixelspeicher
    		cli();
    		 // h_sync abwarten (syncsignal länger 40 bedeutet Seitenanfang)
    		 do { h_sync=0; while (ADCH > 20); while (ADCH < 30) h_sync++; } while (h_sync < 40);
    
    		// 30-35 Zeilen Austastzeit überlesen (der Rest des hsyncs+nicht darstellbare BTX-Infos)
    		h_step=35; while (h_step) { while (ADCH > 20); while (ADCH < 30); h_step--; }
    	
    		// Der Lesecursor befindet sich jetzt oben links im TV-Bild
    		// ab hier werden in step-Sprüngen in allen Zeilen jeweils das Pixel eingelesen,
    		// das sich im zeitlichen h_delay-Abstand vom linken TV-Bildrand befinden
    		// (= eine TV-Bildspalte)
    
    	 	while (lines--)
    		{
    			// auf die nächste gültige Zeile warten
    			h_step=step; while (h_step) { while (ADCH > 20); while (ADCH < 30); h_step--; }
    
    			// mit h_delay steuern wir nun den Pixel an
    			// Nach dem sync fängt das Bild etwas verzögert an (schwarzschulter), bei mir 20
    			// bei ca. 150 beginnt die 2.Schwarzschulter. Bei 150-20 möglichen Bildpunkten
    			// ergibt sich eine maximale Auflösung von 128 horizontal. Zusammen mit der
    			// vertikalen Auflösung von 230 kämen wir dann bei einem 8MHz-ATMega auf stolze
    			// 128*230 Bildpunkte.
    			h_delay=20+4*rows; while (h_delay--);
    
    			*pixelzeiger=ADCH; // letzten ADC-Wert auslesen und wegwerfen
    			*pixelzeiger++=ADCH;  // aktuellsten ADC-Werte speichern
    		}
    		sei();
    		
    		pixelzeiger=&pixel[0];
    		bildzeiger=&bildspeicher[32*rows];
    		for (i=0; i<32; i++) *bildzeiger++ = *pixelzeiger++;
    		
    	}while (rows++ <zeilen);
    }
    
    int main(void)
    {
    	uint16_t i, j;
    
    	initRobotBase();
    	extIntOFF(); // schaltet den E_INT1-Port auf Eingang für den ADC
    	//powerON();
    // ADC interne Referenz 2,56V, Ergebniss linksbündig, Kanal ADC4 (E_INT1)
    	ADMUX = (1<<REFS1) | (1<<REFS0)  | (1<<ADLAR) | 4;
    // setzte free running triggern
    	SFIOR = (0<<ADTS2) | (0<<ADTS1) | (0<<ADTS0);
    // kein interupt, Wandler einschalten, prescaller /2
    	ADCSRA = (0<<ADIE) | (1<<ADEN) | (0<<ADPS2) | (0<<ADPS1)  | (1<<ADPS0);
    // Autotriggern bedeutet jetzt free running aktivieren, altes Flag löschen
    	ADCSRA |= (1<<ADATE) | (1<<ADIF);
    // Initialisierung starten
    	ADCSRA |= (1<<ADSC);
    // und noch die wohl eher unnötige Initiallesung
    	while (!(ADCSRA & (1<<ADIF)));
    	ADCSRA |= (1<<ADIF);
    
    	while(1)
    	{
    		bild_einlesen();
    
    		for (i=0; i<32; i++)
    		{
    			for (j=0; j<32; j++)
    			{
    				if (bildspeicher[j+32*i] >90) writeString_P("*");
    		   		else writeString_P(" ");
    			}
    			writeInteger(i,DEC);
    	   	writeString_P("\n\r");
    		}
    	mSleep(200);
    }
    
    	return(0);
    }
    Damit macht man z.B. 32x32-"Bilder", wenn das jemand braucht. Die Ausgabe dazu findet ihr im Anhang.
    Ein kleines Filmchen zeigt, wie es funktioniert:


    (http://www.youtube.com/watch?v=5PCvCAti1RY)

    Hier wird übrigens nur per Wert > 90 ein Punkt gesetzt oder nicht. Quasi nur die Rohdaten ohne Bearbeitung.

    Gruß

    mic
    Angehängte Dateien Angehängte Dateien

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  2. #12
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    06.08.2004
    Beiträge
    378
    Hi Radbruch,

    das sieht ja alles recht gut aus...
    Aber wenn ich in sDatenblatt schaue, sehe ich das der Atmega32
    für eine ADC Wandlung im bestenfall 13µS benötigt. 200Khz ist ja nur die Taktfrequenz des ADC Wandlers! Normale Video ADC Arbeiten mit Taktfrequenzen von 35Mhz...

    Wenn also unser ADC 13µS benötigt verpasst er auf jedenfall einige H_Syncs. bis er loslegt. Das ende findet er ja dann auch nicht immer...

    Erklärungsnotstand

  3. #13
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    57
    Beiträge
    5.796
    Blog-Einträge
    8
    Hallo Sommer

    Aber wenn ich ins Datenblatt schaue, sehe ich das der Atmega32
    für eine ADC Wandlung im bestenfall 13µS benötigt.
    Darüber habe ich mich auch schon gewundert. Allerdings steht das nur oben im Datenblatt. Bei der Beschreibung des ADCs wird dann immer nur von 13,5 "Taktzyklen" gesprochen. Und die verwendet er auch, ich habe keinen Weg gefunden, um das Samplen nach weniger als 10Bit abzubrechen. Mit der Zyklenrechnerei hab ich's nicht so, aber der Ansatz wäre wohl (Bei 8MHZ ATMega und perscaler /2): 1/4000000Hz Clock*14 Zyklen oder 3,5us. Dann würde ich das 4,7us-hsync mindestens einmal treffen. Wenn man genau hinschaut, sieht man im Beitrag oben im Diagramm die Schwarzschultern und die hsyncs. Allerdings ist das blöderweise spiegelverkehrt, weil ich die Werte rückwärts ausgegeben hatte. Hier ein Code der 256 Werte in Zeile 100 einliest und tabellengerecht an den PC sendet:

    Code:
    // Liest ab der 100. Zeile 256 Werte am Stück ein
    // und sendet die Daten als Tabellenvorlage zum PC.
    
    #include "RP6RobotBaseLib.h"
    
    int main(void)
    {
    uint8_t pixel[256],*pixelzeiger, *endezeiger;
    uint8_t vsync, lines;
    
    	initRobotBase();
    	extIntOFF();
    	//powerON();
    // interne Referenz 2,56V, linksbündig, Kanal ADC4
    	ADMUX = (1<<REFS1) | (1<<REFS0)  | (1<<ADLAR) | 4;
    // free running triggern
    	SFIOR = (0<<ADTS2) | (0<<ADTS1) | (0<<ADTS0);
    // kein interupt, einschalten, prescaller /2
    	ADCSRA = (0<<ADIE) | (1<<ADEN) | (0<<ADPS2) | (0<<ADPS1)  | (1<<ADPS0);
    // free running aktivieren, altes Flag löschen
    	ADCSRA |= (1<<ADATE) | (1<<ADIF);
    // Initialisierung starten
    	ADCSRA |= (1<<ADSC);
    
    	while (!(ADCSRA & (1<<ADIF)));
    	ADCSRA |= (1<<ADIF);
    
    	pixelzeiger=&pixel[0];
    	endezeiger=&pixel[255];
    	lines=100;
    	cli();
    
    	do // vsync abwarten
    	{
    		vsync=0;
    		while (ADCH > 20);
    		while (ADCH < 30) vsync++;
    	}while (vsync < 40);
    	
    	while (lines) // zeile abwarten
    	{
    		while (ADCH > 20);
    		while (ADCH < 30);
    		lines--;
    	}
    
    // 256 Werte am Stück einlesen und als Basis für ein Diagramm senden
    	do *pixelzeiger=ADCH; while (pixelzeiger++ < endezeiger);
    	sei();
    
    	writeString("------------------------\n\r");
     	lines=0;
     	do
    	{
    		writeInteger(lines, DEC);
    		writeString_P("; ");
    		writeInteger(pixel[lines], DEC);
    		writeString_P("; ");
    	   writeString("\n\r");
    	}while (++lines);
    
    	while (1)
    	return(0);
    }
    Damit schafft man mit einem 8MHz-ATMega "nur" knapp 50 Lesungen pro Zeile, weil sich die Formulierung des Einlesebefehls und damit der Code geändert hat. Im Anhang die Ausgabe des Programms mit einem schwarzen Strich vor der Kameralinse.

    Wie gut sich meine Kamera an das BAS-Timeing hält, weiß ich allerdings nicht. Ich sollte mal eine alternative Signalquelle testen (oder Abwarten, bis es einer von euch nachgebaut hat).

    Das zeilenweise Einlesen des TV-Signals ist inzwischen "Schnee von gestern". Meine aktuellen Programme lesen das Bild spaltenweise ein, mit je einem h_delay zwischen hsync und Lesen des Pixels pro Zeile. Damit schaffe ich nun, wie im Quellcode der Smilieerkennung zu lesen ist, locker 150 unterschiedliche Lesepositionen innerhalb einer BAS-Zeile.

    Gruß

    mic

    [Edit]
    Wenn ich das so lese, stelle ich auch fest, dass da was nicht stimmen kann. Wenn eine Zeile (laut Wiki) 64us dauert, kann ich mit 3,5us pro Lesung niemals auf 50 oder gar 60 Werte/Zeile kommen. Da passt etwas noch nicht, aber trotzdem funktioniert es irgendwie.
    Angehängte Dateien Angehängte Dateien

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  4. #14
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    57
    Beiträge
    5.796
    Blog-Einträge
    8
    Hallo

    Das scheint eine eierlegende Wollmilchsau zu sein. Auch die Abstandsmessung (im Ansatz nach der hier vorgeschlagenen Laserscanner-Methode) funktioniert. Natürlich muss man sich das mit viel massgeschneidertem Code erkaufen, aber Entwicklungszeit kostet uns ja nichts und macht uns schlauer.

    Auf der Basis des Smiliebildes ermittelt dieses Programm die Helligkeitsverteilung im 32x32-Bild (nur die oberen 4 Bit des Wertes) und sucht dann im Bild den Bereich mit der größten Helligkeit. Die erste Zeile mit mehr als 10 heller-als-Durchschnitt Punkten ist dann der Abstand (wir wollen es ja nicht zu kompliziert machen). Das ist der von meinen LEDs (ich habe leider keinen Laserpointer) erzeugte Lichtfleck. In der Ausgabe zum PC steht die Zeilennummer und die Nummer der ersten Zeile mit geforderter Helligkeit zur Diagnose und Einstellung:

    Code:
    #include "RP6RobotBaseLib.h"
    
    #define power 50
    #define rampe 50
    
    uint8_t bildspeicher[1024], *bildzeiger; // 32*32=1KB * 8Bit Bildspeicher bereitstellen
    
    // Achtung! Die PWM-Werte werden hier OHNE Rampe verändert!
    void setMotorPWM(uint8_t power_links, uint8_t power_rechts)
    {
    extern uint8_t mleft_ptmp, mright_ptmp;
    
    	if(power_links > 210) power_links = 210;
    	if(power_rechts > 210) power_rechts = 210;
    	mleft_power=mleft_ptmp=power_links;
    	mright_power=mright_ptmp=power_rechts;
    
    	OCR1BL = power_links;
    	OCR1AL = power_rechts;
    
    	if(power_links || power_rechts)
    		TCCR1A = (1 << WGM11) | (1 << COM1A1) | (1 << COM1B1);
    	else
    		TCCR1A = 0;
    }
    
    void bild_einlesen(void)
    {
    	uint8_t pixel[32],*pixelzeiger;
    	uint8_t i, zeilen, step, lines, rows, h_step, h_sync, h_delay;
    
    	zeilen=32; // Das fertige Bild soll 32 Zeilen haben
    	step=7; // sichtbares TV-Bild ist ca. 30-260=230/32 ergibt Zeilensprung=7
    	rows=0; // Anzahl der Spalten (32x32, rechengünstig,aber verzerrt)
    
    	do
    	{
    		lines=zeilen; // Anzahl der einzulesenden Zeilen
    		pixelzeiger=&pixel[0]; // Zeiger auf Start Pixelspeicher
    		cli();
    		do { h_sync=0; while (ADCH > 20); while (ADCH < 30) h_sync++; } while (h_sync < 40);
    		h_step=35; while (h_step) { while (ADCH > 20); while (ADCH < 30); h_step--; }
    
    	 	while (lines--)
    		{
    			// auf die nächste gültige Zeile warten
    			h_step=step; while (h_step) { while (ADCH > 20); while (ADCH < 30); h_step--; }
    			h_delay=20+4*rows; while (h_delay--);
    
    			*pixelzeiger=ADCH; // letzten ADC-Wert auslesen und wegwerfen
    			*pixelzeiger++=ADCH;  // aktuellsten ADC-Werte speichern
    		}
    		sei();
    		
    		pixelzeiger=&pixel[0];
    		bildzeiger=&bildspeicher[32*rows];
    		for (i=0; i<32; i++) *bildzeiger++ = *pixelzeiger++;
    		
    	}while (rows++ <zeilen);
    }
    
    int main(void)
    {
    	uint16_t i, j;
    	uint16_t gamma[16],g_hell, g_dunkel, g_hell_count;
    	uint8_t lichtpunkt, abstand;
    	uint8_t dir_l, dir_r, pow_l, pow_r;
    
    	initRobotBase();
    	extIntOFF(); // schaltet den E_INT1-Port auf Eingang für den ADC
    	//powerON();
    // ADC interne Referenz 2,56V, Ergebniss linksbündig, Kanal ADC4 (E_INT1)
    	ADMUX = (1<<REFS1) | (1<<REFS0)  | (1<<ADLAR) | 4;
    // setzte free running triggern
    	SFIOR = (0<<ADTS2) | (0<<ADTS1) | (0<<ADTS0);
    // kein interupt, Wandler einschalten, prescaller /2
    	ADCSRA = (0<<ADIE) | (1<<ADEN) | (0<<ADPS2) | (0<<ADPS1)  | (1<<ADPS0);
    // Autotriggern bedeutet jetzt free running aktivieren, altes Flag löschen
    	ADCSRA |= (1<<ADATE) | (1<<ADIF);
    // Initialisierung starten
    	ADCSRA |= (1<<ADSC);
    // und noch die wohl eher unnötige Initiallesung
    	while (!(ADCSRA & (1<<ADIF)));
    	ADCSRA |= (1<<ADIF);
    	
    	dir_l=dir_r=FWD;
    	setMotorDir(dir_l,dir_r);
    	pow_l=pow_r=power;
    	setMotorPWM(pow_l,pow_r); mSleep(rampe);
    
    	while(1)
    	{
    		bild_einlesen();
    		
    		for (i=0; i<16; gamma[i++]=0); // 16 gammawerte zählen
    		bildzeiger=&bildspeicher[0];
    		for (i=0; i < 1024; i++) gamma[*bildzeiger++ >> 4]++;
    
    	g_hell=g_dunkel=0;
     	for (i=1; i<16; i++)
    	{
    	   if (gamma[i] > g_hell) { g_dunkel=g_hell; g_hell=gamma[i]; }
    	      else if (gamma[i] > g_dunkel) g_dunkel=gamma[i];
    	}
    	
    	if (g_hell < g_dunkel)
    	{
    		i=g_hell;
    		g_hell=g_dunkel;
    		g_dunkel=i;
    	}
    
     	for (i=1; i<16; i++)
    	{
     	   if (g_dunkel == gamma[i]) g_dunkel=i;
     	   if (g_hell == gamma[i]) g_hell=i;
    	}
    
    
    		for (i=0; i<16; i++)
    		{
    		   writeInteger(gamma[i], DEC);
    		   if (gamma[i] == g_hell) writeString_P("H");
    		      else if (gamma[i] == g_dunkel) writeString_P("D");
    		         else writeString_P(" ");
    		}
    		writeString_P("\n\r");
    		writeInteger(g_dunkel, DEC);
    		writeString_P("-");
    		writeInteger(g_hell, DEC);
    		writeString_P("\n\r");
    
    	   g_hell_count=0;
    	   lichtpunkt=0;
    		for (i=0; i<32; i++)
    		{
    			for (j=0; j<32; j++)
    			{
    				//if (bildspeicher[j+32*i]>>4 == g_dunkel) writeString_P(" ");
          		//if (bildspeicher[j+32*i]>>4 > g_hell+1) writeString_P("*");
          		//else writeString_P(" ");
    				if (bildspeicher[j+32*i]>>4 > g_hell+1) g_hell_count++;
    			}
    			if ((!lichtpunkt) && (g_hell_count > 10)) lichtpunkt=i;
    				else g_hell_count=0;
    			writeInteger(i,DEC);
    			if (lichtpunkt) { writeString_P("-"); writeInteger(lichtpunkt, DEC);}
    	   	writeString_P("\n\r");
    		}
    		
    	abstand=18;
    if (lichtpunkt)
    
    {
    	pow_l=pow_r=power/2;
    	setMotorPWM(pow_l,pow_r); mSleep(rampe);
    	if (lichtpunkt > abstand)
    	{
    		if (dir_l == BWD)
    		{
    			setMotorPWM(pow_l/2,pow_r/2); mSleep(rampe);
    			setMotorPWM(0,0); mSleep(rampe);
    			dir_l=dir_r=FWD;
    			setMotorDir(dir_l,dir_r);
    			setMotorPWM(pow_l/2,pow_r/2); mSleep(rampe);
    			setMotorPWM(pow_l,pow_r); mSleep(rampe);
    		}
    	}
    
    	if (lichtpunkt < abstand)
    	{
    		if (dir_l == FWD)
    		{
    			setMotorPWM(pow_l/2,pow_r/2); mSleep(rampe);
    			setMotorPWM(0,0); mSleep(rampe);
    			dir_l=dir_r=BWD;
    			setMotorDir(dir_l,dir_r);
    			setMotorPWM(pow_l/2,pow_r/2); mSleep(rampe);
    			setMotorPWM(pow_l,pow_r); mSleep(rampe);
    		}
    	}
    
    	if (lichtpunkt == abstand)
    	{
    		setMotorPWM(0,0); mSleep(rampe);
    	}
    } // ende lichtpunkt != 0
    else
    	{
    		if (dir_l == BWD) dir_l=dir_r=FWD;
    		setMotorDir(dir_l,dir_r);
    		if (pow_l < power)
    		{
    			pow_l=pow_r=power;
    			setMotorPWM(pow_l,pow_r); mSleep(rampe);
    		}
    	}
    }
    
    	return(0);
    }
    Bei 32 Zeilen beträgt die messbare Distanz ca. 27 (Lichtpunkt hebt sich bei Tageslicht nicht mehr auf weisem Blatt ab oder Sehne des Kreissegment des Lichtpunkts kommt am unteren Bildrand nicht mehr auf 10)) und 3 (zu dicht an der Kamera). Das hängt natürlich sehr vom Aufbau ab, die Werte gelten bei Tageslicht und meinem Aufbau:



    Es fehlt noch das "Feintuning", auf diesem Video "sieht" er gelegentlich den Lichtreflex auf den Fliesen:


    (http://www.youtube.com/watch?v=KVcep1v9yT4)

    Und noch ein weiteres Video meiner Tests. Wie man sieht, die Kamera ist ein robuster und katzenresistender Sensor:


    (http://www.youtube.com/watch?v=qWIY-yKWcD4)

    Ich vermute, hier sieht er in erster Linie den Lichtpunkt am Boden. Ohne Kamera klappt es deutlich besser. *schwört* Wenn man da etwas Zeit investiert, kann man sicher viel damit anstellen.

    Gruß

    mic

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  5. #15
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    22.11.2005
    Ort
    Braunschweig
    Alter
    43
    Beiträge
    685
    Moin!
    Das sieht doch alles sehr vielversprechend aus!! Leider hatte ich ncoh keine Zeit, selbst zu basteln, dafür hab ich einen kleinen Linienlaser Ich hoff mal, am WE kann ich auch die ersten Bilder (Achtung, Wortspiel!!!) liefern. Weiter so!!
    MfG
    Volker
    Meine kleine Seite
    http://home.arcor.de/volker.klaffehn
    http://vklaffehn.funpic.de/cms
    neuer Avatar, meine geheime Identität

  6. #16
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    12.02.2006
    Beiträge
    459
    Hallo radbruch,

    sehr interessante Idee, die Kamera so ganz ohne Elektronik an den Atmega32 anzuschließen.
    Vor einiger Zeit habe ich das mal mit einem Atmega8 gemacht, um die Dynamik des Videosignals voll auszunutzen, musst ich mir aber einen kleinen Verstärker basteln.

    Hier das Projekt

    Gruß,
    robo

  7. #17
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    57
    Beiträge
    5.796
    Blog-Einträge
    8
    Hallo robo,

    das ist auch ein hübsches Projekt. Da ich den ADC im Dauerlauf betreibe, kann ich die Syncs auch ohne zusätzlichen Vergleicher erkennen. Mein Kameramodul liefert 1,4V am Multimeter, das messe ich direkt ohne Verstärker. Dadurch erhalte ich bei der internen 2,56V-Referenz zwar nur Werte bis ca. 140 (na sowas), da ich aber sowieso nur mit 4-Bit-Werten weiterrechne, stört das nicht sonderlich.

    Der Ablauf ist aber gleich: Warten auf neue Seite, Zeilen zählen bis gewünschte Zeile erreicht ist, kurze Verzögerung um den Pixel in der Zeile anzusteuern, Wert einlesen.

    32x32 (=1024Byte) erscheint mir auch als optimale Bildgrösse, allerdings wird's dann beim ATMega8 schon sehr eng. Für ein einfaches Linienverfolgen reicht aber auch ein Bruchteil der Zeilen aus. Auch einfache Mustererkennung sollte möglich sein, einen netten Ansatz dazu habe ich hier gefunden. Minimalste Bildverarbeitung und pfiffige Speichermethoden reduzieren dabei den Platzbedarf für ein Bild auf 64 Bytes. Damit könnte man dann mehrere aufeinanderfolgende Bilder speichern und vergleichen, das ist die Grundlage für Bewegungserkennung und den optischen Fluss. Dafür reichen aber meine Mathekenntnisse noch nicht aus. Auch für neuronale Netze, das eigentliche Thema dieses Threads, bin ich noch nicht reif genug.

    Gruß

    mic

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  8. #18
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    29.07.2007
    Beiträge
    386
    eine tolle leistung mit der camera.

  9. #19
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    22.11.2005
    Ort
    Braunschweig
    Alter
    43
    Beiträge
    685
    Moin!
    Ich wecke den Thread mal wieder, ich bin auch grad dabei, das hier umzusetzen, allerdings bin ich etwas verwirrt. Ich benutze einen Mega8 mit 16MHz, aber Deine ADC-Konfig verstehe ich nicht so ganz, lt. Datenblatt wird beim Mega8 für den ADC nix im SFIOR-Register konfiguriert? Also im Prinzip schaltest Du den Prescaler auf 2, den ADC in den Free Running Mode (das ist bei mir ADFR in ADCSRA?), liest fröhlich ADCH, um die Syncs zu erkennen, wartest ab dem Sync eine fixe Zeit, um einen bestimmten Pixel der Zeile zu erwischen? Soweit richtig? Ich habe bei mir das Problem, daß ich 'Ausreißer' bei den Messungen habe, oder die Syncs nicht erwische, dabei müßte ich doch bei 16MHz eher mehr Werte prü Zeile bekommen? Morgen(naja.... nachher) werd ich hier mal ein paar gesampelte Daten+meinen Code hinpacken, evtl. ist's einfach zu spät dafür...

    MfG Volker
    Meine kleine Seite
    http://home.arcor.de/volker.klaffehn
    http://vklaffehn.funpic.de/cms
    neuer Avatar, meine geheime Identität

  10. #20
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    57
    Beiträge
    5.796
    Blog-Einträge
    8
    Hallo,

    der Ablauf ist genau richtig beschrieben. Mit dem SFIOR-Register wird beim ATMega32 der Free-Runing-Modus als Triggerquelle ausgewählt.

    Wichtig ist noch die 2,56V-Referenz für den ADC. Mit den "Schwarzschultern" dauert eine Zeile bis der Wert unter 20 ist. Dann folgt der Zeilensync, dessen Ende ein Wert über 30 (Ende der Schwarzschulter) kennzeichnet. Möglicherweise weichen die Pegel etwas ab. Ein schwarzes Bild (Objektiv geschlossen) sollte Bildpunktwerte von ca. 30 (=schwarz) haben.

    Zwischen Zeilensuchen und Einlesen des Wertes sollten die Interrupts gesperrt sein. Ob der Prescaller /2 bei 16 Mhz auch noch funktioniert konnte ich noch nicht testen. Ich nehme zwar an, dass sich die Werte nicht mehr wesentlich verfälschen, aber sicherheitshalber kannst du auch mal mit /4 testen.

    Morgen ... werd ich hier mal ein paar gesampelte Daten+meinen Code hinpacken
    ...und vielleicht noch eine Anschlußsizze.

    Gruß

    mic

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

Seite 2 von 14 ErsteErste 123412 ... LetzteLetzte

Stichworte

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •