Pages: [1] 2   Go Down
Author Topic: radio control reciever spektrum ar6100 ar6200 signal noise  (Read 3612 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 8
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hey all has anyone used pulseIn() to read signals from a RC rx like spektrums since they dont use ppm signals im trying to just read the pulses. im having trouble getting a clean signal it seems to want to just jump around. this wasnt a problem for me till now that im trying to be a little more accurate with the data and the jumping even if i apply digitalsmooth() to the data still wont get me where i need to be. any suggestions here is my code.

Code:
#define TIMEOUT 100

// { roll, pitch, yaw }
float angles[3] = {0.0, 0.0, 0.0};

#include <Servo.h>
Servo motor1;
int pos = 0;
int pin3 = 3;
int pin4 = 4;
int pin5 = 5;
int pin6 = 6;
int pin7 = 7;
int pin8 = 8;
int pin9 = 9;
int pin10 = 10;
int pin11 = 11;
int threshold = 30; //threshold for smoothing Rx data
int uGear = 0, uYaw = 0, uPitch = 0, uRoll = 0, uThrtl = 0,uGear1 = 0, uYaw1 = 0, uPitch1 = 0, uRoll1 = 0, uThrtl1 = 0;


void setup() {
  Serial.begin(57600);
  Serial1.begin(57600);
  motor1.attach(8);
  pinMode(pin3, INPUT);
  pinMode(pin4, INPUT);
  pinMode(pin5, INPUT);
  pinMode(pin6, INPUT);
  pinMode(pin7, INPUT);
}

void loop() {


  uThrtl = pulseIn(pin3, HIGH);
  uRoll = pulseIn(pin4, HIGH);
  uPitch = pulseIn(pin5, HIGH);
  uYaw1 = pulseIn(pin6, HIGH);
  uGear = pulseIn(pin7, HIGH);

 
  Serial.print("Throttle: ");
  Serial.print(uThrtl); //throttle 
  Serial.print("Yaw: ");
  Serial.print(uYaw);
  Serial.print("Pitch: ");
  Serial.print(uPitch);
  Serial.print("Roll: ");
  Serial.print(uRoll);
  Serial.print("Gear: ");
  Serial.print(uGear);
  Serial.println("");
}
Logged

Ottawa, ON
Offline Offline
Newbie
*
Karma: 0
Posts: 15
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I don't have any advice to offer, but I am very interested in your project.

My project also invloves a Sketrum AR6200 receiver. Essentially, I want to be able to switch from manual operation to automatic or use the Arduino to change the function of the sticks on the remote.

Looking at your code, I'm getting the impression I can simply connect each of the rx channels to one of the digital inputs and read the data. It appears that you've used PWM pins for your inputs. Is this required, or can you use any pin?

I did find this link that seemed to be helpful and may help you get a cleaner signal. I'm not sure how to make it work with multiple inputs though. Good luck!

http://scottrharris.blogspot.com/2010/01/reading-servo-pwm-with-arduino.html

I also found another interesting link from the arduino site. I'm not sure I fully understand the code though...

http://arduino.cc/playground/Code/ReadReceiver


« Last Edit: January 05, 2012, 01:20:22 am by prdufresne » Logged

Arduino Beginner, but I catch on quick!

0
Offline Offline
Newbie
*
Karma: 0
Posts: 8
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes u need pwm ports cause the signal varies in length. The problem I was getting is that the measurement of e length of signal varied pretty drastically while reading to the point that if I were to use them the program would just be jittery and frankly very hard to use. Thanx for the links but I have read those. I have used a smoothing algorithm to smoothe the signals but unfortubately it makes the programs response laggy and very unsmooth when user input is attempted.
Logged

Dubai, UAE
Offline Offline
Edison Member
*
Karma: 21
Posts: 1670
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Guys,

I am also reading RC Receiver signals in my projects. Its very simple using an interrupt, my function that reads the input has very little code.

Sample interrupt routine -

Code:
// this is the interrupt service routine, note that is called on pin CHANGE, we use
// HIGH to catch the start of a pulse, LOW to catch the end
// nThrottleIn must be declared as volatile as its used in both this function and loop

void calcInput()
{
  // read the PIN, figure out if we are starting HIGH or ending LOW
  if(digitalRead(THROTTLE_SIGNAL_IN_PIN) == HIGH)
  {
    // NEW PULSE
    ulStartPeriod = micros();
  }
  else
  {
    // END OF PULSE
    ulEndPeriod = micros();
    // trap the first pulse which is often a falling pulse and otherwise causes an unexpected value error
    if(ulStartPeriod)
    {
      nThrottleIn = (int)(ulEndPeriod - ulStartPeriod);
    }
  }
}

I am seeing the signal wander by a few ms, I am assuming that this is originating in my RC Equipment, not the Arduino. I am not using a smoothing function as the project is ultimately going to end up in my radio controlled race car. Instead of smoothing I am using 'confidence', I have a very simple algorithm that compares the most recently read input to the last input, if the difference is large, the assumption is 'this guy really wants to turn, accelerate, brake' and the input is used. If the input is very close to the last value I increase a confidence value, if I see enough consecutive increases in confidence I adopt the new value, however if the input at any point reverts to the original value, confidence in the intervening values resets to 0. This way I ignore wandering around the current value, but can still react instantly to meaningful changes in input.

Sample Confidence code -

NOTE - code is cut and paste from a wider project, please check my blog over the weekend for the full context and final version.

Code:

     // If we assume a range of 1000 to 2000 to cover 60 degrees, 4 microseconds = .25 of a degree
     // Due to the transmitter quality or arduino timing, we often get readings that drift around by 4 or 8ms.
     // if the input signal is 16 or more ms different that the current output, change immediatley
     // if the input signal is 8 or more different get 2 consecutive readings before change
     // if the input signal is 4 or more different get 4 consecurity readings before change
     int nDifference = nThrottleIn - nThrottleOut;
     if((nDifference > 16) || (nDifference < -16))
     {
      nConfidence = 16;
     }
     else
     {
      if(nDifference == 0)
      {
        // previous deviations can be discarded as noise, set confidence back to 0
        nConfidence = 0;
      }
      else
      {
        if((nDifference > 8) || (nDifference < -8))
        {
          nConfidence ++;
        }
        if((nDifference >= 4) || (nDifference <= -4))
        {
          nConfidence ++;
        }
      }
     }

      // we only want to call this for meaningful changes
      // repeated calling causes servo chatter which cost power, causes heat, wear and tear

     // if confidence >= 4 we are considering this as a meaningful change so
     //  will put whatever work we want to do with the input into this section
     if(nConfidence >= 4) // we should move all of the confidence handling to a seperate function and fine tune
     {
       // Reset the confidence level back to 0
       nConfidence = 0;

       // Useful work/ Your code goes in here .... i.e.       throttleServo.writeMicroseconds(nThrottleIn);
      }
     


[EDIT - moved throttleServo.writeMicroseconds into last if block ]

I will post the whole project with code and write up on my blog this weekend see -

http://rcarduino.blogspot.com/

DuaneB


« Last Edit: January 05, 2012, 05:41:08 am by DuaneB » Logged


Ottawa, ON
Offline Offline
Newbie
*
Karma: 0
Posts: 15
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thank you Duane. I like the aproach you're taking with your code. I'm thinking I can take your approach and generalize it into a function so it can effectively monitor all 4 channels from the receiver without having to write four separate interrupt handlers confidence code.

I have a two other channels that are really binary in nature. One of these will be used to switch between autonomous and manual control. I really needed the receiver data for manual control mode.

Like your blog. I'll have to keep an eye on it!
Logged

Arduino Beginner, but I catch on quick!

Dubai, UAE
Offline Offline
Edison Member
*
Karma: 21
Posts: 1670
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,
   I have spent the evening trying to eliminate noise in my system, I have reduced rogue pulses to about 1 in 50, mostly through decoupling capacitors in the power lines. It will eventually be necessary to update the confidence algorithm to handle rogue pulses but while its on the bench I want to get the hardware as clean as possible.

I am still on breadboard at the moment, hopefully a move to a strip board shield will clean up some of the remaining noise.

Interestingly my rogue pulses are concentrated around neutral, with any amount of acceleration or braking the rogue pulses drop to one every few seconds (better than 1 in 200 or 300) , I have observed the same results for two different electronic speed controllers a Tamiya TEU101BK and a MTroniks IPSport6.

Both of my speed controllers are budget orientated, if you are using better quality electronics you may not have a problem.

I will let you know how I get on after the move to strip board.

Duane.
Logged


Ottawa, ON
Offline Offline
Newbie
*
Karma: 0
Posts: 15
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


My son's R/C truck, the recipient of the Arduino brain, has a Rooster rock crawling speed controller, so it is a higher-end controller. I'm probably a few days away from being able to test anything that combines the Arduino with the receiver, but I'll let you know how the testing goes.

I'll start with something simple with one channel, and take it from there.
Logged

Arduino Beginner, but I catch on quick!

Ottawa, ON
Offline Offline
Newbie
*
Karma: 0
Posts: 15
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Duane,

I'm looking through your code, but I'm struggling with the interrupts. Can I generate an interrupt with any input, or do I have to used one of the built-in hardware interrupt pins, such as pins 2 and 3?

ThanX!

EDIT:

I think I'll have to wait to see your code on your blog. I'm having a difficult time figuring out how all the pieces fit together at this point.

As for the interrupts question above, I'm pretty sure I need to use the built-in hardware interrupts, of which there are 6 on the Arduino Mega.
« Last Edit: January 06, 2012, 09:29:36 pm by prdufresne » Logged

Arduino Beginner, but I catch on quick!

Offline Offline
God Member
*****
Karma: 0
Posts: 547
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Does this reciever have a ground test port on the power connector? JR recievers map the detector output to the signal pin of the power connector so you can inject controls via a cable from the trainer port on the transmitter, it also allows a second reciever to decode a master's bitstream. If raw data is available you should be able to snoop the bitstream with the Serial object.
Logged

Ottawa, ON
Offline Offline
Newbie
*
Karma: 0
Posts: 15
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


Quote
Does this reciever have a ground test port on the power connector? JR recievers map the detector output to the signal pin of the power connector so you can inject controls via a cable from the trainer port on the transmitter, it also allows a second reciever to decode a master's bitstream. If raw data is available you should be able to snoop the bitstream with the Serial object.

After reading your post, I did some research and got conflicting information. Officially, it seems that the AR6200 receiver does not have a flight log port, but I've read on some forums that it does. If it does, I'm not sure how I would be able to separate the data for the channels. I've read somewhere that the longest low pulse would precede the timing bit and that the channel signals should follow sequentially. On that basis I would assume that the I would receive 6 pulses (6 channel receiver) of varying lengths each one representing the pulse width that would be sent to it's respective channel.

I guess I could run a test by connecting that batt/bind port to a input on the Arduino and do some data logging.
 
Logged

Arduino Beginner, but I catch on quick!

Dubai, UAE
Offline Offline
Edison Member
*
Karma: 21
Posts: 1670
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,
   My thinking is that for most projects its sufficient to let the receiver handle all of the channel decoding. It is very simple to read a channel once it has been decoded.

The following code sample is a stripped down extract from my current project, its very simple all it does is -

1) Create a Servo object and set the servo position to centered - Servos and ESCs use the same signals, so a centered servo is the same signal as neutral throttle - a pulse width of 1500ms.

