Endlich lebt der Bär und spricht! Er erkennt auch, dass man sich ihm nähert und fäng automatisch mit der Beratung an 🙂
Interrupts, Timer und Compare-Match um bis zu 12 Servos zu steuern
Hier ist ein kurzer Ausschnitt aus unserem Code des Microkontrollers mit dem wir 5 Servos parallel steuern. Wir wollen eine hohe Bewegungsgenauigkeit in der Servosteuerung erreichen und verwenden daher keine Softwarelösung mit delays. So mit ist ein Timer mit Interrupts und Compare-Match unser einziger Ausweg. Mit den zwei Registern OCR1A und OCR1B können wir bis zu 12 Servos parallel steuern. In diesem Code-Beispiel werden nur 5 Motoren angesprochen und nur das Register OCRA wird benutzt.
/* * myinterrupt.c * * Created on: 26.05.2012 */ #include <avr/io.h> #include <avr/interrupt.h> // ATmega8 // ======= // // PWM output to the servo motor utilizes Timer/Counter1 in 8-bit mode. // Output to the motor is assigned as follows: // // OC1A (PB1) - Servo PWM output direction A // OC1B (PB2) - Servo PWM output direction B // Values for up to six PWM channels. // On each channel, the special value 0 can be used to switch the signal off. // volatile uint8_t pwm_value[6]; extern volatile uint16_t pwm_value[6]; static volatile uint8_t resetcounter = 1; // For ATmega 8 void servoinit() { // PSR10 -> 1 reset prescaler timer0 and timer 1, no prescale for timer SFIOR = (1 << PSR10); // Stop the timer1 TCCR1B &=(~((1<<CS12)|(1<<CS11)|(1<<CS10))); // Enable PB1/OC1A and PB2/OC1B as outputs. // Enable PB3/OC2 DDRB |= ((1<<DDB1) | (1<<DDB2) | (1<<DDB3) | (1<<DDB4) | (1<<DDB5)); // Set PB1..5 to low. // Set PB1 oder PB2 not high, its very useful PORTB &= ~((1<<PB1) | (1<<PB2) | (1<<PB3) | (1<<PB4) | (1<<PB5)); // Disable timer 1 and enable timer 2. TCCR1A = (0<<COM1A1) | (0<<COM1A0) | // Clear OC1A, OC1B on compare match on up-count. (0<<COM1B1) | (0<<COM1B0) | // (non-inverting mode) (1<<WGM11) | (0<<WGM10); // Fast PWM - waveform generation mode 14. dataScheet page 97 // Top ICR1, update of OCR1x BOTTOM // Set clock select bits to start timer. TCCR1B = (0<<ICNC1) | (0<<ICES1) | // Input on ICP1 disabled, not receiving ICP Event from ICP pin 1 (1<<WGM13) | (1<<WGM12); // Select waveform mode 14. //| (0<<CS12) | (0<<CS11) | (1<<CS10); // Timer1 16bits prescaling 256 clkIO/256. From DataScheet Page 99 // Set TOP of Timer1 to 1249 for 50Hz output Top + 1 = clkIO / (Prescale * PWM_Output_frequenz). // get 245Hz output Top + 1 = 16000000 / 256 * 245 interrupts occur at a frequency of 244.14Hz //ICR1 = 255; ICR1 = 0xFFFF; // output compare match appears in 4ms , 250Hz // Reset count and compare registers. // TCNT1 16 Bit Timer TCNT2 8 Bit Timer TCNT1 = 0; // f_OCnA * 2 * N * ( 1 + OCRnA ) = f_clkIO // N = 8 (Fast PWM prescale, set by WGMxx ) //clkIO 16MHz/256 = 62500 Hz ,after prescaling, per instruction is 16us // one Cycle of counter steps with 16bit counter and ICR1 = 255 //OCR1A = 92; // should be 95 for 1.52ms , Set Top of Compare Match A // initializing the fire motors // pro step 0,0625us pwm_value[0] = 23552; // not used pwm_value[1] = 23552; pwm_value[2] = 23552; pwm_value[3] = 23552; pwm_value[4] = 23552; pwm_value[5] = 23552; // Timer1 Output Compare Match A Interrupt Enable : DataScheet Page 100 // Timer1 Output Compare Match B Interrupt Enable // Timer2 Output Compare Match Interrupt Enable TIMSK = (1 << OCIE1A) | (1 << TOIE1) ; // Enable Interrupts : Restore interrupts sei(); // Start the Timer 1 // Timer1 16bits prescaling No. TCCR1B |= (1<<CS10); // Select waveform mode 5. CS11 and CS10 are equal to 0 } // The timer compare interrupt ends the current PWM pulse ISR(TIMER1_COMPA_vect) { // clear all output in compare match A // PB1 .. 5 to low // PORTB &= ~(2+4+8+16+32); // set all 5 PWM outputs to low PORTB &= ~((1<<PB1) | (1<<PB2) | (1<<PB3) | (1<<PB4) | (1<<PB5)); } // The timer overflow interrupt starts a PWM pulse, // on each interrupt, the port == resetcounter will be set to high ISR(TIMER1_OVF_vect) { uint16_t value = pwm_value[resetcounter]; // Turn on the output PWM Port /** * because of the time delay. * we need to switch 1 to PB5 instead of PB1, so that pwm_value(1) * is used for PB1 otherwise it is used for PB5 * It is very trigg here. */ if (value>0) { switch (resetcounter) { case 1: PORTB |= (1<<PB5); break; case 2: PORTB |= (1<<PB1); break; case 3: PORTB |= (1<<PB2); break; case 4: PORTB |= (1<<PB3); break; case 5: PORTB |= (1<<PB4); break; } OCR1A=value; } // increase the reset counter resetcounter++; if (resetcounter >= 6) { // Do not change the 6, even when you need less than 6 channels! // reset counter to 0 we use counter start from 1 instead of 0 resetcounter = 1; } }
Allerhand Feinarbeit
Eigentlich sollte schon alles laufen – aber wie so oft, steckt der Teufel im Detail. Nachdem mittlerweile alle Fragen im Wav-Format eingesprochen (und kräftig mit Kompressoren, Noise-Gates und Pitch) nachbearbeitet wurden, machte speziell die sequentielle Steuerung des Frage-Antwort-Frage-Ablaufs Schwierigkeiten. Zudem zickt die Kinect noch ab und an, wenn es darum geht, die Sprache des Users zu erfassen.
Auch der Einbau des kompletten Gestänges mit den Motoren gestaltete sich schwieriger als angenommen. Auch wenn es uns selbst weh tat: Der Kopf musste zwangsläufig fast komplett abgeschnitten werden, damit das neue Skelett entsprechend eingepasst werden konnte.
Das noch letzte Problem neben allerhand Feinarbeit: Lassen sich die Motoren parallel ansprechen – und falls ja: Wie kriegen wir das (schmerzfrei) hin?
Und er schuf den Bären …
Das Gestell für den interaktiven Wahl-Bär-Rater in Aktion:
Erste Bewegungen
Hier die ersten zaghaften Bewegungen unseres Zombies:
Vom Friedhof der Kuscheltiere
Der Bär lebt! Mit einer Konstruktion aus Aluminumstangen, Schellen und Servomotoren haben wir es tatsächlich geschafft, dem Bären Leben einzuhauchen. Als Herz dient wie geplant ein Steckbrett mit einem Atmega8-Microcontroller, über den sich nun Arme, Kopf und Schnauze ansteuern lassen. Problem dabei: Der Bär macht das noch ziemlich sinnfrei, nachdem noch keine Logik existiert, wann (und vor allem: wieso?) er sich bewegen soll.
Deswegen steht nun vor allem auf dem Plan, die bereits programmierte Kinect-Schnittstelle (ein Programm das “Ja”, “Nein” und “Weiter” versteht) mit dem Atmega8-Microcontroller zu verbinden. Das Ziel: Wenn Sprache vom Laptop über den Lautsprecher (wiederum im Inneren des Bären) ausgegeben wird, soll der Bär etwas Gestik rüberbringen. Im Informatikersprech: Eine Schnittstelle von Laptop (C#) zum Microcontroller (C) inklusive Protokoll muss her.
Von Bären und Sensoren
Essenzielle Überlegung: Welche Hardware brauchen wir für das Projekt? Der Bär soll Sprache verstehen und sich bewegen, wenn er selbst spricht. Für die Spracherkennung war der Einsatz der Kinect von Microsoft von Anfang an fest. Im Laufe der Praktikumsvorträge haben wir zudem gelernt, einfache Befehle für Servomotoren über einen Atmega8-Microcontroller zu programmieren, die die Gestik des Bären beim Sprechen (hoffentlich) simulieren können. Als Vermittlerstelle zwischen Microcontroller und Kinect ist zudem ein Laptop nötig sowie ein Lautsprecher, der die Audioausgabe des Bären übernimmt. Darüber hinaus brauchen wir ein Gerüst, über das die Servomotoren Arme, Kopf und eventuell die Schnauze steuern können. Nachteil dabei: Der arme Teddybär musste ausgeweidet werden…
Wohin damit?
Berechtigte Frage: Wer braucht sowas? Nachdem wir uns diese Frage selbst gestellt hatten, haben wir uns einfach mal folgendes Szenario überlegt:
– Auf einer Wahlveranstaltung bewegt sich ein Besucher in die Nähe des Wahl-Bär-Raters, woraufhin ihn dieser anspricht.
– Der Besucher antwortet auf die Frage, ob er sich schon für eine Partei entschieden habe mit „Nein“ und bejaht anschließend, dass ihm der Wahl-Bär-Rater ein paar Fragen stellen dürfe.
– Auf die Fragen zu politischen Inhalten antwortet der Besucher je nach Meinung mit “Ja”, “Nein” oder “Egal”.
– Nachdem alle Fragen gestellt wurden, gibt der Wahl-Bär-Rater eine Empfehlung, welche Partei am bestem zum Besucher passt.
Wie kam’s zur Idee?
Wir sind zwar weder Politiker noch in der Kuschelbärenszene aktiv, finden aber die Idee des Wahl-O-Mats ganz wunderbar. Und nachdem in der Aufgabenstellung zum Projektentwicklung ausdrücklich die Verwendung der Microsoft Kinect erlaubt wurde, fanden wir eine Kombination aus Spracherkennung, harter Realpolitik und einem knuffigen Bären interessant.
Was passiert hier eigentlich?
Aller Anfang ist schwer, deswegen machen wir’s kurz und schmerzlos: Wir sind Team 1 des Praktikums “Building Interactive Objects” an der LMU München und entwickeln einen interaktiven Wahlberater. Damit’s nicht ganz so trocken wird (Politik an sich ist ja schon schwerfällig genug), lassen wir einfach einen Teddybär in die Rolle des Wahlberaters – den “Wahl-Bär-Rater” (*hüstel*) – schlüpfen.
You must be logged in to post a comment.