Accelerometer bouncing/mis-triggering?

Long story short, I'm trying to make a device that plays a sound as your foot hits the ground, and am using an ADXL335 accelerometer to determine the gate of the step. Right now, I'm just trying to get one leg to work properly.

What I'm seeing is that the sketch does read the leg coming up, sets the state in preparation for the leg going down, sees the leg go down and triggers the sound. The problem is that if the leg comes up quickly, it seems to bring the accelerometer up, bounce it back down below the trigger threshold, and play the sound.

For numbers, I'd see something like..

310 = at rest accelerometer z reading
*Start walking
310
320 **goes above lift threshold
330
312 ** plays the sound - it's shouldn't.. your leg is actually up, not down!
350
350
350
350
355
340
336
320
318 *plays the sound because the foot is down now
310
310 **you're standing still again

void loop()
{
  if (calibrate == 1)
  {
    calibrateLegs(); // This calibrates the "at rest" position for the leg.
  }
  
  int zRRead = analogRead(zRPin);
  int zLRead = analogRead(zLPin);

      if (zRRead > ((zRightNeutral + liftThreshold)) && rightLegBack==1) {
      Serial.print("Threshold ");    
      Serial.println(zRRead);
      debounce = 1;

      rightUpCount = 0;
      }

  if (zRRead < (zRightNeutral +  10) && (debounce == 1))
  {
      Serial.print("Trigger ");
      Serial.println(zRRead);
      debounce = 0;
      lastLeg = 0;
      makeTone();
       }
}

I'd appreciate any thoughts or help. I've re-written this routine about 1/2 dozen times, and there's got to be something fundamental I'm missing!

Skyminer:
What I'm seeing is that the sketch does read the leg coming up, sets the state in preparation for the leg going down, sees the leg go down and triggers the sound. The problem is that if the leg comes up quickly, it seems to bring the accelerometer up, bounce it back down below the trigger threshold, and play the sound.

I'd appreciate any thoughts or help. I've re-written this routine about 1/2 dozen times, and there's got to be something fundamental I'm missing!

From my perspective, what you're missing is the rest of your code. We aren'tstanding there looking over your shoulder. If we were, we might be able to determine what the value of debounce does, to cite just one example.

When you're ready to post your code, please copy it directly from the IDE, after using Tools->Auto Format. Then paste it into the editor here, highlight it, and press the 'code' icon; the # sign above the editor.

Sorry.. first time posting. :wink: The entirety of the code is here. Again, please note that I'm only focusing on getting 1 leg working here right now, even though the calibrate code is in there for both.

/*
Voice 0000 = Sound to play when foot hits the ground
Voice 0008 = "Calibrating for 10 Seconds"
Voice 0009 = "Calibration Complete"

*/

#include <Wtv020sd16p.h>

//Analog read pins
const int zLPin = 1;
const int zRPin = 2;
const int buzzerPin = 9;

int minVal = 265;
int maxVal = 502;
int calibrate = 1;
int zRightCalibrate = 0;
int zRightCalibrateSum = 0;
int zRightNeutral = 0;
int zLeftCalibrate = 0;
int zLeftCalibrateSum = 0;
int zLeftNeutral = 0;
int calibrateCount = 0;
int rLift = 0;
int liftThreshold = 60;
int resetPin = 5;
int clockPin = 4;
int dataPin = 3;
int busyPin =2;

//long lastDebounceTime = 0;
//long debounceDelay = 100;

//to hold the caculated values
double x;
double y;
double z;

Wtv020sd16p wtv020sd16p(resetPin,clockPin,dataPin,busyPin);

void setup(){
  Serial.begin(9600); 
  wtv020sd16p.reset();

}


void loop(){

  if (calibrate == 1)
  {
    calibrateLegs();
  }
  
  int zRRead = analogRead(zRPin);
  int zLRead = analogRead(zLPin);


// Watch for right leg lift.  If the leg lifts above the threshold, set rLift to 1, allowing the "down" routine to happen
      if (zRRead > (zRightNeutral + liftThreshold)) {
        Serial.print("Threshold ");    
        Serial.println(zRRead);
        rLift = 1;
      }

// Watch for leg going down.  If it goes down, and the leg has already hit the lift threshold, trigger the sound.
  if ((zRRead < (zRightNeutral +  10) && (rLift == 1) ))
  {
        Serial.print("Trigger ");
        Serial.println(zRRead);
        rLift = 0;
        makeTone();
      }

}


void makeTone()  // Play the sound
{
   wtv020sd16p.asyncPlayVoice(0);
  }