2) Create an interrupt and a service routine (calcInput) , the service routine calculates the input pulse width by reading from the receiver.

The simplest test you can do with this code is to connect the PWM output pin to the interrupt PIN and read your own signal, maybe then add a potentiometer on an analogue input which you can read and adjust the PWM output in your code to simulate a controller.

Once you are happy with that its time to connect a real receiver and ESC. I would suggest that when you connect to a real ESC you initially fix the output to 1500ms or NEUTRAL_THROTTLE as I have defined it, this will reduce the risk of any unexpected behavior.

Notes -

The code has been stripped down to show only the bare minimum, there is no error handling and no means to trap rogue signals. Before sending anything other than the NEUTRAL_THROTTLE signal you should adapt the code to handle errors, out of range conditions etc.

While the code has been compiled, it has not been uploaded and tested, it is a cut and paste from a larger project, the attached code has not been tested outside of the wider project but is expected to work for the purpose of generating a PWM output suitable for a servo and reading the same output back in through an interrupt.

Please give some consideration to protecting your Arduino when interfacing to external systems, particularly noisy ones such as RC Systems with motors attached.

Stripped down sample -

Code:
#include <Servo.h>


// Digital Output
#define THROTTLE_SIGNAL_OUT 9 // early versions of Arduino only support the Servo Library on digital pin 9 and 10 so lets stay with this convention

// Interupts
#define THROTTLE_SIGNAL_IN 0 // INTERRUPT 0 = DIGITAL PIN 2
#define THROTTLE_SIGNAL_IN_PIN 2 // INTERRUPT 0 = DIGITAL PIN 2

#define NEUTRAL_THROTTLE 1500

// Global Variables and Resources
Servo throttleServo;

int nThrottleOut = NEUTRAL_THROTTLE;
volatile int nThrottleIn = NEUTRAL_THROTTLE; // volatile, we set this in the Interrupt
volatile unsigned long ulStartPeriod = 0;
volatile unsigned long ulEndPeriod = 0;

volatile boolean bNewThrottleSignal = false;

void setup()
{
  throttleServo.attach(THROTTLE_SIGNAL_OUT);
 
  attachInterrupt(THROTTLE_SIGNAL_IN,calcInput,CHANGE);
}

boolean bInit = false;

void loop()
{
  if(bNewThrottleSignal)
  {
    bNewThrottleSignal = false;
    Serial.println(nThrottleIn);
  }
 
  if(false == bInit)
  {
    bInit = true;

    // Note that we only do this once, the function sets the hardware counters within the arduino to output
    // a pulse of 1500ms about 50 times a second, this will continue automatically in the background until we
    // explicitly tell it to stop or change the value - very useful !
    throttleServo.writeMicroseconds(NEUTRAL_THROTTLE);
    Serial.println("Done Init");
  }
}

