How do I correct an analog axis bouncing?

Status
Not open for further replies.

Doon1

Member
HI all,
I have a joystick that I use the 2 slider axis (analog 4 and 5). In game (DCS) the 2 axis they control can be seen to be bouncing ever so slightly. The code for the 2 axis is just:
Code:
 // Pitch and throttle axis
  Joystick.sliderLeft(analogRead(4));
  Joystick.sliderRight(analogRead(5));

Is there something I can add that will smooth out the input throughout the range of motion?
Thanks.
John
 
I found the smoothing tutorial. Who knew...
I need to smooth 2 inputs. Can I write it like this?

Code:
const int numReadings = 10;

int readings[numReadings];      
int readIndex = 0;              
int total = 0;                  
int average = 0;   
            
int inputPin = A4 and A5;

Thanks
John
 
I use a sliding window averaging scheme for my game controller. Each pass of the main loop I grab an A/D sample and stuff it into an array that is 16 words deep. I use a counter (Analog_Cnt) that is incremented with each pass to determine where in the array to to stuff it (the array index).

Every main loop pass I sum all the values in the array. Since they are all 16-bit values the resulting number is always less than a 32-bit unsigned integer, so no worries about exceeding the 32-bit maximum value.

Next, I divide the sum of all values in the array by 16 by right shifting the result 4 places. This is a fast way to divide without math.

That's it. My main loop executes once every 1 mS.

In the example below some of my A/D inputs are not used and forced to zero.

Code:
  //---------------------------------------------------------------------------
  // Analog Section
  // Read analog inputs sequentially and apply slidding window averaging
  //---------------------------------------------------------------------------
  total_x = 0;
  total_y = 0;
  total_z = 0;
  total_xr = 0;
  total_yr = 0;
  total_zr = 0;
  total_dial = 0;
  total_slider = 0;

  // Read 8 analog inputs and use them for the joystick axis
  Analog_X [Analog_Cnt] = analogRead(4);
  Analog_Y [Analog_Cnt] = analogRead(5);
  Analog_Z [Analog_Cnt] = 0; // analogRead(2);
  Analog_XR [Analog_Cnt] = 0; // analogRead(3);
  Analog_YR [Analog_Cnt] = 0; // analogRead(0);
  Analog_ZR [Analog_Cnt] = 0; // analogRead(1);
  Analog_Dial [Analog_Cnt] = 0;
  Analog_Slider [Analog_Cnt] = 0;
 
  // Counter for sliding average window
  Analog_Cnt++;                         
  if (Analog_Cnt >= ANALOG_AVERAGE_CNT)
  {
    Analog_Cnt = 0;
  }

  // Sliding window average X, Y, & Z axis
  for (i = 0; i < ANALOG_AVERAGE_CNT; i++)
  {
    total_x += Analog_X[i];
    total_y += Analog_Y[i];
    total_z += Analog_Z[i];
    total_xr += Analog_XR[i];
    total_yr += Analog_YR[i];
    total_zr += Analog_ZR[i];
    total_dial += Analog_Dial[i];
    total_slider += Analog_Slider[i];
  }
  // Right shift (divide by 16)
  average_x = total_x >> ANALOG_AVERAGE_DIV_SHIFT;
  average_y = total_y >> ANALOG_AVERAGE_DIV_SHIFT;
  average_z = total_z >> ANALOG_AVERAGE_DIV_SHIFT;
  average_xr = total_xr >> ANALOG_AVERAGE_DIV_SHIFT;
  average_yr = total_yr >> ANALOG_AVERAGE_DIV_SHIFT;
  average_zr = total_zr >> ANALOG_AVERAGE_DIV_SHIFT;
  average_dial = total_dial >> ANALOG_AVERAGE_DIV_SHIFT;
//  average_slider = total_slider >> ANALOG_AVERAGE_DIV_SHIFT;

  Joystick.X(average_x);
  Joystick.Y(average_y);
  Joystick.Z(average_z);
  // Temp assign unused analog inputs to zero.
  average_xr = 0;
  average_yr = 0;
  average_zr = 0;
  average_dial = 0;
  Joystick.Xrotate(average_xr);
  Joystick.Yrotate(average_yr);
  Joystick.Zrotate(average_zr);
  Joystick.Dial(average_dial);
//  Joystick.Slider(average_slider);  // Used as encoder data
 
I understood absolutely nothing of that. But thank you.
I will try to gain some understanding of it and apply it to what I'm doing.
That's how we learn, right?
 
Yes. Keep struggling with the problem. You will get it!

The algorithm is simply an algebraic average. That is, you take a number of samples, say 8, and write the down on a piece of paper.

Now add up all 8 samples. Next divide the results by the total number of samples, which is 8. The result of that division is the average of the 8 samples.

That's the crux of it, The rest of the code is all housekeeping stuff to make it work. For example, the array Analog_X [] is just that piece of paper, keeping a record of all the A/D reads.

The last 16 samples are added in the for loop for (i = 0; i < ANALOG_AVERAGE_CNT; i++). That's the sliding window summation routine.

The Right shift (divide by 16) divides the total and gives you the average for the last 16 analog reads in the variable average_x.

Finally, the value in average_x is placed in the USB data structure and manually sent to the PC at the end of the main loop routine.

Some of my A/D channels are forced to zero because in the particular application I was using the software for there was nothing connected to those channels and I didn't want random noise and coupling from other channels creating false values in unused channels.
 
Thank you for that explanation.
So if I am reading this correctly, like me, you are only using 2 of the analog inputs (4 and 5). But you are using them for the x and y axis whereas I am using them for the slider axis. For the 0-3 inputs, the first section is:
Analog_Z [Analog_Cnt] = 0; // analogRead(2);
Analog_XR [Analog_Cnt] = 0; // analogRead(3);
Analog_YR [Analog_Cnt] = 0; // analogRead(0);
Analog_ZR [Analog_Cnt] = 0; // analogRead(1);

Was there a need to include them in the remaining sections or was that for continuity sake?
Also. Is all of this in the void loop section of the sketch?
Thanks again for your help. It's starting to make sense.
John
 
Thank you for that explanation.
So if I am reading this correctly, like me, you are only using 2 of the analog inputs (4 and 5). But you are using them for the x and y axis whereas I am using them for the slider axis. For the 0-3 inputs, the first section is:
Analog_Z [Analog_Cnt] = 0; // analogRead(2);
Analog_XR [Analog_Cnt] = 0; // analogRead(3);
Analog_YR [Analog_Cnt] = 0; // analogRead(0);
Analog_ZR [Analog_Cnt] = 0; // analogRead(1);

Was there a need to include them in the remaining sections or was that for continuity sake?
Also. Is all of this in the void loop section of the sketch?
Thanks again for your help. It's starting to make sense.
John

The software is used in different configurations, depending on how it is implemented and the total number of channels can vary. When I first did this I wanted to create something that would contain the largest number of A/D channels needed, then I would zero out unused channels.

I know there are more eloquent ways to do that, but I was experimenting at that point.

Yes, all of this was contained in the void loop.
 
Status
Not open for further replies.
Back
Top