void calibrateLegs()  // Calibrate the legs.  While the person is standing still, take 10 samples, average them, and set it as the "neutral" value
{
delay(2000);
  wtv020sd16p.asyncPlayVoice(8);
delay(3000);  
  while (calibrateCount < 10)
  {
    delay(500);
    int zRightCalibrate = analogRead(zRPin);
    Serial.println(zRightCalibrate);
    zRightCalibrateSum = zRightCalibrateSum + zRightCalibrate;
    int zLeftCalibrate = analogRead(zLPin);
    zLeftCalibrateSum = zLeftCalibrateSum + zLeftCalibrate;
    calibrateCount++;
  }
    Serial.println(zRightCalibrateSum);
    zRightNeutral = (zRightCalibrateSum / 10);
    Serial.print("Calibrated at Right rest value = ");
    Serial.println(zRightNeutral);
    zLeftNeutral = (zLeftCalibrateSum / 10);
    calibrate = 2;
    wtv020sd16p.asyncPlayVoice(9);
}

I'm essentially seeing 2 problems. One is that sometimes the calibrate legs routine doesn't do anything the first time. It'll say that it's calibrating the legs, nothing will appear on the serial monitor, then it'll say it again, THEN you'll see the output from the accelerometer and the legs will calibrate.

The second and bigger problem is that while it senses the foot down and plays the sound properly, it's not uncommon that it triggers when your leg is coming up, especially when walking quickly, or standing still and lifting my leg quickly.

I think what I need is a debounce of sorts on the leg down part, so it only is triggered once the leg has been in the down position for a set amount of time, but can't figure out where the it would go.

I assume that the "if ((millis() - lastDebounceTime) > debounceDelay)" would go right before where I print Trigger, but can't work out where I'd set the lastDebounceTime. Or, if there's even a better way to do this.

Thanks in advance!

If you only want the calibration to happen once at startup time, put it in setup().

There is nothing in the code that would produce the calibration symptoms you describe so I suspect the Arduino is crashing during the calibration. You could confirm that by displaying a startup message in setup(). I usually use Serial.println(FILE " " DATE " " TIME); so I can tell which sketch is running.

Your sample output does not show anywhere near the number of samples I'd expect to need to detect gait. I would have though you would need to average a dozen or more samples taken over something like 100ms to get a reliable indication of impulses.

Also note that you check for a leg lift regardless of whether you have already detected it so you would be vulnerable to multiple detections. Since you aren't smoothing your accel inputs you'd be vulnerable to the accelerometer equivalent of 'contact bounce'. You're right IMO to use a state variable to show what you're expecting to happen next but you need to be more rigorous about which events you check for in each state. If I were you, I'd make rLift a boolean and restructure that logic to test rLift first, and then depending on the result check for the relevant acceleration event.

I am wondering about your example. An accelerometer, of course, does not measure position, but only direction and acceleration. Your examples seem to show a constant direction, with varying accelerations. ie. at-rest is 310, which, if the accelerometer is oriented to sense acceleration vertically, willbe measuring the force of gravity (acceleration and gravity are the same thing). As the leg rises, the reading increases (g + acceleration from rising), but when the leg drops, the reading should decrease to BELOW the at-rest value. In fact, if you were to drop your leg at 32ft/sec squared, the accelerometer should read zero g, whatever that value is when you read the accelerometer.

If you have the accelerometer oriented correctly (or, that you are using the correctly oriented ond., and are reading it fairly often (I wonder about the 500 ms delay in the calibration routine), then you might want to consider integrating acceleration vs. time, as a method of debounce. It would tell you where the leg is at any given interval, and instead of deciding that the leg is downward-bound, based only on acceleration, you can determine where the leg is, using a sequence of readings showing a trend in position with small variations of acceleration ignored. The sequence 330, 312, 350 tells me that the leg is not on the way down, but rather that its acceleration in the up direction decreased between the other two readings.

You should probably try a simple sketch that reads the accelerometer fairly quickly, sending the data to the serial port. Then try things like lifting it quickly, slowly, or with varying rates of speed. Then do the same thing with dropping it with varying speeds. A longish USB cable will be handy for this excercise.

Thanks for the feedback guys.

PeterH - I moved the calibration routine to setup(), and it does appear to have solved the problem. I'd originally put it in the loop as I was going to add a capability to recalibrate if someone lifted their leg really high, but it's probably not necessary.

I've not used booleans yet, but will research them.

La3ry - I'm using an ADXL335 accelerometer, but am using its raw inputs, not calculating for real acceleration. So, when the accelerometer (and leg, if that's what it's attached to) is stationary, it produces a relatively reliable reading.. say 300. As you lift your leg (tilting the accelerometer in the Z axis), the number increases. If you hold your leg at, say, a 45 degree angle, the accelerometer reads 400, and stays at that reading so long as you're keeping it there. So, I'm not really reading acceleration per se, but more of the leg position.

Where I'm encountering the problem is that when standing stationary and lifting your leg quickly, the serial output shows the values increasing, and at the highest point of the lift, seems to bounce. This does not happen when the leg (or accelerometer) is lifted/tilted as a slower speed. It's this bloody bounce effect that I can't figure out how to program out.

Skyminer:
La3ry - I'm using an ADXL335 accelerometer, but am using its raw inputs, not calculating for real acceleration. So, when the accelerometer (and leg, if that's what it's attached to) is stationary, it produces a relatively reliable reading.. say 300. As you lift your leg (tilting the accelerometer in the Z axis), the number increases. If you hold your leg at, say, a 45 degree angle, the accelerometer reads 400, and stays at that reading so long as you're keeping it there. So, I'm not really reading acceleration per se, but more of the leg position.

OK, I see that, but it brings up a few questions...

  1. Are you using a breakout board that will supply ~3V to the chip? If not, are you running it on the 3.3V pin?

  2. You are using the Z axis, so I assume the chip is horizontal (pin side aimed toward the ground). If this is the case, tilting it should DECREASE the reading, and increase either X or Y, and decrease X or Y, depending on direction of tilt.

  3. I will amend my descripton of a good test. The simple sketch should read all three axes, one after the other, and report all three at short intervals. If you move it in a walking motion, oriented as it will be when attached to a leg,, you may find better criteria for deciding leg rising/falling, etc. It is entirely possible that the best criteria will involve some combination of readings from 2 or more axes.

Where I'm encountering the problem is that when standing stationary and lifting your leg quickly, the serial output shows the values increasing, and at the highest point of the lift, seems to bounce. This does not happen when the leg (or accelerometer) is lifted/tilted as a slower speed. It's this bloody bounce effect that I can't figure out how to program out.

Well, if you figure out what readings mean which motion, you could throw away any short-term 'bumps' in the series. And I still think it's worth while finding a way to actually measure rising and falling, as oppoesed to leg tilt angle.

Try creating a short-term rolling average of the readings instead of using the raw readings. That will remove any short term spikes and give you a nice smooth reading.

Either that or add a capacitor across the accelerometer's output as per the data sheet to reduce the bandwidth.

Edit: Here's a bit I wrote a while back about using analogue accelerometers: http://hacking.majenko.co.uk/inertial-accelerometers

lar3ry:

  1. Are you using a breakout board that will supply ~3V to the chip? If not, are you running it on the 3.3V pin?

I'm using the 3.3v pin on the arduino to supply the power.

lar3ry:
2. You are using the Z axis, so I assume the chip is horizontal (pin side aimed toward the ground). If this is the case, tilting it should DECREASE the reading, and increase either X or Y, and decrease X or Y, depending on direction of tilt.

The chip is oriented to that if you're standing upright and the chip is on your thigh, the X axis is to the left and right, Y axis is up and down, and the Z axis is moving forward and backward.

lar3ry:
3. I will amend my descripton of a good test. The simple sketch should read all three axes, one after the other, and report all three at short intervals. If you move it in a walking motion, oriented as it will be when attached to a leg,, you may find better criteria for deciding leg rising/falling, etc. It is entirely possible that the best criteria will involve some combination of readings from 2 or more axes.

Yeah, the first thing I did was strap it to my leg, take a walk, and log all 3 axis. As expected, there was little movement in the X axis (side to side), and minimal in the Y axis (as your leg isn't really moving up and down all that much), but large movement in Z.

Again, I have it tracking the walking movement well and it does trigger properly when I want it to (foot coming down on the step).. it sees that just fine. It's the bounce effect when the sensor moves too quickly that's causing me the grief.

majenko:
Try creating a short-term rolling average of the readings instead of using the raw readings. That will remove any short term spikes and give you a nice smooth reading.

Either that or add a capacitor across the accelerometer's output as per the data sheet to reduce the bandwidth.

Edit: Here's a bit I wrote a while back about using analogue accelerometers: http://hacking.majenko.co.uk/inertial-accelerometers

Thanks for the tip. I did try a averaging raw input (the wrong way according to your page!), but will take another look at my code.