void calcInput()
{
  // read the PIN, figure out if we are starting HIGH or ending LOW
  if(digitalRead(THROTTLE_SIGNAL_IN_PIN) == HIGH)
  {
    // NEW PULSE
    ulStartPeriod = micros();
  }
  else
  {
    // END OF PULSE
    ulEndPeriod = micros();
    // trap the first pulse which is often a falling pulse and otherwise causes an unexpected value error
    if(ulStartPeriod)
    {
      nThrottleIn = (int)(ulEndPeriod - ulStartPeriod);
      ulStartPeriod = 0;
      // set on updating nThrottleIn, reset by loop when read
      // this way we only pay attention to new signals, leaving us free to do more work in loop as the project develops
      bNewThrottleSignal = true;
    }
  }
}


Now that I have located the source of the noise in my current project I hope to make some rapid progress over the next week or so, see here for a small update - http://rcarduino.blogspot.com/

DuaneB
Logged


Offline Offline
God Member
*****
Karma: 0
Posts: 547
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

There is no neutral on throttle. ESC arming requires stick full back with no trim, this is to prevent lost fingers, sudden fly aways ect. 0 is actually -45 degrees for a standard servo, also note transmitters support axis reversal on the transmit side, as not all throttle applications on fueled engines have the throttle movement in the same direction. Most of us that fly seriously try to build our installations for an engine shutdown on a reciever failure/undervoltage.
Logged

Dubai, UAE
Offline Offline
Edison Member
*
Karma: 21
Posts: 1670
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,
   It makes perfect sense that there would be no neutral on air ESCs, however car ESCs have a neutral at 1500, halfway between full brake/reverse and full throttle. Similar to air ESCs, car ESCs need to arm, in order to do this the neutral signal should be sent for a few seconds.

Whether you intend to use a land/air/marine ESC, you will still be able to monitor the pulse width being sent from you receiver for any single channel by using the code sample provided and by connecting INT1 (Pin2 on my UNO) to the white signal line from your receiver.

Duane.









Logged


Dubai, UAE
Offline Offline
Edison Member
*
Karma: 21
Posts: 1670
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,

    Just a quick follow up to my post above - does anyone have a tried and tested protection circuit for interfacing between the receiver signal (6v or more in some applications) and the 5V Arduino ?

    Duane.
Logged


Ottawa, ON
Offline Offline
Newbie
*
Karma: 0
Posts: 15
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Duane,

First of all, thank you for posting that code. Working with what you posted,i generalized the code so that the same interrupt handler can be used for all the receiver channels. I've posted it below in case in can help others.

The loop() is only working with one of the 6 channels as that's all I connected for testing, but I think the principle is pretty clear.

Code:
#include <Servo.h>

// Digital Output - Define pins attached to Servos
#define SV_DIG_OUT 8          // define Dig Servo output
#define SV_THROTTLE_OUT 9     // define Throttle Servo output
#define SV_FRONT_STEER_OUT 10 // define Front Steering output
#define SV_REAR_STEER_OUT 11  // define Rear Seteering output

