Author: Dipl.-Ing. Wilfried Klaas
Board: Arduino Duemilanove
Ein Kreuzmischer mischt 2 Servosignale, also die Ruderausschläge, zweier Kanäle auf 2 Ausgangskanäle so zusammen, dass der eine Kanal beide Ausgangskanäle proportional steuert, während der 2 Kanal die beiden Ausgangskanäle gegengesetzt steuert. Beispiele für den Einsatz: Kettenfahrzeug mit 2 getrennten Kettenantrieben, Flugzeug mit einem V-Leitwerk, Boot mit 2 Schrauben zur Ruderunterstützung… Damit lässt sich über einen Kreuzküppel der Fernsteuerung das Gefährt einfach handhaben.
Beim Kreuzmischen kann man einfach für den 1. Ausgangskanal die beiden Eingangskanäle addieren, während man für den 2. Kanal den Steuerkanal abzieht. Also z.B. so:
A1 = ESC + STE; A2 = ESC - STE;
Nun sieht man hier ganz leicht, das wir das so einfach nicht machen können. Sagen wir mal, unsere Eingangs- und Ausgangssignale sind im Bereich -128 bis 128. Fahren wir nun voll Kraft voraus, dann hat Kanal A1 einen Wert von 128 und und Kanal A2 ebenfalls einen Wert von 128. Wenn wir jetzt etwas Seitenruder geben, sagen wir mal mit -60 dazu. Dann wird aus A1 = 128 + -60 = 68. OK und A2 = 128 - -60 = 128 + 60 = 188;
Upps, den Wert können wir jetzt nicht mehr darstellen. Denn wir können ja nur bs 128 steuern.
Um das zu umgehen, gibt es, je nach Einsatzzweck verschiedene Möglichkeiten:
* Wir lassen alles so wie beschrieben und ignorieren Werte > 128. Dann wäre im o. Fall A1 = 68 und A2= 128.
* Oder wir halbieren immer jeden Servoweg. Dann wäre A1 = 64 + -30 = 34 und A2= 64 - -30= 94.
Dazwischen sind natürlich noch viele andere Möglichkeiten drin.
Zuerst lesen wir die beiden Empfängerkanäle ein, dann wird gerechnet und zwar je nach Einstellung entweder nach Methode 1 oder 2. Was wir hier (wie bei meinen RC Projekten fast immer) machen, wir bestimmen beim Starten zunächst die Nullpunkte des Empfängers. Dann werden alle Werte durch einen Puffer geschleift. Der enthält 10 Werte und wenn man einen Wert abfragt, wird automatisch der Mittelwert über diese Werte gebildet. (Größter und kleinster fliegen dabei raus.) Dadurch ist man vor kruzen Empfangsstörungen sicher, aber das System reagiert nicht ganzt so schnell. Das kann evt. für Flugzeuge und Hubschrauber nicht gut sein. Da sollte man sich eine andere Strategie einfallen lassen, oder aber einfach den Puffer verkleinern. z.B. auf 4.
UPDATE: 18.10.2012: Ich habe auch diesen Kreuzmischer auf meine RCReceiver Bibliothek umgebaut. Und ich habe den Servoreverse pro Kanal eingebaut. Dazu werden 2 weitere Pins abgefragt.
UPDATE: 18.11.2013: Ich habe beide Programme auf den aktuellen Stand der RCReceiver Bibliothek umgebaut.
#include <debug.h> #include <makros.h> #include <RCReceive.h> #include <Servo.h> /* Kreuzmischer. Kanal 1 ist Geschwindigkeit, Kanal 2 ist Steuerung. Der Empfänger wird vom Pin 2 und 3 gelesen, */ // Hardwareanbindung für Arduino Hardware // Empfängerkanäle const byte PIN_RC_THR = 2; // das ist INT 0 aber Pin 2!!!! const byte PIN_RC_STE = 3; // das ist INT 1 aber Pin 3!!!! // Modus Soft oder Hard const byte PIN_MODE = 4; const byte PIN_REVERS_1 = 7; const byte PIN_REVERS_2 = 8; // Ausgänge const byte SERVO_1 = 9; // PWM Kanal const byte SERVO_2 = 10; // PWM Kanal const byte LED = 13; // LED auf dem Board RCReceive escReceiver; RCReceive servoReceiver; Servo a1, a2; void setup() { // Kanäle auf Ausgang, und dann deaktivieren pinMode(SERVO_1, OUTPUT); pinMode(SERVO_2, OUTPUT); pinMode(LED, OUTPUT); digitalWrite(LED, LOW); // Eingang für RC pinMode(PIN_RC_THR, INPUT_PULLUP); pinMode(PIN_RC_STE, INPUT_PULLUP); escReceiver.attach(PIN_RC_THR); servoReceiver.attach(PIN_RC_STE); // Eingang Modus pinMode(PIN_MODE, INPUT_PULLUP); pinMode(PIN_REVERS_1, INPUT_PULLUP); pinMode(PIN_REVERS_2, INPUT_PULLUP); // Servos definieren a1.attach(SERVO_1); a2.attach(SERVO_2); a1.write(90); a2.write(90); #ifndef __AVR_ATtinyX4__ #ifdef debug Serial.begin(57600); Serial.flush(); Serial.println("RC V-mixer"); delay(100); #endif #endif } void loop() { // Aktuellen RC-Wert Gas lesen escReceiver.poll(); // und gleich auch für's Steuerservo servoReceiver.poll(); dbgOut("E:"); dbgOut(escReceiver.getLastRCValue()); dbgOut(",S:"); dbgOutLn(servoReceiver.getLastRCValue()); if (escReceiver.hasError() || servoReceiver.hasError()) { a1.write(90); a2.write(90); } else if (escReceiver.hasNP() && servoReceiver.hasNP()) { doWork(); } #ifdef debug // delay(100); #endif } /* Modus Soft berechnung, Werte werden jeweils halbiert und dann addiert. */ void doWork() { // Werte holen int escValue = escReceiver.getValue(); int servoValue = servoReceiver.getValue(); dbgOut("E:"); dbgOut(escValue); dbgOut(",S:"); dbgOutLn(servoValue); // Nullpunkte festlegen escValue = escValue - escReceiver.getNP(); servoValue = servoValue - servoReceiver.getNP(); // Hier wird zwischen hard und soft unterschieden. if (digitalRead(PIN_MODE) == 0) { // jetzt Wertebereich verkleinern escValue = escValue / 2; servoValue = servoValue / 2; } // die eigentliche Berechnung int servo1 = escValue + servoValue; int servo2 = escValue - servoValue; // Und damit das nicht aus dem Ruder läuft, etwas begrenzen servo1 = constrain(servo1, -128, 128); servo2 = constrain(servo2, -128, 128); if (digitalRead(PIN_REVERS_1) == 0) { // Servokanal 1 umkehren servo1 = servo1 * -1; } if (digitalRead(PIN_REVERS_2) == 0) { // Servokanal 1 umkehren servo2 = servo2 * -1; } // Nullpunkte wieder hinzu servo1 = servo1 + 128; servo2 = servo2 + 128; // Auf den Servo umsetzen servo1 = map(servo1,0, 255,0,180); servo2 = map(servo2,0, 255,0,180); // und einstellen a1.write(servo1); a2.write(servo2); }
Vielleicht ist euch aufgefallen, daß gerade beim Kreuzmischer die Servos arg zittern. Das liegt weder am Arduino noch an der Programmierung, naja, vielleicht doch, aber nicht so offentsichtlich. Denn wie wir bereits bei der RC Bibliothek gesehen haben, stören sich die Servo Bibliothek und der pulseIn()
gegenseitig. Linderung, wenn auch keine Abhilfe, schafft da, das ganze Auswerten des Empfängers ebenfalls mit Intterupts zu machen. Hier das entsprechende Programm.
#include <debug.h> #include <makros.h> #include <RCReceive.h> #include <Servo.h> /* Kreuzmischer. Kanal 1 ist Geschwindigkeit, Kanal 2 ist Steuerung. Der Empfänger wird vom Pin 2 und 3 gelesen, */ // Hardwareanbindung für Arduino Hardware // Empfängerkanäle const byte PIN_RC_THR = 2; // das ist INT 0 aber Pin 2!!!! const byte PIN_RC_STE = 3; // das ist INT 1 aber Pin 3!!!! // Modus Soft oder Hard const byte PIN_MODE = 4; const byte PIN_REVERS_1 = 7; const byte PIN_REVERS_2 = 8; // Ausgänge const byte SERVO_1 = 9; // PWM Kanal const byte SERVO_2 = 10; // PWM Kanal const byte LED = 13; // LED auf dem Board RCReceive escReceiver; RCReceive servoReceiver; Servo a1, a2; void setup() { // Kanäle auf Ausgang, und dann deaktivieren pinMode(SERVO_1, OUTPUT); pinMode(SERVO_2, OUTPUT); pinMode(LED, OUTPUT); digitalWrite(LED, LOW); // Eingang für RC pinMode(PIN_RC_THR, INPUT_PULLUP); pinMode(PIN_RC_STE, INPUT_PULLUP); escReceiver.attachInt(PIN_RC_THR); servoReceiver.attachInt(PIN_RC_STE); // Eingang Modus pinMode(PIN_MODE, INPUT_PULLUP); pinMode(PIN_REVERS_1, INPUT_PULLUP); pinMode(PIN_REVERS_2, INPUT_PULLUP); // Servos definieren a1.attach(SERVO_1); a2.attach(SERVO_2); a1.write(90); a2.write(90); #ifndef __AVR_ATtinyX4__ #ifdef debug Serial.begin(57600); Serial.flush(); Serial.println("RC V-mixer"); delay(100); #endif #endif } void loop() { dbgOut("E:"); dbgOut(escReciver.getLastRCValue()); dbgOut(",S:"); dbgOutLn(servoReciver.getLastRCValue()); if (escReceiver.hasError() || servoReceiver.hasError()) { a1.write(90); a2.write(90); } else if (escReceiver.hasNP() && servoReceiver.hasNP()) { doWork(); } else { int value = escReceiver.getValue(); dbgOut("E:"); dbgOut(value); value = servoReceiver.getValue(); dbgOut("S:"); dbgOut(value); dbgOutLn(", no NP"); } #ifdef debug delay(100); #endif } /* Modus Soft berechnung, Werte werden jeweils halbiert und dann addiert. */ void doWork() { // Werte holen int escValue = escReceiver.getValue(); int servoValue = servoReceiver.getValue(); dbgOut("E:"); dbgOut(escValue); dbgOut(",S:"); dbgOutLn(servoValue); // Nullpunkte festlegen escValue = escValue - escReceiver.getNP(); servoValue = servoValue - servoReceiver.getNP(); // Hier wird zwischen hard und soft unterschieden. if (digitalRead(PIN_MODE) == 0) { // jetzt Wertebereich verkleinern escValue = escValue / 2; servoValue = servoValue / 2; } // die eigentliche Berechnung int servo1 = escValue + servoValue; int servo2 = escValue - servoValue; // Und damit das nicht aus dem Ruder läuft, etwas begrenzen servo1 = constrain(servo1, -128, 128); servo2 = constrain(servo2, -128, 128); if (digitalRead(PIN_REVERS_1) == 0) { // Servokanal 1 umkehren servo1 = servo1 * -1; } if (digitalRead(PIN_REVERS_2) == 0) { // Servokanal 1 umkehren servo2 = servo2 * -1; } // Nullpunkte wieder hinzu servo1 = servo1 + 128; servo2 = servo2 + 128; // Auf den Servo umsetzen servo1 = map(servo1,0, 255,0,180); servo2 = map(servo2,0, 255,0,180); // und einstellen a1.write(servo1); a2.write(servo2); }