- 3D-Druck Einstieg und Tipps         
Seite 2 von 11 ErsteErste 1234 ... LetzteLetzte
Ergebnis 11 bis 20 von 137

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

Hybrid-Darstellung

Vorheriger Beitrag Vorheriger Beitrag   Nächster Beitrag Nächster Beitrag
  1. #1
    Hallo Radbruch

    Gigantisch was du da gemacht hast. Funktionier das auch mit einer Farbkamera zb. 156967 - 62 vom grossen C ? Muss man da etwas besonderes beachten oder kann ich die Kamera wie oben beschrieben anschliessen ?

    Danke und Grüße
    HSC123

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

    Obwohl ich es nie mit einer Farbkamera versucht habe, gehe ich davon aus, dass es mit dieser Kamera auch funktioniert. 5V Versorgung und 1V Signal scheint ideal, nur der Preis ist etwas happig. Bevor du dich in Unkosten stürzt, sollte dir klar sein, das es keine "ready to use"-Lösung für diese Anwendung einer Kamera gibt.

    Für den Fall, dass du das noch nicht gefunden hast, hier der Link zum "Mitmach-Projekt" (das softwaremäßig leider in den Startlöchern stecken geblieben ist). Großes Lob an dieser Stelle an Dirk:

    http://www.roboternetz.de/community/...ra-für-den-RP6

    Gruß

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

  3. #3
    Erfahrener Benutzer Roboter-Spezialist Avatar von RolfD
    Registriert seit
    07.02.2011
    Beiträge
    414
    Hallöle,
    man muss vielelicht dazu sagen, das eine cam Lösung weniger an irgendwelchen technischen Problemen scheitert - eagl ob SW oder das schwieriger zu dekodierende FBAS Farbsignal - als viel mehr am verfügbaren Ram um das Image dann auch im Speicher abzubilden und auszuwerten. Die meisten mir bekannten Lösungen schieben daher das Bild raw auf den PC welcher das Image in Folge bearbeitet und Steuersequenzen an den Mega32 sendet. Auf dem Mega32 ist nicht viel mehr als z.B. ein einfacher Linienfolger machbar - und der muss nicht bunt sein. Ein Farbbild braucht im günstigsten Fall "nur" 3 mal mehr Ram als ein greyscale gleicher Bittiefe.
    Möchte man dann aber noch Einstellungen an der cam vornehmen (capture/exposure time usw.), ist man mit einer i2c-cam eh besser bedient. Allerdings löst das auch nicht das allgegenwärtige Speicherproblem.
    Wenn du sinnvoll mit einer Cam spielen möchtest, leg dir daher lieber gleich nen anständiges ARM7/9 Board mit satt Speicher und USB Cam zu oder versuch die PC-lösung... z.B. mit der neuen WLAN Karte.

    Selbst das Minibild einer Gameboy cam in greyscale verlangt schon 8-16k Ram.... je nach bittiefe
    http://www.angelfire.com/de3/juliprograms/amr/gbcam.htm
    Und das Ding ist wenigstens dank Artifical Retina Image Sensor einstellbar.
    (äm.. womit ich nicht das Projekt hier kritisieren möchte... aber es hat eben Grenzen...)

    Gruß Rolf
    Sind Sie auch ambivalent?

  4. #4
    Neuer Benutzer Öfters hier
    Registriert seit
    25.05.2012
    Beiträge
    15
    Hi,

    man muss aber nicht das ganze Bild im RAM halten für einige bestimmte algos.
    z.B. hier:
    http://www.jrobot.net/Projects/AVRcam.html

    farbige Objekte verfolgen und Statistiken mit nem kleinen MEGA8.
    Das geht natürlich nicht mit der analogen Cam das ist klar.
    Bei den analogen Cams geht natürlich auch noch einiges mehr an Rechenzeit für das erfassen des Bildes ansich drauf...

  5. #5
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    61
    Beiträge
    5.799
    Blog-Einträge
    8
    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:

    Bild hier  
    (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:

    Bild hier  
    (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
    Bild hier  
    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  6. #6
    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

  7. #7
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    61
    Beiträge
    5.799
    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
    Bild hier  
    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  8. #8
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    61
    Beiträge
    5.799
    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:

    Bild hier  

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

    Bild hier  
    (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:

    Bild hier  
    (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
    Bild hier  
    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  9. #9
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    22.11.2005
    Ort
    Braunschweig
    Alter
    48
    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

  10. #10
    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

Seite 2 von 11 ErsteErste 1234 ... LetzteLetzte

Stichworte

Berechtigungen

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

LiFePO4 Speicher Test