Inhaltsverzeichnis

Kreuzmischer

Author: Dipl.-Ing. Wilfried Klaas

Board: Arduino Duemilanove

Was ist ein Kreuzmischer?

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.

Und wie funktioniert das?

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.

Programm

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.

Kreuzmischer.ino
#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);
}

Programm mit Interrupt

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.

Kreuzmischer_int.ino
#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);
}