// Digital Inputs - Define Inputs from Receiver
#define RX_AUX1 2 // AUX1 Pin (maps to Interrupt 0)
#define RX_GEAR 3 // GEAR Pin (maps to Interrupt 1)
#define RX_THRO 21 // THRO Pin (maps to Interrupt 2)
#define RX_AILE 20 // AILE Pin (maps to Interrupt 3)
#define RX_ELEV 19 // ELEV Pin (maps to Interrupt 4)
#define RX_RUDD 18 // RUDD Pin (maps to Interrupt 5)

// Interupts - Define Interrupts associated with RX inputs
#define INT_AUX1 0 // INTERRUPT 0 = DIGITAL PIN 2
#define INT_GEAR 1 // INTERRUPT 1 = DIGITAL PIN 3
#define INT_THRO 2 // INTERRUPT 2 = DIGITAL PIN 21
#define INT_AILE 3 // INTERRUPT 3 = DIGITAL PIN 20
#define INT_ELEV 4 // INTERRUPT 4 = DIGITAL PIN 19
#define INT_RUDD 5 // INTERRUPT 5 = DIGITAL PIN 18


#define NEUTRAL 1500

// Global Variables and Resources
Servo svFrontSteer;
Servo svRearSteer;
Servo svThrottle;
Servo svDig;

boolean bInit = false;

int nThrottleOut = NEUTRAL;

// We have 6 channels to measure, so we need these setup as arrays.
volatile int nChannelIn[6];
volatile unsigned long ulStartPeriod[6];
volatile unsigned long ulEndPeriod[6];

volatile boolean bNewSignal[6];

void setup()
{
  // Attach the servos. I'm only using one for testng.
  svFrontSteer.attach(SV_FRONT_STEER_OUT);
 
  attachInterrupt(INT_AUX1,handleINT0,CHANGE);
  attachInterrupt(INT_GEAR,handleINT1,CHANGE);
  attachInterrupt(INT_THRO,handleINT2,CHANGE);
  attachInterrupt(INT_AILE,handleINT3,CHANGE);
  attachInterrupt(INT_ELEV,handleINT4,CHANGE);
  attachInterrupt(INT_RUDD,handleINT5,CHANGE);
 
  Serial.begin(115200);
 
    // Initialize all the servos to center positions.
    svFrontSteer.writeMicroseconds(NEUTRAL);
   
    Serial.println("Done Init");
}

void loop()
{
  if(bNewSignal[INT_THRO])
  {
    bNewSignal[INT_THRO] = false;
    Serial.println(nChannelIn[INT_THRO]);
    svFrontSteer.writeMicroseconds(nChannelIn[INT_THRO]);
  }
 

}

void handleINT0() {calcInput(RX_AUX1, INT_AUX1);}
void handleINT1() {calcInput(RX_GEAR, INT_GEAR);}
void handleINT2() {calcInput(RX_THRO, INT_THRO);}
void handleINT3() {calcInput(RX_AILE, INT_AILE);}
void handleINT4() {calcInput(RX_ELEV, INT_ELEV);}
void handleINT5() {calcInput(RX_RUDD, INT_RUDD);}

void calcInput(int rxChannel, int rxInt)
{
  // read the PIN, figure out if we are starting HIGH or ending LOW
  if(digitalRead(rxChannel) == HIGH)
  {
    // NEW PULSE
    ulStartPeriod[rxInt] = micros();
  }
  else
  {
    // END OF PULSE
    ulEndPeriod[rxInt] = micros();
    // trap the first pulse which is often a falling pulse and otherwise causes an unexpected value error
    if(ulStartPeriod[rxInt])
    {
      nChannelIn[rxInt] = (int)(ulEndPeriod[rxInt] - ulStartPeriod[rxInt]);
      ulStartPeriod[rxInt] = 0;
      // set on updating nThrottleIn, reset by loop when read
      // this way we only pay attention to new signals, leaving us free to do more work in loop as the project develops
      bNewSignal[rxInt] = true;
    }
  }
}


It works well in testing with one channel, and I hope to test it with 4 channels in the next couple of days. I also dropped the bInit code by moving it to the setup() section of the code.
Logged

Arduino Beginner, but I catch on quick!

Pages: [1] 2   Go Up
Jump to: