Pages: [1]   Go Down
Author Topic: RC-Heli über MX-12 mit X-Box Controller steuern  (Read 1577 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hallo,

ich möchte meinen RC-Modellhelikopter über meine Fernsteuerung (Graupner MX-12) mit meinem X-Box Controller steuern können.


|X-Box Controller| --USB--> |PC| --COM--> |Arduino Uno Rev.3| --PPM--> |MX-12| --Funk--> |RC-Heli|


Die Steuerung mit dem X-Box Controller funktioniert sogar teilweise schon aber leider sehr Zeitverzögert (ca. 0,5 sec).  smiley-confuse Der X-Box Controller ist per USB an meinem PC angeschlossen. Ein Vb.net Programm liest die Achsenpositionen des Controllers aus und sende diese an den Arduino Uno per serieller Schnittstelle. Der Arduino Uno erzeugt dann ein PPM-Signal (Puls-Pausen-Modulation), welches von der Fernsteuerung verarbeitet werden kann. Dieses PPM-Signal wird über die Lehrer-Schüler-Schnittstelle der Fernsteuerung übermittelt. Die Fernsteuerung steuert dann den RC-Heli.

Hier mein Vb.net Code zum Auslesen und Senden der X-Box Controller Achsen:

Code:
Imports System.IO.Ports     'COM-Port
Imports System

Public Class Form1

    Dim JInfo As JOYINFO
    Public Shared indata As String

    Public s As New SerialPort
    Private Declare Function joyGetPos Lib "winmm.dll" (ByVal uJoyID As Integer, ByRef pji As JOYINFO) As Integer

    Private Structure JOYINFO

        Dim X As Integer
        Dim Y As Integer
        Dim Z As Integer
        Dim Buttons As Integer

    End Structure

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        s.Close()
        s.PortName = "com3"                         'will need to change to your port number
        s.BaudRate = 9600
        s.DataBits = 8
        s.Parity = Parity.None
        s.StopBits = StopBits.One
        s.Handshake = Handshake.None
        s.Encoding = System.Text.Encoding.Default   'very important!
        s.Open()
        s.RtsEnable = True

        AddHandler s.DataReceived, AddressOf DataReceviedHandler

    End Sub

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        Dim x, y, z, b As Integer

        joyGetPos(0, JInfo)      'Joystick 0

        b = Str$(JInfo.Buttons)

        x = Math.Round(Str$(JInfo.X) / 65525 * 255, 0)
        y = Math.Round(Str$(JInfo.Y) / 65525 * 255, 0)
        z = Math.Round(Str$(JInfo.Z) / 65525 * 255, 0)

        TextBox1.Text = b
        TextBox2.Text = x
        TextBox3.Text = y
        TextBox4.Text = z

        s.Write(Chr(x))
        's.Write(Chr(y))
        's.Write(Chr(z))

    End Sub

    Private Shared Sub DataReceviedHandler(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
        Dim sp As SerialPort = CType(sender, SerialPort)
        indata = sp.ReadLine

    End Sub
End Class

Hier mein Arduino-Code zur Erzeugung des PPM-Signals:

Code:
int pin = 3;  // PPM INPUT PIN
unsigned int duration1 = 1000;
unsigned int duration2 = 1000;
unsigned int duration3 = 1000;

void setup()
{
  Serial.begin(9600);
  pinMode(pin, OUTPUT);
}

void loop()
{       
digitalWrite(3, LOW);
delayMicroseconds(350);

digitalWrite(3, HIGH);
delayMicroseconds(1000);
digitalWrite(3, LOW);
delayMicroseconds(400);

digitalWrite(3, HIGH);
delayMicroseconds(duration1);
digitalWrite(3, LOW);
delayMicroseconds(400);

digitalWrite(3, HIGH);
delayMicroseconds(1000);
digitalWrite(3, LOW);
delayMicroseconds(400);

digitalWrite(3, HIGH);
delayMicroseconds(1000);
digitalWrite(3, LOW);
delayMicroseconds(400);

digitalWrite(3, HIGH);
delayMicroseconds(1000);
digitalWrite(3, LOW);
delayMicroseconds(400);

digitalWrite(3, HIGH);
delayMicroseconds(1000);
digitalWrite(3, LOW);
delayMicroseconds(400);

digitalWrite(3, HIGH);
delayMicroseconds(20000 - (400*7) - (1000*5) - duration1);

}

void serialEvent(){
duration1 = map(Serial.read(), 0, 255, 700, 1500);
}

Habt ihr eine Idee wie ich die oben beschriebene Zeitverzögerung minimieren kann?

Danke vorab für eure Ideen und schönen Gruß  smiley,

Christoph
Logged

Forum Moderator
BZ (I)
Offline Offline
Brattain Member
*****
Karma: 271
Posts: 21879
+39 349 2158303
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ich dachte die PPM impulse von Fernsteuerungen dauern von 1000 bis 2000µS und nicht von 700 bis 1500µS.
Der Arduino-Sketch verzögert gar nichts, da er keine Eingabe von der seriellen Schnittstelle bekommt und nur konstante Werte sendet.
Vieleicht solltest Du den richtigen Setch uns geben.
Grüße Uwe
Logged

AREA COLOGNE
Offline Offline
Edison Member
*
Karma: 21
Posts: 1146
I am 1 of 10 who understands binary
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Uwe der bekommt doch was von der Seriellen Schnittstelle und die Manipuliert nur den Wert Duration1. Ich weiss nur nicht wie das Aussieht mit serial.Event wenn er inmitten eines Delays hängt unterbricht er beartbeitet die Daten und arbeitet dann weiter, oder ignoriert er das bis zu nächsten durchlauf?
Logged

So ist das Leben:
Manchmal bis du das Denkmal, manchmal die Taube!

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Zu uwefed: Ich hab das PPM-Signal der Fernsteuerung mit einem anderen Arduino-Sketch ausgelesen und es demnach reproduziert. Ich werde aber morgen mal probieren ob es auch mit einer HIGH-Signalverzögerung von 1 - 2 ms funktioniert.
Zu volvodani: Das oben aufgeführten Arduino-Sketch mit der "Serial.available" Methode in der Loop() Routine führt zum gleichen Problem.  smiley-confuse

Code:
if (Serial.available() > 0) {

        }

Grüße

Christoph
Logged

Forum Moderator
BZ (I)
Offline Offline
Brattain Member
*****
Karma: 271
Posts: 21879
+39 349 2158303
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

@Deluxee
In dem Sketch den Du uns gegeben hast, wird die Funkton void serialEvent(){ nie angesprungen und somit sendet der Arduino nie die Daten des Controllers -> PC ->  an die Fernsteuerung.
Grüße Uwe
Logged

AREA COLOGNE
Offline Offline
Edison Member
*
Karma: 21
Posts: 1146
I am 1 of 10 who understands binary
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Auszug aus der HardwareSerial.cpp


Code:
void serialEventRun(void)
{
#ifdef serialEvent_implemented
  if (Serial.available()) serialEvent();
#endif
#ifdef serialEvent1_implemented
  if (Serial1.available()) serialEvent1();
#endif
#ifdef serialEvent2_implemented
  if (Serial2.available()) serialEvent2();
#endif
#ifdef serialEvent3_implemented
  if (Serial3.available()) serialEvent3();
#endif
}

Und dann der aufruf aus der Main.h

Code:
#include <Arduino.h>

int main(void)
{
init();

#if defined(USBCON)
USB.attach();
#endif

setup();
   
for (;;) {
loop();
if (serialEventRun) serialEventRun();
}
       
return 0;
}
Das wird "nach der loop" ausgeführt, damit habe ich dann auch meine Antwort auf meine Frage smiley-eek-blue.
Was mir aber so nach dem durchschauen auffällt du verlierst ja 30ms auch im Ardu durch warten. Bist du dir sicher das es keinen "Zeitverlust" zwischen Controller und Ardu Eingabe
« Last Edit: April 27, 2012, 03:09:27 am by volvodani » Logged

So ist das Leben:
Manchmal bis du das Denkmal, manchmal die Taube!

Offline Offline
Jr. Member
**
Karma: 0
Posts: 73
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ich muß doch mal ganz blöd Fragen warum das ganze so kompliziert gemacht wird?
Wozu überhaupt der PC dazwischen?
Warum wandelst Du nicht mit dem Arduino direkt das X-Box Protokoll in ein PPM-Summensignal mit entsprechender Kanalanzahl um.
Das wäre doch einfacher, sicherer und garantiert um etliches schneller.

LG Lena
Logged

„Zwei Dinge sind unendlich, das Universum und die menschliche Dummheit, aber bei dem Universum bin ich mir noch nicht ganz sicher.“

Albert Einstein

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Zu uwefed: Die "serialEventRun" Routine ist quasi ein Programm-Interrupt und braucht keinen Sprungbefehl.
Zu volvodani: Die Zeitverzögerung ist auf jeden Fall größer als 30 ms. Ich schätze es sind ca. 500 ms bis der Servo am RC-Heli auf eine Bewegung am X-Box Controller reagiert.
Zu Lena: Die indirekt Steuerung über den PC bietet mehr Möglichkeiten z.B. bezogen auf die Weiterverarbeitung oder Auswertung der Signale. Ich werde aber mal versuchen die Variable "duration1" über ein Poti direkt zu ändern.

Grüße,

Christoph
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Das Problem mit der Verzögerung hab ich gelöst!  smiley-grin Problem war, dass der vb.net Timer nicht jede Millisekunde das Signal gesendet hat. Zudem musste ich die "if (Serial.available() > 0)" Methode benutzen, da ansonsten der Stellwert sich immer wieder auf 1000 zurückgesetzt hat. Zudem hab ich auch noch die Stellgrößen duration 2 und 3 übertragen!

Grüße

Christoph

Hier der arduino-Sketch:

Code:
int pin = 3;  // PPM INPUT PIN
unsigned int duration1 = 1000;
unsigned int duration2 = 1000;
unsigned int duration3 = 1000;
int lowduration = 350;

void setup()
{
  Serial.begin(115200);
  pinMode(pin, OUTPUT);
}

void loop()
{        
 if (Serial.available() > 0) {
  
   if (Serial.read() == 255){
        duration1 = map(Serial.read(), 0, 250, 700, 1500);
        duration2 = map(Serial.read(), 0, 250, 700, 1500);
        duration3 = map(Serial.read(), 0, 250, 700, 1500);
    }
  }
  
digitalWrite(3, LOW);
delayMicroseconds(lowduration);

digitalWrite(3, HIGH);
delayMicroseconds(duration3);
digitalWrite(3, LOW);
delayMicroseconds(lowduration);

digitalWrite(3, HIGH);
delayMicroseconds(duration1);
digitalWrite(3, LOW);
delayMicroseconds(lowduration);

digitalWrite(3, HIGH);
delayMicroseconds(duration2);
digitalWrite(3, LOW);
delayMicroseconds(lowduration);

digitalWrite(3, HIGH);
delayMicroseconds(1000);
digitalWrite(3, LOW);
delayMicroseconds(lowduration);

digitalWrite(3, HIGH);
delayMicroseconds(1000);
digitalWrite(3, LOW);
delayMicroseconds(lowduration);

digitalWrite(3, HIGH);
delayMicroseconds(1000);
digitalWrite(3, LOW);
delayMicroseconds(lowduration);

digitalWrite(3, HIGH);
delayMicroseconds(20000 - (lowduration*7) - (1000*3) - duration1 - duration2 - duration3);
}

Und hier der VB.code

Code:
Imports System.IO.Ports     'COM-Port
Imports System

Public Class Form1

    Dim JInfo As JOYINFO
    Public Shared indata As String

    Public s As New SerialPort
    Private Declare Function joyGetPos Lib "winmm.dll" (ByVal uJoyID As Integer, ByRef pji As JOYINFO) As Integer

    Private Structure JOYINFO

        Dim X As Integer
        Dim Y As Integer
        Dim Z As Integer
        Dim Buttons As Integer

    End Structure

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        s.Close()
        s.PortName = "com3"                         'will need to change to your port number
        s.BaudRate = 115200
        s.DataBits = 8
        s.Parity = Parity.None
        s.StopBits = StopBits.One
        s.Handshake = Handshake.None
        s.Encoding = System.Text.Encoding.Default   'very important!
        s.Open()
        s.RtsEnable = True

        AddHandler s.DataReceived, AddressOf DataReceviedHandler

    End Sub

    Private Shared Sub DataReceviedHandler(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
        Dim sp As SerialPort = CType(sender, SerialPort)
        indata = sp.ReadLine

    End Sub

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        Dim x, y, z As Integer

        joyGetPos(0, JInfo)      'Joystick 0

        x = Math.Round(Str$(JInfo.X) / 65525 * 250, 0)
        y = Math.Round(Str$(JInfo.Y) / 65525 * 250, 0)
        z = Math.Round(Str$(JInfo.Z) / 65525 * 250, 0)


        TextBox1.Text = x + y + z

        TextBox2.Text = x
        TextBox3.Text = y
        TextBox4.Text = z
  
    End Sub

    Private Sub TextBox1_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged
        Dim x, y, z As Integer

        joyGetPos(0, JInfo)      'Joystick 0

        x = Math.Round(Str$(JInfo.X) / 65525 * 250, 0)
        y = Math.Round(Str$(JInfo.Y) / 65525 * 250, 0)
        z = Math.Round(Str$(JInfo.Z) / 65525 * 250, 0)

        s.Write(Chr(255) & Chr(x) & Chr(y) & Chr(z))

    End Sub
« Last Edit: April 27, 2012, 11:27:23 am by Deluxee » Logged

Pages: [1]   Go Up
Jump to: