Go Down

Topic: RC-Heli über MX-12 mit X-Box Controller steuern (Read 1 time) previous topic - next topic

Deluxee

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).  :~ 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: [Select]

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: [Select]

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ß  :),

Christoph

uwefed

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

volvodani

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?
So ist das Leben:
Manchmal bis du das Denkmal, manchmal die Taube!

Deluxee

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.  :~

Code: [Select]

if (Serial.available() > 0) {

        }


Grüße

Christoph

uwefed

@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

volvodani

#5
Apr 25, 2012, 10:37 pm Last Edit: Apr 27, 2012, 10:09 am by volvodani Reason: 1
Auszug aus der HardwareSerial.cpp


Code: [Select]

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: [Select]
#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
So ist das Leben:
Manchmal bis du das Denkmal, manchmal die Taube!

Lena

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
„Zwei Dinge sind unendlich, das Universum und die menschliche Dummheit, aber bei dem Universum bin ich mir noch nicht ganz sicher."

Albert Einstein

Deluxee

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

Deluxee

#8
Apr 27, 2012, 04:53 pm Last Edit: Apr 27, 2012, 06:27 pm by Deluxee Reason: 1
Das Problem mit der Verzögerung hab ich gelöst!  :D 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: [Select]

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: [Select]

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

Go Up