Using the 10-DOF to swing that swing responsively

Post here about your Arduino projects, get help - for Adafruit customers!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
flowersniffer
 
Posts: 8
Joined: Thu Mar 26, 2015 2:08 pm

Using the 10-DOF to swing that swing responsively

Post by flowersniffer »

My project is to activate a 36" length plank suspended from the ceiling using two opposing DC Fans, a motor drive shield, and the readings from an Adafruit 10-DOF.

The plank's activity is a side-to-side swing (not back-and-forth as with a playground swing). Below is a simplified diagram (A and B are the Fans and the + is placement of the 10-DOF on the plank) :

|.......................................|
|.......................................|
|.......................................|
|.......................................|
|.......................................|
========+========
<FanA................ FanB>


If plank is at standstill: Fan A at max, pushing the plank to the right. Fan B idle.
If plank is moving right (towards B): Fan A at max until a change of direction. Fan B idle.
If plank is moving left (towards A): Fan B at max until a change of direction. Fan A idle.

A version of this experiment has worked in the past by my setting the fan increments (delay) manually based on the length of the hang. I need a more responsive solution.

I have been playing with the 10-DOF and figured I'd use the x-axis readings to detect the direction and activate the fans accordingly. If it was moving along the x-axis to the right, I was receiving a positive reading, if it switched direction, the reading would drop below 0, heading the other direction. Here's the loop code:

Code: Select all

void loop() {
  /* Get a new sensor event */
  sensors_event_t event;
   
  /* Retrieve data (acceleration is measured in m/s^2) */
  accel.getEvent(&event);
  
  float  markerX = event.acceleration.x;
  
  if(markerX < 0){
    analogWrite(ledPin1, 255);
    analogWrite(ledPin2, 0);
  }
  else{
    analogWrite(ledPin2, 255);
    analogWrite(ledPin1, 0);
  }
}
I feel my code usage and understanding of this sensor is too over-simplified. I've been all over the webs, looking at perpetual motion experiments, pendulums, etc. Nothing seems to quite fit. I've read that I should use the gyro sensor to "clean up" my accel readings to eliminate the stuttering, but the author didn't go further than this. I hope someone could jump in here and elaborate on which readings are most pertinent and, possibly, how to implement them as constraints. Also, any advice on getting the machine to be "noise proof" or self-correcting would be appreciated.
Thank you in advance!

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: Using the 10-DOF to swing that swing responsively

Post by adafruit_support_bill »

The problem is that acceleration is not the same thing as velocity (speed).
Acceleration is a measure of the change in velocity. As noted in the code, the units are m/s^2 or meters per second per second.
So, the biggest accelerations will be measured at either end of the swinging motion as the swing changes direction.

User avatar
flowersniffer
 
Posts: 8
Joined: Thu Mar 26, 2015 2:08 pm

Re: Using the 10-DOF to swing that swing responsively

Post by flowersniffer »

Thank you @adafruit_support_bill for responding.

Bear with me while I wrap my head around this.

In the current code, I am asking the program to change directive every time there's a significant change in acceleration along the x-axis. My original thought was that a change in the accel value that I get from the 10-DOF (from a float>0 to a float<0) indicates this change. But this is not the case(?).

I understand that a velocity measurement could allow me to time the fans' firing. I'm not sure I receive these values (displacement, time) with the 10-DOF unless I'm missing something.

Thank you.

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: Using the 10-DOF to swing that swing responsively

Post by adafruit_support_bill »

I think your code is a step in the right direction. But it does not do quite what you described in the OP.
I'd use the x-axis readings to detect the direction and activate the fans accordingly.
What your code is reading is the change in velocity, not the change in direction. The swing will begin to slow down for some time before it actually reverses direction. As soon as it begins to slow, the readings will change sign.

User avatar
flowersniffer
 
Posts: 8
Joined: Thu Mar 26, 2015 2:08 pm

Re: Using the 10-DOF to swing that swing responsively

Post by flowersniffer »

adafruit_support_bill wrote:What your code is reading is the change in velocity, not the change in direction. The swing will begin to slow down for some time before it actually reverses direction. As soon as it begins to slow, the readings will change sign.
I've done some more hunting and believe I fully understand.
I'm still not quite sure how measuring velocity would help me. Wouldn't I run into similar issues? As you say, in the end, it's direction of the swing I need.

Since you are familiar with the capabilities of this sensor, perhaps you could recommend a different approach that will address my particular project, either with this sensor or another.

Here's my theories. Perhaps one of these are more applicable.
-Filter my data using some of the available work out there to determine more precise indicators. I've been reading up on the Kalman filter.
-Use the sensor (magnetometer?) to record original position, relative maxDirectionAs and maxDirectionBs and use relative positioning to drive my motors
-Convert my gyro's angular velocity to velocity and use this as an indicator (though I've read that there may be drift)

