Steuerung per Gamecontroller

Wie geht denn das?

In einem vorherigen Post habe ich bereits den Anschluss des USB-Host-Shields an einen Arduino Mega beschrieben. Ziel war es einen XBOX360-Controller zum Steuern des Roboters zu verwenden. Die Usb-Host-Library bringt einige Module für die Kommunikation mit verschiedenen USB-Geräten mit. Darunter auch eine Library für die XBOX-Controller. Die ausgelesenen Steuerdaten eines Steuerkreuzes werden dann mit einem APC220-Funkmodul an den Roboter geschickt und dort an den Motorcontroller weitergereicht.

Details bitte

Die Host-Library bietet die Möglichkeit sämtliche Steuerkreuze und Buttons des Controllers auszulesen. Die beiden analogen Steuerkreuze geben hierbei einen “signed 16Bit Integer” für die X- & Y-Achsen aus. Das bedeutet das der mögliche Wert je Achse zwischen −32.768 und 32.767 liegen kann. Liegt das Steuerkreuz in Ruhezustand ist der Wert 0. Für meine Demo beschränke ich mich auf das Steuerkreuz welches sich unten-rechts auch dem Controller befindet. Die anderen Funktionen wie Buttons etc. werden hier nicht gebraucht.

Hier ein einfacher Beispielcode zum Auslesen des rechten Steuerkreuzes:

Nochmal ein wichtiger Hinweis: Ich müsst unbedingt(!) eine Externe Stromversorgung an den Arduino anschliessen. Der USB-Port alleine kann das ganze nicht mit Strom versorgen. Ihr bekommt dann einfach keine Daten. Ich habe mich da dumm und dusselig gesucht bis ich das Problem erkannt hatte.

 

Wenn ihr alles richtig angeschlossen und das Sketch auf den Arduino geladen habt, könnt ihr die Serielle Konsole öffnen. Es sollten dann die Werte des Steuerkreuzes ausgegeben werden

Controller Ausgabe

Wie auf dem Screenshot zu sehen ist haben wir nun Werte – und damit auch gleich das erste Problem: die Werte dort Oben bekomme ich wenn ich den Stick loslasse. Eigentlich erwartet man das beide Werte wieder auf 0 gehen, das passiert allerdings nicht da die Steuerkreuze ziemlich empflindlich sind. Ein leichtes antippen reicht um die Werte zu verändern…

Um das zu verhindern und einen sauberen Nullpunkt zu bekommen setzt man einen DEAD CENTER, also einen Bereich rund um den Nullpunkt welcher immer 0 ausgibt wenn sich die Werte in diesem Bereich befinden. Ich habe für mich einen Bereich von 7000 genommen. Das bedeutet: Wenn der Wert einer Achse zwischen -7000 und 7000 liegt gebe ich als Wert 0 aus.

Erweiterter Code mit “Dead Center”:

Nun habe ich ordentliche Werte. Weiter gehts :)

Die Funkübertragung

Um mit den Werten etwas anfangen zu können müssen die irgendwie zum Roboter übertragen werden. Ein Kabel ist keine Option. Bleibt also Funk.

Ich hatte noch ein Paar APC220-Funkmodule rumliegen. Die Module senden serielle Daten auf 434MHz und sind ziemlich leicht an einen Arduino anzubinden das sie über TTL-UART mit 5V  angeprochen werden.

APC220 Module

APC220 Module

Überlegungen…

Wie bekommt man nun zwei variable Datenwerte (X- & Y-Achse) über die serielle Schnittstelle und die Funkmodule zum Roboter? Und wie erkennt man ob die Daten auch valide sind? Und, viel wichtiger: Wie sichere ich den Robot so ab das er bei falschen Daten (z.b. Funkstörungen) keinen Amoklauf veranstaltet?

Teil 1: Die Übertragung

Zum steuern des Roboters benötigt man zwei Werte: Drive & Turn. Der erste gibt an wie schnell und in welche Richtung der Roboter fährt, der zweite ob er sich nach links oder rechts drehen soll. Der benutze Sabertooth Motorcontroller benutzt hierfür jeweils Werte von -127 bis +127. Das sind 8 Datenbits (1 Byte). Wir haben allerdings Werte zwischen −32.768 und +32.767, also 16 Datenbits (2 Bytes). Für die Übertragung der beiden Achsen benötigen wir also insgesamt 32 Datenbits (4 Bytes).

Da die Funkstrecke möglichst Störungsfrei auch über größere Entfernungen funktioniert, habe ich die Funkmodule auf eine Baudrate von 9600 Bits pro Sekunde eingestellt. Theoretisch können also 9600 / 8 = 1200 Byte pro Sekunde übertragen werden. Das wir 2 Werte á 2 Byte übertragen, könnten(!) also 9600 / (8 Byte * (2 Pakete * 2 Byte)) = 300 Pakete pro Sekunde gesendet werden.

Theoretisch alles schön, praktisch blödsinn.

  1. Wozu 16 Bit Werte senden wenn der Motorcontroller damit garnichts anfangen kann
  2. Was ist mit der Fehlersicherheit? Nur ein falsches Bit und der Roboter fährt mit voller Kraft gegen die Hauswand.
  3. Wie erkennt man in welcher Reihenfolge die Daten ankommen? Kommt Drive immer vor Turn an?

 

Ok, meine Entscheidung ist gefallen: Wir basteln ein kleines Übertragunsprotokoll um die 3 Punkte zu umschiffen. Pro Datenpaket (Steueranweisung) wird ein 5-Byte Datenpaket übertragen.

Es beinhaltet einen 2 Byte Prefix zum erkennen des Paketes auf Empfängerseite, jeweils 1 Byte für die X- und die Y-Achse und eine einfache Checksumme um fehlerhafte Datenpakete zu erkennen.

Beispielpaket
Byte 1 2 3 4 5
0xAA 0xFF 0x10 0x20 0xA7
  • Byte 1+2: Prefix (immer 0xAA & 0xFF)
  • Byte 3: X-Achse (zwischen 0 und 254)
  • Byte 4: Y-Achse (zwischen 0 und 254)
  • Byte 5: Checksumme (XOR aus Byte 3+4)

Hier der komplette Sendercode:

 Teil 2: Der Empfänger

Der Empfängercode macht eigentlich nich viel. Ankommende Daten vom Funkmodul annehmen, Datenpaket einlesen und nach validierung die Daten an den Motorcontroller senden.

 

 

Das war es auch schon.

Bei Fragen: Fragen!!

Einfach einen Kommentar eintragen und ich versuche zu Antworten :)

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">