-         
Seite 1 von 14 12311 ... LetzteLetzte
Ergebnis 1 bis 10 von 137

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

  1. #1
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    58
    Beiträge
    5.799
    Blog-Einträge
    8

    Minimallösung: Kamera für den RP6

    Anzeige

    Hallo

    In diesem Thread möchte ich euch zeigen, wie man unglaublich einfach und kostengünstig eine Kamera an den RP6 anschliessen kann.

    Grundsätzlich geht es allerdings darum, mit einem 8MHz-ATMega ein Composite-Video-Signal auszuwerten. Ob das, wie in meinem Fall, eine ausgeschlachtete Überwachungskamera liefert, oder ein 15€-Teil vom großen c (art-nr: 150001-62) oder gar der Video-Ausgang einer Digicam oder Foto-Handys, ist völlig egal.

    Die Hardware:

    - Ein Kameramodul, das mit 5V funktioniert und ein Composite-Video-Signal liefert (5V, 1VSS/75Ohm)
    - Ein Stecker, der in die RP6-XBUS1/2-Buchse passt
    - Ein Cinch-Stecker, der das Kontrollsignal für einen Bildschirm auskoppelt.

    Der Anschluß:

    Die 5V-Kamera wird am Pin 3/5(Vcc) und 1/2(GND) angeschlossen. Der Vid-Ausgang der Kamera (oder des beliebigen Composite-Video-Lieferanten) kommt an Pin 8 (E_INT1). Dies ist, außer den User_ADCs der einzig freie ADC-Kanal, der zudem noch praktischerweise am XBus liegt. Außerdem hat er einen PullDown von 10K, die optimale Last für ein Composite-Signal. Der Chinch-Stecker wird zusätzlich zwischen Pin 8 (Signal) und Pin 1/2 (GND) angeschlossen. Der ist eigentlich nur zu Kontrolle gedacht, damit man sehen kann, was der RP6 sieht. Gam(ma) ist bei meiner Kamera nicht angeschlossen, sollte man aber als Jumper vorsehen.

    Die Software:

    Erste Hürde war der ADC und die damit möglichen Samplezeiten. Die im Datenblatt des ATMegas angegebenen 200kHz reichen für die 5MHz eines fbas-Signals bei weitem nicht aus. Aber er ist schnell genug, um wenigstens ein paar Daten einzulesen. Hier das Setup des ADC:

    Code:
    // 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);
    Referenzspannung sind die interenen 2,56V. Das ist recht praktisch, den ich lese das linksbündige Ergebniss als Byte-Variable ein und jeder Schritt entspricht nun 0,01V. (mein Signal hat 1,4V mit dem Multimeter gemessen) Im "8-Bit-Modus" ließt man nur die MSB der Ergebnisse, die der ADC bei "free running" mit 4MHz digitalisiert.

    Nächste Hürde war das Timing des Signals. Mit der Samplerate kann ich das vsync-Signal gut erkennen, das hsync ist deutlich länger und markiert den Start des Halbbildes (Alle Fachbegriffe und Kentnisse habe ich von hier). Einige auf einander folgende Zeilen sehen etwa so aus:

    http://radbruch.roboterbastler.de/rp...nc-signale.pdf

    Man sieht deutlich die vsyncs, allerdings schaffe ich so nur ca. 60 Werte pro Zeile. Mehr geht einfach nicht mit C, vielleicht kann man mit Assembler hier noch etwas "rauskitzeln". Mein Code für das Einlesen eines Pixels:

    do *pixelzeiger=ADCH; while (*pixelzeiger++ > 20);

    Alternativen wie:

    while (count_pixel) pixel[--count_pixel]=ADCH;
    for (;count_pixel; pixel[--count_pixel]=ADCH);

    wurden fast identisch übersetzt und brachten nur ca. 50 Lesungen pro Zeile. Mit

    Code:
    	while (line) // Zeile abwarten
    	{
    		while (ADCH > 20); while (ADCH < 30); line--;
    	}
    kann ich nun das vsync erkennen, hsync dauert bei mir ca. 47 Zyclen, also prüfe ich so:

    Code:
    	do // hsync abwarten
    	{
    		vsync=0; while (ADCH > 20); while (ADCH < 30) vsync++;
    	} while (vsync < 40);
    Da ich so vertikal "nur" ca. 60 Werte erfassen kann, aber horizontal alle Zeilen zwischen ca. 30 und 260 direkt ansteuern kann, habe ich meine Kamera zum Linienfolgen um 90 Grad gedreht. Zudem verwende ich nur die Pixel 10 bis 14, also einen recht kleinen Bereich der verfügbaren Daten:

    Code:
    #include "RP6RobotBaseLib.h"
    
    #define mpower 50
    #define spur 145
    
    // 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;
    }
    
    uint16_t get_line(uint16_t line)
    {
    	uint8_t pixel[256],*pixelzeiger, vsync;
    	uint16_t temp=0;
    
    	pixelzeiger=&pixel[0]; // Zeiger auf Start Pixelspeicher
    	cli();
    	do // hsync abwarten
    	{
    		vsync=0; while (ADCH > 20); while (ADCH < 30) vsync++;
    	} while (vsync < 40);
    
    	while (line) // Zeile abwarten
    	{
    		while (ADCH > 20); while (ADCH < 30); line--;
    	}
    
    	do *pixelzeiger=ADCH; while (*pixelzeiger++ > 20); // Zeile einlesen
    	sei();
    	*pixelzeiger=0; // Endekennung der Daten
    
    	pixelzeiger=&pixel[10]; // Summe der Pixel 10-14 bilden
    	while (pixelzeiger < &pixel[15]) temp+=*pixelzeiger++;
    	return (temp);
    }
    
    int main(void)
    {
    	uint16_t zeile;
    	uint16_t gamma, i;
    	uint16_t strich_links, strich_rechts, strich_mitte, strich_breite;
    	uint8_t 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);
    
    	gamma=0; i=0;
    	for (zeile=30; zeile<260; zeile+=5)
    	{
    		gamma+=get_line(zeile);
    		i++;
    	}
    	gamma=gamma/i;
    	gamma-=gamma/3;
    	writeInteger(gamma,DEC);
    	writeString_P("\n\r");
    	
    	setMotorDir(BWD,BWD);
    	pow_l=pow_r=mpower;
    	setMotorPWM(pow_l/2,pow_r/2);
    	mSleep(mpower);
    	setMotorPWM(pow_l,pow_r);
    
    	do
    	{
    	   strich_links=strich_rechts=0;
    		zeile=30;
    		do
    		{
    		   if (!strich_links && (get_line(zeile)<gamma))
    			{
    				strich_links=zeile;
    				zeile+=5;
    			}
    		   if (strich_links && (get_line(zeile)>gamma)) strich_rechts=zeile;
    			zeile+=10;
    		} while ((zeile<260) && !strich_rechts);
    		if (!strich_rechts) strich_rechts=260;
    
    		strich_mitte=(strich_links+strich_rechts)/2;
    		strich_breite=strich_rechts-strich_links;
    
    		if ((strich_links < spur) && (strich_rechts > spur))
    			pow_l=pow_r=mpower;
    		else
    		{
    			if (strich_links > spur) {pow_l=pow_l/2; pow_r=mpower;}
    			if (strich_rechts < spur) {pow_l=mpower; pow_r=pow_r/2;}
    		}
    		setMotorPWM(pow_l,pow_r);
    
    		//writeInteger(spur,DEC);
    		//writeString_P(" - ");
    		writeInteger(strich_mitte,DEC);
    		writeString_P(" - ");
    		writeInteger(strich_breite,DEC);
    		writeString_P("\n\r");
     		//mSleep(100);
    	} while (strich_breite < 100);
    	//setMotorDir(FWD,FWD);
    	setMotorPWM(pow_l/2,pow_r/2);
    	mSleep(mpower);
    	setMotorPWM(0,0);
    	//mSleep(500);
    	while (1);
    	return(0);
    }
    Für OCR oder das Erkennen von Personen dürfte diese Lösung nicht taugen. Noch fehlt die KI, aber dafür haben wir ja andere Spezialisten.

    Gruß

    mic

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

  2. #2
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    20.01.2004
    Alter
    32
    Beiträge
    645
    Wow, echt beeindruckend was du da geschafft hast! Ich komm zur Zeit einfach zu nichts...
    Wie sieht es mit der Stromaufnahme der Kamera aus? Man könnte auf diese Weiße einen Laserscanner verwirklichen. Schau mal auf die Homepage von toemchen...
    MfG Xtreme
    RP6 Test - alles zum Nachfolger des bekannten RP5 im neuen RP6 Forum!

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

    Die Stromaufnahme der Kamera wird bei Conrad mit 10mA angegeben.

    Natürlich könnte man damit auch solch einen Laserscanner realisieren, der ohne unterstützenden PC funktioniert.

    Das funktioniert übrigends nicht nur mit dem RP6, sondern mit allen 8MHz-ATMegas mit freiem ADC-Kanal. Mehr als 8Mhz ist kein Problem, weniger ist kritisch, weil der vsync nur 4,7us dauert.

    Anbauvorschlag für den asuro:

    Wenn man den Widerstand R12(12k) der Batteriemessschaltung entfernt, kann man das Kamerasignal am ADC5 anschließen. Der R13(10k) kann drin bleiben und bildet wie bei meinem RP6 eine Grundlast für das Signal(PullDown). Plus der Kamera ins freie Loch vom R12 (Vbat), Minus an den R13(GND). Mit einem kleinen Stecker könnte man noch wahlweise die Kamera oder den R12 einstecken. Wobei ich davon ausgehe, dass die Kamera auch mit 4,8V funktioniert. Einzige Programmänderung wäre dann ein anderer Kanal im ADMUX-Register des ADC.

    Noch ein paar Pics:


    Gruß

    mic

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

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

    Da ich die Kamera vorerst zum Linienfolgen einsetzen möchte, reicht es, wenn der RP6 schwarz und weis unterscheiden kann. Deshalb verwende ich nur noch die oberen 4Bit der Ergebnisses. Da dann nur 16 Werte auftreten, kann man locker in Echtzeit eine Gammaberechnung machen und nun erkennt der RP6 die Linie ziemlich gut und vor allem recht unabhängig vom Fremdlicht:


    (Bild anklicken für youtube-Video)

    In dieser Version kann der RP6 über 40 verschiedene Positionen des Strichs von links-raus nach rechts-raus erkennen. Das sind bei diesem Abstand einige Zentimeter. Strichbreite und vor allem Strichmitte kann er auch schon berechnen.

    Gruß

    mic

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

  5. #5
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    22.11.2005
    Ort
    Braunschweig
    Alter
    44
    Beiträge
    685
    Hallo!
    Sehr schöne Idee, da werd ich mich wohl von meiner Gameboycamera wieder lösen und es mal damit versuchen. Wenn Du bei 8MHz ca.60Pixel pro Zeile schaffst, komme ich mit 16MHz auch in die Nähe von 120, bei voller Zeilenauflösung, da bin ich besser als die GBCAM mit 128*128 Pixeln und hab nicht den ganzen Stress mit Belichtung und Registerkonfiguration.... Vielen Dank für die Inspiration, werde bei Gelegenheit berichten, wie es dann bei mir aussieht.

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

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

    Du wertest also die Spannungen zwischen V und HSync aus oder wie kann ich das verstehen? So ganz bin ich bei deiner Auswertung noch nicht mitgekommen Auch deine Bitaufteilung verstehe ich noch nicht ganz? Bitte um aufklärung... mich würds einfach näher Interessieren...

    Gruß Ulli

  7. #7
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    58
    Beiträge
    5.799
    Blog-Einträge
    8
    Hallo Volker

    Mit der GB-Kamera wollte ich's auch mal versuchen, nur leider hatte ich keine rumliegen.

    Mit der 8Mhz funktioniert es eigentlich schon recht gut. Man kann natürlich keine Wunder erwarten. Ein großes Problem ist auch die Datenmenge, mehr Pixel bedeutet eben auch mehr Daten.

    In der ersten Variante habe ich auf den vsnyc (und den vertikalen Strahlrücklauf) gewartet, er dauert bei meiner Lesegeschwindigkeit ca. 47 Lesungen. Dann habe ich die hsyncs gezählt um die gewünschte Zeile anzusteuern (wegen der Datenmenge nur jeder Xte Zeile, das ergibt die vertikale Auflösung). Bei Erreichen der gewünschten Zeile habe ich dann so schnell wie möglich die Daten der Zeile eingelesen und komme so auf ca. 60 Pixel/Zeile (horizontale) Auflösung. Aus Speichermangel habe ich dann die Zeile verarbeitet und dann erst die nächste eingelesen. Deshalb benötigt ein kompletter Scan des Bildes pro eingelesene Zeile mindestens ein Halbbild für das eigentliche scannen und noch ein paar weitere fürs verarbeiten. In Summe deutlich zu lange.

    Gegenüber diesen ersten Versuchen sieht das Einlesens nun wesentlich anders aus. Ich lese pro Scan nun nur noch einen (beliebigen) horizontalen Pixel pro Zeile, den dafür aber pro Halbbild in allen gewünschten vertikalen Linien auf einen Schlag. Das Bild wird dadurch Spaltenweise eingelesen. Der sichtbare Bereich meines Bildes liegt zwischen den Zeilen 30 und 260, also habe ich so eine maximal mögliche vertikale Auflösung von 230 Pixeln.

    Da ich nun horizontal nur noch einen Pixel/Zeile einlesen muss, ist es nun nicht mehr kritisch, wie schnell man das kann. Man muss nur den Pixel finden. Und das mache ich in dieser Variante so:

    Wie gewohnt warten auf den Start des Halbbildes, dann 30 Zeilen "Austastlücke für BTX" abwarten, jetzt beginnt das eigentliche Einlesen. Und hier nun die wesentliche Änderung, nach dem hsync wird erstmal eine kurze Zeit abgewartet:

    h_delay=50; while (h_delay--);

    Das ist der x-Versatz des Pixels, das ich einlesen will. Über die Dauer der Verzögerung kann ich unabhängig von der Lesegeschwindigkeit des ADCs erst den Lesezeitpunkt innerhalb einer Zeile festlegen (max für h_delay weiß ich noch nicht). Dann löscht eine Dummy-Lesung das alte ADC-Resultat und startet damit ein erneutes Sampeln des Signals. Nun lese ich das ADCH nochmal aus und habe damit den gewünschten Pixel. Jetzt bleibt noch genug Zeit um das Ende der aktuellen Zeile abzuwarten. Nach dem nächsten vsync kann das Pixel in der nächsten (oder Xten) Zeile eingelesen werden. Somit dauert das Lesen einer kompletten Spalte so lange wie ein Halbbild.

    Die horizontale Auflösung entspricht den möglichen Werte für das h-delay, also etwas mehr als 0 bis Ende der Zeile. Das habe ich noch nicht gemessen, denn eigentlich reicht es, nur an bestimmten Punkten eine Spalte zu scannen um einen Punkt, Strich oder gar ein einfaches Muster zu erkennen. Deshalb ist meine Kamera auch um 90° gedreht, der Strich kommt von links ins Bild und die Lage des Strichs entspricht dadurch den Zeilen des Bildes. Dann reicht ein Scan einer Spalte, um den Strich zu erkennen. Mit 2 Scans in verschiedenen Spalten kann man dann schon die Richtung des Striches auswerten...

    Hier noch mein aktueller, ungeschminkter Arbeitscode als Anregung:

    Code:
    #include "RP6RobotBaseLib.h"
    
    #define mpower 50
    #define spur 145
    
    // 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 get_lines(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();
    	//*pixelzeiger=0; // Endekennung der Daten
    
    	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;
    	}
    /*
    	for (i=0; i<16; i++)
    	{
    		if (i) writeString_P("-");
    		writeInteger(gamma[i],DEC);
    	}
    	writeString_P("\n\r");
    	writeInteger(g_dunkel, DEC);
    	writeString_P("-");
    	writeInteger(g_hell, DEC);
    	writeString_P("\n\r");
    */
    	strich_links=0;
    	strich_rechts=0;
    	for (i=0; i<zeilen; i++)
    	{
    		if ((pixel[i] >> 4) == g_dunkel) writeString_P("*");
    			else writeString_P(" ");
    		if (((pixel[i] >> 4) == g_dunkel) && ((g_hell-g_dunkel)>1))
    		{
    			if (!strich_links) strich_links=i;
    				else strich_rechts=i;
    		}
    	}
    	strich_mitte=(strich_links+strich_rechts)/2;
    	strich_breite=strich_rechts-strich_links;
    	writeString_P(" | ");
    	writeInteger(strich_links, DEC);
    	writeString_P("-");
    	writeInteger(strich_rechts, DEC);
    	writeString_P("-");
    	writeInteger(strich_mitte, DEC);
    	writeString_P("\n\r");
    }
    
    int main(void)
    {
    //	uint16_t strich_links, strich_rechts, strich_mitte, strich_breite;
    //	uint8_t 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);
    
    while (1){
    	get_lines(50,48);
    	mSleep(50);
    }
    /*
    	setMotorDir(BWD,BWD);
    	pow_l=pow_r=mpower;
    	setMotorPWM(pow_l/2,pow_r/2);
    	mSleep(mpower);
    	setMotorPWM(pow_l,pow_r);
    
    	do
    	{
    	   strich_links=strich_rechts=0;
    		zeile=30;
    		do
    		{
    		   if (!strich_links && (get_line(zeile)<gamma))
    			{
    				strich_links=zeile;
    				zeile+=5;
    			}
    		   if (strich_links && (get_line(zeile)>gamma)) strich_rechts=zeile;
    			zeile+=10;
    		} while ((zeile<260) && !strich_rechts);
    		if (!strich_rechts) strich_rechts=260;
    
    		strich_mitte=(strich_links+strich_rechts)/2;
    		strich_breite=strich_rechts-strich_links;
    
    		if ((strich_links < spur) && (strich_rechts > spur))
    			pow_l=pow_r=mpower;
    		else
    		{
    			if (strich_links > spur) {pow_l=pow_l/2; pow_r=mpower;}
    			if (strich_rechts < spur) {pow_l=mpower; pow_r=pow_r/2;}
    		}
    		setMotorPWM(pow_l,pow_r);
    
    		//writeInteger(spur,DEC);
    		//writeString_P(" - ");
    		writeInteger(strich_mitte,DEC);
    		writeString_P(" - ");
    		writeInteger(strich_breite,DEC);
    		writeString_P("\n\r");
     		//mSleep(100);
    	} while (strich_breite < 100);
    	//setMotorDir(FWD,FWD);
    	setMotorPWM(pow_l/2,pow_r/2);
    	mSleep(mpower);
    	setMotorPWM(0,0);
    	//mSleep(500);
    */
    	while (1);
    	return(0);
    }
    Der Funktion get_line() wird der Wert für den h_delay und die Anzahl der gewünschten Pixel/Spalte übergeben (das Pixelfeld hat 256 Elemente, ich lese aber nur 48 Pixel ein).

    Nach dem Stetzen der Startbedingungen wird eine Spalte in pixel[] eingelesen, dann ein Hystogramm für die oberen 4Bit von pixel[] in gamma[] berechnet. Dann wird in gamma[] eine "Kante" von mindestens g_sprung gesucht, von dunkel her ist das die Linie, von hell aus gesucht das Blatt. Jetzt folgt noch die (Test-)Ausgabe der erkannten Strich-Pixel und die Berechnung der Strichdaten Kante-links/-rechts, Strich-Breite und Mitte. Strichmitte soll übrigens mal der Rückgabewert dieser Funktion werden.

    @Sommer
    Vielleicht reichen dir die Erklärungen schon.

    Gruß

    mic

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

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

    ja denke vorerst mal schon
    Jetzt werden alle mal selber probieren

    Auf jedenfall eine Interessante Low Cost Lösung wenns funktioniert wie du schreibst. Kannst auch mal ein Video posten wo RP6 eine Linie entlangfährt?

    Gruß Ulli

  9. #9
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    29.11.2006
    Ort
    Geislingen a. d. Steige
    Alter
    29
    Beiträge
    344
    Hi radbruch,

    Ich kapier das nicht richtig.
    kannst du den code mal so abspecken das er das bild in ein array schreibt?

    MfG Martin

  10. #10
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    22.11.2005
    Ort
    Braunschweig
    Alter
    44
    Beiträge
    685
    Hallo!
    Danke für die ausführliche Erläuterung, meine Kamera auch 90° gedreht, das ganze soll, wie irgendwo in dem Thread hier schon erwähnt, als Laserscanner arbeiten, d.h. ich brauche quasi nur zeilenweise auswerten, allerdings wollte ich später noch ein wenig mehr machen, quasi einfachste Mustererkennung und so, mein Mega32 hat auch schon 32KB SRAM zur Seite, da kann man notfalls das eine oder andere kleine Bildchen zum vergleichen abspeichern, ich weiß leider nur noch nicht, wann ich mal wieder Zeit zum basteln habe....
    MfG
    Volker
    Meine kleine Seite
    http://home.arcor.de/volker.klaffehn
    http://vklaffehn.funpic.de/cms
    neuer Avatar, meine geheime Identität

Seite 1 von 14 12311 ... LetzteLetzte

Stichworte

Berechtigungen

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