Thanks again. Love me some Adafruit.

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: Using the 10-DOF to swing that swing responsively

Post by adafruit_support_bill »

The solution should not be very difficult. The peak acceleration forces (both positive and negative) will be measured at the 2 extremes of the swinging motion. When you see the acceleration peak and start to decline, that means the swing has just reversed direction and you can switch the fans.

User avatar
adafruit_support_mike
 
Posts: 67446
Joined: Thu Feb 11, 2010 2:51 pm

Re: Using the 10-DOF to swing that swing responsively

Post by adafruit_support_mike »

You can also keep track of the average time between transitions.

Pendulums are famous for having a period that's largely independent of the distance the weight travels (the amplitude of oscillation) as long as the amplitude remains fairly small (less than about 10 degrees either side of center). Even for larger amplitudes, the periods of any two swings will be equal as long as their amplitudes are about the same.

You can use the timing to predict when the swing will reach the top of its arc and start swinging back the other way, then verify that prediction with the accelerometer data.

User avatar
flowersniffer
 
Posts: 8
Joined: Thu Mar 26, 2015 2:08 pm

Re: Using the 10-DOF to swing that swing responsively

Post by flowersniffer »

Excellent. I'll take your advices. It's starting to take shape.

I'm backtracking to calibrate the sensor to the object and incorporating a smoothing function to work out the jitters.

I will post my code when I have something to show.
adafruit_support_mike wrote:You can also keep track of the average time between transitions.

Pendulums are famous for having a period that's largely independent of the distance the weight travels (the amplitude of oscillation) as long as the amplitude remains fairly small (less than about 10 degrees either side of center). Even for larger amplitudes, the periods of any two swings will be equal as long as their amplitudes are about the same.

You can use the timing to predict when the swing will reach the top of its arc and start swinging back the other way, then verify that prediction with the accelerometer data.
I like grabbing a timing variable. Very elegant. A newbie question: does the Arduino have a 'stopwatch' function built in, or would I derive it from my sensor data?

Thanks a bunch!

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: Using the 10-DOF to swing that swing responsively

Post by adafruit_support_bill »

The Arduino has a "millis()" function that returns the number of milliseconds since the last reset. That is handy for many timing functions:

http://arduino.cc/en/reference/millis

User avatar
flowersniffer
 
Posts: 8
Joined: Thu Mar 26, 2015 2:08 pm

Re: Using the 10-DOF to swing that swing responsively

Post by flowersniffer »

Hello Bill and Mike,

Here's my latest code:

Code: Select all

#include <elapsedMillis.h> //measure duration

/* Libraries to fully operate the 10DOF sensor */
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_LSM303_U.h>
#include <Adafruit_BMP085_U.h>
#include <Adafruit_L3GD20_U.h>
#include <Adafruit_10DOF.h>

/* Assign a unique ID to the sensors */
Adafruit_LSM303_Accel_Unified accel = Adafruit_LSM303_Accel_Unified(30301);
Adafruit_LSM303_Mag_Unified   mag   = Adafruit_LSM303_Mag_Unified(30302);
Adafruit_BMP085_Unified       bmp   = Adafruit_BMP085_Unified(18001);
Adafruit_L3GD20_Unified       gyro  = Adafruit_L3GD20_Unified(20);

/* The following are the offset compensations used for accel calibration */
/* Numbers should only be changed with full knowledge of defaults */
float AccelMinXOffset = -0.35;//default at 0
float AccelMaxXOffset = 0;//default at 0

/* Other global variables */
int accelX;
int accelPrev; 
elapsedMillis timeElapsed;
int duration;

/* Board Output Assignments */
int fanPushPos = 9; //pin 9 activates fan promoting positive x-acceleration
int fanPushNeg = 10; //pin 10 activates fan promoting negative x-acceleration

/* Smoothie for LSM303 X-acceleration _from Smoothing//BEGINS//  */
// Define number of samples to keep track of.  The higher the number,
// the more the readings will be smoothed, but the slower the output will
// respond to the input.  Using a constant rather than a normal variable lets
// use this value to determine the size of the readings array.
const int numReadings = 100;
int readings[numReadings];      // the readings from the analog input
int index = 0;                  // the index of the current reading
int total = 0.0;                  // the running total
int average;                // the average

int smoothie(){  
    // subtract the last reading:
    total= total - readings[index];        
    // read from the sensor:  
    sensors_event_t event;
    accel.getEvent(&event); 
    readings[index] = (event.acceleration.x * 100); //multiply by 10 or 100 for accuracy
    // add the reading to the total:
    total= total + readings[index];      
    // advance to the next position in the array:  
    index = index + 1;                    

    // if we're at the end of the array...
    if (index >= numReadings)              
      // ...wrap around to the beginning:
      index = 0;                          

    // calculate the average:
    average = total / numReadings;        
    // send it to the computer as ASCII digits
    return(average);  
    delay(1);        // delay in between reads for stability
  }; 
/* Smoothie for LSM303 X-acceleration //ENDS */

void setup(void)
{
  sensor_t sensor;
  accel.getSensor(&sensor);
  
  /*Application set-up information follows*/
  pinMode(fanPushPos, OUTPUT);
  pinMode(fanPushNeg, OUTPUT);
}

void loop() { 
  
  accelX = (smoothie()); //get my smoothed reading  

  if (accelX >= 0 && accelX >= accelPrev) {
      analogWrite(fanPushPos, 255); //we push with the fan at Pos
      analogWrite(fanPushNeg, 0); //we stall with the fan at Neg
      accelPrev = accelX; 
    }
  else if (accelX < 0 && accelX >= accelPrev) {
      analogWrite(fanPushPos, 255); //we push with the fan at Pos 
      analogWrite(fanPushNeg, 0); //we stall with the fan at Neg
      accelPrev = accelX;
    }
  else{
      analogWrite(fanPushPos, 0); //we stall with the fan at Pos
      analogWrite(fanPushNeg, 255); //we push with the fan at Neg
//      delay(timeElapsed);
//      timeElapsed = 0; //reset
      accelPrev = accelX; 
    };
 }
It's working responsively. However, the accelerometer still produces too much noise on the transition to progress past a certain point. I also find I am unable to implement a duration variable (as Mike suggested).

I'd like to smooth it out further but can not afford to expand the sample size. I have been reading about Complementary Filters, but most point to a conversion of 3 dimensional data and are talking about roll, pitch, and yaw, none of which seem to apply here. Frankly, I feel like it should be simple, but haven't seen any newb-geared examples.

Knowing this project and this sensor's output, could you point me in the right direction on what accel and gyro data I need to grab. If I do need to convert, how?

Thank you,
FlowerSniffer

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: Using the 10-DOF to swing that swing responsively

Post by adafruit_support_bill »

However, the accelerometer still produces too much noise on the transition to progress past a certain point.
How do you know this? Your code is not printing out either the raw or smoothed values.

User avatar
flowersniffer
 
Posts: 8
Joined: Thu Mar 26, 2015 2:08 pm

Re: Using the 10-DOF to swing that swing responsively

Post by flowersniffer »

I excluded the Serial printout lines from the code for brevity but I have been monitoring the data this whole time.

I also have a light indicator on the top of sculpture showing me when the Pos - Neg fans are kicking. It is not uncommon to see the Pos light suddenly kick in and out during the Neg swing, particularly near the Min and Max points and vis-a-versa.

For the practical purpose of the swing, it doesn't interfere too much. For the purpose of using duration as a variable and making a smoother and more controllable device, it is too noisy. The timeElapsed variable resets prematurely.

I should also note that at rest, the lights bounce back in forth and the numbers are always moving.

My thoughts are two-fold: I would like to implement a better smoothing filter (perhaps a complementary filter) and, perhaps, introduce a slight threshold of +-somenumber. I would love some help with the first fold.

Thanks again.

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: Using the 10-DOF to swing that swing responsively

Post by adafruit_support_bill »

Accelerometer data does tend to be noisy. Vibrations are accelerations too and MEMS accelerometers excel at measuring vibrations.

Complementary filters generally combine the data from two sensors with complementary properties - such as the accelerometer and they gyro. Here is an excellent post on implementing such a filter:
http://www.pieter-jan.com/node/11

User avatar
flowersniffer
 
Posts: 8
Joined: Thu Mar 26, 2015 2:08 pm

Re: Using the 10-DOF to swing that swing responsively

Post by flowersniffer »

Thank you.

This is the website I found as well. You have a decent thread with Nickbee regarding implementing this.
However, the question Nickbee had is the same that I have is as follows:
nickbee wrote:I guess that's the part I'm not understanding. The gyro raw data is in degrees per second. The accelerometer raw data is in meters per second squared. Ultimately I want to know my XYZ rotation. Would that mean I would need to process the raw accelerometer data to "convert" it to an XYZ rotation to plug into my complementary filter?
Do we need to convert to raw?

I'm looking at a much more simple movement tracking than Nickbee, mostly concerned with x-axis. Do I need to concern myself with other dimensions of movement.

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: Using the 10-DOF to swing that swing responsively

Post by adafruit_support_bill »

The two sensors are measuring different things, so their raw data is in different units. However, the data from each is being used to calculate an angular orientation in degrees. The resulting angles are in the same units, so they are comparable.

However, for your application, even a complementary filter may be overkill. You don't need to know the orientation. You only need to detect the apex of the swing. If you have the device mounted so that it rotates about some axis as the swing moves, you only need to look at the change in sign of the rotation measurement. I would expect your gyro data to be a bit cleaner than your accelerometer data. I'd start by printing out the raw data to see what it looks like.

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino”