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
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
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!
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?
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...
Hallo
Es wächst und gedeiht, hier mein einfacher Linienfolger:
und ein paar Videos der Tests. Zuerst die RN-Teststrecke: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); }
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...
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:Ich kapier das nicht richtig.
kannst du den code mal so abspecken das er das bild in ein array schreibt?
Damit macht man z.B. 32x32-"Bilder", wenn das jemand braucht. Die Ausgabe dazu findet ihr im Anhang.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); }
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
Bild hier
Atmel’s products are not intended, authorized, or warranted for use
as components in applications intended to support or sustain life!
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
Hallo Sommer
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:Aber wenn ich ins Datenblatt schaue, sehe ich das der Atmega32
für eine ADC Wandlung im bestenfall 13µS benötigt.
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.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); }
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.
Bild hier
Atmel’s products are not intended, authorized, or warranted for use
as components in applications intended to support or sustain life!
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:
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: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); }
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!
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
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
Lesezeichen