Der ARX-HRCH01 hat im Kopf eine runde Vertiefung mit Schalllöchern für einen Lautsprecher vorgesehen. Damit der Eric auch ein paar Töne macht, habe ich einen Kleinlautsprecher verbaut, den ich an dem PIC18F25K22 über einen Tiefpass und LM386 angesteuert habe. Jetzt kan der Eric einen Rock'n'Roll aufs Parket legen
Ich habe mich jetzt mit dem Distanzsensor des ARX beschäftigt. Es ist ein Standard IR LED - Phototransistor Pärchen. Mit 30mA LED Strom lassen sich wunderbar Hindernisse vor dem Roboter erkennen. Mit der folgenden Schaltung reagiert der Sensor auf ca 20-30cm bei künstlicher Zimmer beleuchtung. Bei Sonnenlicht bzw. hellem Tageslicht funktionierts mit simpler Digitalauswertung nicht, wenn der Phototransistor bereits durch das Umgebungslicht durchsteuert. Stört aber bei den ersten Tests bei künstlicher Beleuchtung nicht weiter. Bild hier Mit dem Distanzsensor kann sich der Eric schon ein bisschen autonom bewegen: Nachtrag 17.06.2018: Um den Kollisionssensor gegen Störlicht unempfindlicher und auch bei Tageslichteinstralung ins Zimmer funktionsfähig zu machen teste ich gerade die Analogauswertung aus. Durch Vergleich der Analogwerte bei ein- und ausgeaschalteter LED liefert der Sensor ein Ergebnis auch bei hellem Umgebungslicht.
Heute habe ich mir den Drehschalter im Kopfgelenk von Eric (so heisst er mittlerweile) vorgenommen. Es werden drei Positionen mittels Schleifkontakte erfasst, die Auswertung habe ich mit zwei Digitaleingängen realisiert. Bild hier Code: // HeadPos: 1=Left,2=Right,3=Middle HeadPos = 0; if (HeadPosRight == 0) HeadPos++; HeadPos <<= 1; if (HeadPosLeft == 0) HeadPos++;
// HeadPos: 1=Left,2=Right,3=Middle HeadPos = 0; if (HeadPosRight == 0) HeadPos++; HeadPos <<= 1; if (HeadPosLeft == 0) HeadPos++;
Zitat von witkatz Rot und Blau ergibt ein schönes rosa Fahrlicht (ganz wichtig!) ... Gruß Searcher
Jetzt hat KNIX eine mehrfarbige RGB-Fahrbeleuchtung und damit die bunten Tasten auf der Fernbedienung eine Funktion bekommen Bild hier Rot und Blau ergibt ein schönes rosa Fahrlicht (ganz wichtig!) und alle drei Farben zusammen geben ein helles Weiss. Somit hat der KNIX reichlich Funktionen bekommen und wird in den normalen Spielbetrieb übergeben. Fertig ist so was nie, es ist noch Platz auf der Steuerplatine für künftige Ideen der "Projektleitung" und auch im Flash des PICs für meine Software-Spielerein. Damit möchte ich diesen Blog-Eintrag abschließen und mich bei allen, die hier reingeschaut haben bedanken.
Danke Searcher für den Verbesserungsvorschlag. Ich muss das mal mit der Projektleitung besprechen Die Servo Ansteuerung ist noch provisorisch in der Software mit einem vorhandenen 125µs Timer generiert. Das reicht eben um mit der Ladeklappe zu klappern. Für eine bessere Auflösung und flüssige Servo Bewegung werde ich ein Compare Modul auf dem Pin ausgeben. Der PIC18F25K22 hat zum Glück reichlich Timer und Compare Module. Der Servo hängt an CCP3, das Quellcode für Servoansteuerung mit CCP3 Interrupt ist recht überschaubar: Code: if(PIR4bits.CCP3IF == 1){ if(CCP3CONbits.CCP3M0 == 0){ // RC6/CCP3 is high CCP3CONbits.CCP3M0 = 1; // go low next Interrupt CCPR3 = TMR1 + tmr1ServoCCP3; } else { CCP3CONbits.CCP3M0 = 0; // go high next Interrupt CCPR3 += (20000 - tmr1ServoCCP3); } PIR4bits.CCP3IF = 0; } Das müsste gehen, muss ich nur noch testen.
if(PIR4bits.CCP3IF == 1){ if(CCP3CONbits.CCP3M0 == 0){ // RC6/CCP3 is high CCP3CONbits.CCP3M0 = 1; // go low next Interrupt CCPR3 = TMR1 + tmr1ServoCCP3; } else { CCP3CONbits.CCP3M0 = 0; // go high next Interrupt CCPR3 += (20000 - tmr1ServoCCP3); } PIR4bits.CCP3IF = 0; }
Hi witkatz, prima! Der Kippvorgang war mir allerdings viel zu trocken. Soll heißen: ist mir viel zu schnell und man kann wenig bei dem Vorgang beobachten Liegt wahrscheinlich daran, daß ich selber vor Kurzem ein bißchen mit Verlangsamung von einem Servo über schrittweise Veränderung der Pulsweite gespielt habe. Da liegt Potential drin Gruß Searcher
Interessant. Möchte demnächst auf einem reaktivierten Oldtimer von mir (dem Quadenc mit Mausencodern) auch irgendeine Art von Regelung realisieren. Gruß Searcher
Eine schnelle Excel-Auswertung der Aufzeichnung von heute nacht bestätigt meine Befürchtung es ist frostig kalt geworden auf Balkonien in Duisburg: Bild hier
Um die Bahnver- und Zerlegung zu beschleunigen, habe ich die Bahnabschnitte an 4 Stellen 1cm eingeschnitten. Jetzt lassen sie die Blätter mit Geraden- und Kurvenabschnitten wie Gleisabschnitte einer Modelleisenbahn einfach zusammenstecken - ohne zusammenkleben. Ein paar Minuten und die Strecke Wohnzimmer - Küche ist zusammengesteckt und mit Kreppband auf dem Boden fixiert
Zwischen den Jahren ist die beste Bastelzeit Der WR02 kann jetzt als Linienfolger kleine Transportaufträge in der Wohnung ausführen. Eine aus einzelnen auf A4 gedruckten Bahnabschitten verlegbare Linienbahn lässt sich flexibel verlegen und verändern. Die PVC-Klebstreifen lassen sich vom Papier und Boden leicht lösen, so dass die Bahn wiederwerwendbar ist. Problematisch sind jedoch Unebenheiten auf der Strecke. Die Höhenunterschiede an Teppichkanten und Türschwellen habe ich stufenweise mit Papierstapeln auf Stufen <2mm nivelliert. Wenn der Liniensensor mit RPR220 richtig in der Höhe justiert ist, hat er etwas reserve im Erfassungsbereich nach oben und nach unten so dass der Roboter <2mm Stufen auf und ab schafft. Wenn die Papierbahn während des Betriebs jedoch größere Beulen bekommt, verliert der Roboter an den Stellen die Linie. Zum Spielen ist eine aus 80g Standardpapier verlegte Bahn ok, aber als Buttler ist so ein Linienfolger auf Papierbahn nicht so zuverlässig Wenn der Spielspaß dauerhaft anhält, werde ich vielleicht die Bahn auf 100 oder 120g Papier ausdrucken. Zum Abschluss dieses Blogs habe ich ein kurzes, unspektakuläres Video gedreht, in dem WR02 ein Flens holt. Prost Neujahr!
Der Liniensensor ist jetzt am Roboter montiert und hat erste Funktionstests bestanden. Bild hier Bis zu 45° Knicke werden bei problemlos im Test Bahn gefahren. Die Bahn besteht aus DIN A4 Blättern, auf allen ist einfach eine 30mm dicke gerade Linie ausgedruckt. Die Blätter sind an den Stoßstellen mit einem Stückchen PVC Isolierband zusammengeklebt, dass sich oft abnehmen und wieder aufkleben lässt. Die "Bahnstücke" lassen sich so für verschiedene Strecken wiederverwenden. Hier ein kurzes Video vom Einsatz als Buttler:
Mein Liniensensor folgt einem anderen Ansatz als deiner. Die Lichtschranken werden schwarz/weiss von einem kleinen Sklaven µC ausgewertet, ohne ADC. Mit den 4 Lichtschranken wird die Entfernung der Linie von der Mitte des Sensors diskret gemessen und als vorzeichenbehaftete Zahl an den Haupt µC gesendet. Das Ergebnis ist also auch eine vorzeichenbehaftete Zahl ähnlich wie bei deiner bipolaren Differentiell-ADC Messung, allerdings bei mir mit einer viel gröberen Auflösung von 1cm, bzw. halben Sensorabstand. In wie weit sich der Ansatz bewährt, weiss ich noch nicht. Ich bin gerade dabei, meinen Roboter so umzubauen, dass ich diesen Sensor hier und später ggf. diverse andere auf eine 100mm breiten Platine aufgebaute Liniensensoren einfach dranschrauben und ausprobieren kann. Gruß witkatz
Hallo witkatz, danke für die Infos. Bei der Verbindung des Liniensensors zum Hauptprozessor hatte ich zu kompliziert gedacht und Deine Lösung geht ja auch eigentlich aus Deiner Vorstellung des Liniensensors hervor. Ich kämpfe gerade bei mir damit rum, da ich ja Daten seriell von meinem Liniensensor-µC zum Haupt-µC übertrage. Bei Deiner Begründung zu den vielen vielen eingesetzten µC ist mir nur Punkt 3 sofort eingängig. Das meiner der Linie recht gut folgt ist eventuell Glücksache. Es gibt keine Regelung, sondern der ADC liest die Sensoren im Freerunning Modus so schnell aus wie er kann, paßt sie größenordnungsmäßig an, damit sie im Hauptprozessor direkt als Steuerinformation genutzt werden kann. Zuerst hatte ich den Liniensensorwert durch 2 geteilt - Übersteuern. Durch 4 geteilt - geht bei angepaßter Grundgeschwindigkeit per Fernbedienung. Quick und dirty eben. Aber auch unterstützt durch die fast unschlagbare Reaktion der Schrittmotore auf die Steuerinformation. Bei mir sieht es nach noch viel Arbeit aus, vor allem die Programme zu überarbeiten und dabei auch in einem Jahr noch verständliche Kommentare zu haben. Hoffe bei Dir läuft das besser. Gruß Searcher
Hallo Searcher, natürlich habe ich gesehen, woran du bastelst. Ich verfolge deinen Blog genau und mit viel Freude. Es sieht sieht super gut aus, wie dein steppender Linienfolger schön exakt und ohne erkennbare Schwingungen die Linie nachfährt. Ob das bei meinem Butler auch so gut klappen wird, werde ich erst sehen. Knicke brauche ich deshalb, weil ich die Bahn aus bedruckten DIN A4 Einzelblättern auslegen möchte. Knicke von 90° sind erstmal nicht vorgesehen. Wenn mein Butler 45° schafft ohne das Getränk zu verschütten, bin ich schon zufrieden. Ich werde dann auch ein Video posten, als neuen Blog-Beitrag oder als Kommentar zu dem alten Roboterbeitrag wie ich das bis jetzt gemacht habe. Was würdest du vorschlagen? Tja, jetzt zu deiner schwierigen warum Frage. Ich habe mehrere Gründe für die verwendung von mehreren µC: 1. I/O Pins des Haupt µC sparen 2. Spaß an Modulen, die ich separat testen kann. 3. Um die PICs zu verbasteln, die ich in 10er Losgrößen in China bestellt habe, als der EUR noch hoch stand Der Liniensensor hat nur 3 Bits zu melden, da nehme ich einfach HW Verbindung mit TTL Pegeln. Bei größeren Datenbreiten würde ich I2C nehmen. Gruß witkatz
Hi, ich bastele ja an was Ähnlichem. Habe nur zwei Photodioden, die analog an einem ADC ausgewertet werden. Funktioniert sehr gut mit Motoren, die praktisch sofort auf Steuerungsinformationen reagieren können ABER ein rechtwinkliger Knick in der Linie wird einfach nicht wahrgenommen und somit ignoriert außer ich schneide ein kleines Dreieck an der Außenkante des Knicks an der Linie ab. Bin gespannt, ob Dein Sensor bzw die Auswertung das packt. Ich muß vermutlich noch irgendetwas nachrüsten (oder alle 90° Knicke beschneiden ) Warum benutzt Du mehrere µCs und wie werden die beiden verbunden, HW und Protokoll? Viel Erfolg und Gruß Searcher
Das Handling des Abstand- und Kollisionssensors im Haupt µC (PIC18F4520) ist fertig getestet. Ich frage den HC-SR04 in einer 250µs ISR Routine ab, damit komme ich auf eine Genauigkeit von +/- 4cm. Für eine Distanzmessung zwecks Kollisionservermeidung ist es ausreichend. Code: volatile struct { char LastColSensor:1; char NewRawColDistanceAvailable:1; char reserve: 6; } Flags; void interrupt isr(void) { // 250 µs Interrupt, oscillator 32MHz, TMR0 prescaler 1:16 TMR0 -= 125; // Timer Value for next 250µs interrupt // ... // handling collision sensor in 250µs ISR Routine Tmr16bit250usColSens++; if ((CollisionSensor == 1) && (Flags.LastColSensor == 0)){ // handle positive trig collision sensor Tmr16bit250usColSens = 0; Flags.LastColSensor = 1; } if ((CollisionSensor == 0) && (Flags.LastColSensor == 1)){ // handle negative trig collision sensor RawDistanceToCollision = Tmr16bit250usColSens; Flags.NewRawColDistanceAvailable = 1; Flags.LastColSensor = 0; } INTCONbits.T0IF = 0; } /* CalcDistanceToCollision should be called in main routine minimum every 30ms */ void CalcDistanceToCollision(void){ if (Flags.NewRawColDistanceAvailable){ // calculate new distance value // distance in cm = 4.5 * Distance in 250µs ticks (ca.); // filtered distance = (3 * filtered distance + new distance) / 4; DistanceToCollision = ((DistanceToCollision << 2) - DistanceToCollision // 3 * filtered distance + (RawDistanceToCollision << 2) + (RawDistanceToCollision >> 1)) // + distance in cm >> 2; // / 4 Flags.NewRawColDistanceAvailable = 0; } if(Tmr16bit250usColSens > 600){ // limit max to 150ms Tmr16bit250usColSens = 600; DistanceToCollision = 0; } } Zum Testen der Auswertung ist es praktisch, den Wert in cm auf Display auszugeben. Im Video sieht man den Signalverlauf des vom PIC12F675 zusammengefassten Signal der 3 HC-SR04 und des Reedsensors und eine Testausgabe der Distanzmessung auf LCD. Dann folgt ein Fahrauftrag "60s gerade aus", der bei gemessenen 40cm Abstand vor einem Hindernis vorzeitig beendet wird.
volatile struct { char LastColSensor:1; char NewRawColDistanceAvailable:1; char reserve: 6; } Flags; void interrupt isr(void) { // 250 µs Interrupt, oscillator 32MHz, TMR0 prescaler 1:16 TMR0 -= 125; // Timer Value for next 250µs interrupt // ... // handling collision sensor in 250µs ISR Routine Tmr16bit250usColSens++; if ((CollisionSensor == 1) && (Flags.LastColSensor == 0)){ // handle positive trig collision sensor Tmr16bit250usColSens = 0; Flags.LastColSensor = 1; } if ((CollisionSensor == 0) && (Flags.LastColSensor == 1)){ // handle negative trig collision sensor RawDistanceToCollision = Tmr16bit250usColSens; Flags.NewRawColDistanceAvailable = 1; Flags.LastColSensor = 0; } INTCONbits.T0IF = 0; } /* CalcDistanceToCollision should be called in main routine minimum every 30ms */ void CalcDistanceToCollision(void){ if (Flags.NewRawColDistanceAvailable){ // calculate new distance value // distance in cm = 4.5 * Distance in 250µs ticks (ca.); // filtered distance = (3 * filtered distance + new distance) / 4; DistanceToCollision = ((DistanceToCollision << 2) - DistanceToCollision // 3 * filtered distance + (RawDistanceToCollision << 2) + (RawDistanceToCollision >> 1)) // + distance in cm >> 2; // / 4 Flags.NewRawColDistanceAvailable = 0; } if(Tmr16bit250usColSens > 600){ // limit max to 150ms Tmr16bit250usColSens = 600; DistanceToCollision = 0; } }
Mein Roboter bekommt jetzt einen Distanz- und Kollisionssensor. Eine Prallplatte aus einem Kabelkanaldeckel mit 3 HC-SR04 Sensoren und einem Magneten ist schwenkbar aufgehängt. In der Ruhelage wird ein kleiner Reed-Schließer von dem Magneten geschaltet. Wird bei Kollision die Platte frontal oder seitlich berührt, dann wird der Reed-Kontakt frei und der Sensor schaltet. Der Sensor hat eine Anzeige LED zur Schaltkontrolle, damit man die Position schnell nachjustieren kann. Die drei HC-SR04 werden alle gleichzeitig von einem PIC12F675 getriggert. Der PIC generiert mit Timer0 alle 65ms ein 250µs Puls, einfach Timer0 mit 1:256 Prescaler bei 4MHz internem Takt laufen lassen und Bit 0 des Timer0 auf GP0 ausgegeben. Die drei Echo-Signale der HC-SR04 und das Signal des Reedsensors werden zu einem einzigen Signal verUNDet, und zum haupt-µC gesendet. Bild hier Bild hier
Zitat von witkatz Das aktualisierte Video im ersten Blogbeitrag zeigt den Motortest nach Einschalten und eine Testfahrt mit einem (halbvollen ) Glas Apfelsaft. Sieht gut aus und scheint ja was zu werden. Allerdings passiert mir da zu wenig. Glas ruhig mal randvoll machen Gruß Searcher
Der Roboter wird jetzt mit einem PIC18F4520 mit 32MHz internem Oszillator gesteuert. Für die Ansteuerung der Motortreiber habe ich die PWM Frequenz auf ca. 16kHz parametriert: Code: // CCP/PWM PR2 = 0x7F; // 15625Hz, PWM Freq = OscFreq / 4 / (PR2+1) / TMR2_Prescale CCPR1L = 0; CCP1CONbits.DC1B = 0; // PWM Duty Cycle CCPR2L = 0; CCP2CONbits.DC2B = 0; TRISCbits.TRISC2 = 0; // set CCP1 pin to Output TRISCbits.TRISC6 = 0; // set Reverse_MotorL pin to Output TRISCbits.TRISC1 = 0; // set CCP2 pin to Output TRISCbits.TRISC5 = 0; // set Reverse_MotorR pin to Output T2CONbits.T2CKPS = 0b01; // TMR2 Prescaler 4 T2CONbits.TMR2ON = 1; // TMR2 on CCP1CONbits.CCP1M = 0b1100; // CCP1 PWM Mode CCP1CONbits.P1M = 0b00; // CCP1 Enhanced PWM: Single Output CCP2CONbits.CCP2M = 0b1100; // CCP2 PWM Mode Für sanften Anlauf sorgen 1s Beschleunigungs/Verzögerungsrampen mit 4ms Timervariablen: Code: softtmr_1 = 250; while(softtmr_1){ MotorL_vor(250-softtmr_1); // 1s beschl Rampe } softtmr_1 = 250; while(softtmr_1){ MotorL_vor(softtmr_1); // 1s verz Rampe } Das aktualisierte Video im ersten Blogbeitrag zeigt den Motortest nach Einschalten und eine Testfahrt mit einem (halbvollen ) Glas Apfelsaft.
// CCP/PWM PR2 = 0x7F; // 15625Hz, PWM Freq = OscFreq / 4 / (PR2+1) / TMR2_Prescale CCPR1L = 0; CCP1CONbits.DC1B = 0; // PWM Duty Cycle CCPR2L = 0; CCP2CONbits.DC2B = 0; TRISCbits.TRISC2 = 0; // set CCP1 pin to Output TRISCbits.TRISC6 = 0; // set Reverse_MotorL pin to Output TRISCbits.TRISC1 = 0; // set CCP2 pin to Output TRISCbits.TRISC5 = 0; // set Reverse_MotorR pin to Output T2CONbits.T2CKPS = 0b01; // TMR2 Prescaler 4 T2CONbits.TMR2ON = 1; // TMR2 on CCP1CONbits.CCP1M = 0b1100; // CCP1 PWM Mode CCP1CONbits.P1M = 0b00; // CCP1 Enhanced PWM: Single Output CCP2CONbits.CCP2M = 0b1100; // CCP2 PWM Mode
softtmr_1 = 250; while(softtmr_1){ MotorL_vor(250-softtmr_1); // 1s beschl Rampe } softtmr_1 = 250; while(softtmr_1){ MotorL_vor(softtmr_1); // 1s verz Rampe }