I am working on a project to calculate speed (Velocity when I get an IMU) of finger taps and then to apply them for further applications like music, hand gesture recognition etc. I have been able to work the integration well enough due to the short time spaces between hand/finger movements.
But, I was getting an unwanted delay and false velocity in opposite directions when the finger reached the end of one vertical movement, i.e. Top/summit to bottom/tabletop and vice versa. I expect to get positive acceleration with downward motion as per my program, attached below. I get correct readings until I hit the tabletop which results in an amplified or equal and opposite acceleration. Similar but reversed phenomenon for the upwards motion and stopping at a summit. Graphs attached.
The first phenomenon of "false negative" during downwards motion doesn't affect my program and hence is categorized as benign. But, it is useful for analysis.
The second phenomenon of "false positive" during upwards motion does affect my program and hence is categorized as Negative effect/Unwanted effect.
I am using an ADXL335 accelerometer and the datasheet is also attached.
My core program with the integration is beyond the scope of this problem and not a cause, therefore not attached here.
Kindly ignore the smoothening snippets of the code as they did not help with this issue. I can however compile the data with graphs for those snippets, if it will help the error analysis. I used a 100ms delay and 50Hz BW for the third datasheet/graph. The program I used to get the data and graphs below:
# define Xpin 0
# define Ypin 1
# define Zpin 2
const byte LED = 12; // Calibration in process or not.
const byte BUTTON = 3; // Press to calibrate.
const int Axis = 3;
const float ZeroPoint[] = { 1.64, 1.64 , 1.655}; // Calculated practically.
const float Sensitivity = 0.33;
float GravCorrection = 1.0;
float R_Curr = 0; float R_Prev = 0;
int Sign = +1;
volatile boolean CaliFlag = false;
/*
* Interrupt to notice Calibration request from digital switch. Later built in Android. etc.:
* Set the caliFlag when switch on. Off otherwise.
*/
void switchPressed ()
{
if ( digitalRead (BUTTON) == LOW )
{
digitalWrite (LED, HIGH);
CaliFlag = true;
}
else
{
digitalWrite (LED, LOW);
CaliFlag = false;
}
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
analogReference(EXTERNAL);
pinMode (LED, OUTPUT); // So we can update the LED
pinMode (BUTTON, INPUT_PULLUP); // Internal pull-up resistor
attachInterrupt (digitalPinToInterrupt (BUTTON), switchPressed, CHANGE); // Attach interrupt handler.
}
void loop() {
// put your main code here, to run repeatedly:
float Rx = 0; float Ry = 0; float Rz = 0;
float R = 0;
if ( CaliFlag == true ) // To calculate the gravitational acceleration to be removed.
{
Serial.println ("Calibration will begin in one second. Keep the device flat on play surface.");
delay(1000);
GravCorrection = getR ( xlRation(analogRead( Xpin ), ZeroPoint[Xpin]), xlRation(analogRead( Ypin ), ZeroPoint[Ypin]), xlRation(analogRead( Zpin ), ZeroPoint[Zpin]) );
Serial.println ("Moving to play mode.");
Serial.print ("Gravity Correction detected = " );
Serial.println (GravCorrection);
return;
}
Rx = xlRation ( analogRead(Xpin), ZeroPoint[Xpin] );
Ry = xlRation ( analogRead(Ypin), ZeroPoint[Ypin] );
Rz = xlRation ( analogRead(Zpin), ZeroPoint[Zpin] );
//Test for cumulative Acceleration, R:
// Acceleration in Gs without Gravity Correction
R = getR ( Rx, Ry, Rz );
R_Curr = GravCorrection - R; // Corrected for gravity.
Serial.print("R_Curr = ");
Serial.println(R_Curr);
//delay(100);
R_Prev = R_Curr;
}
float xlRation ( int AdcReading, float ZeroPoint )
{
return ((( AdcReading * 3.3/1023 ) - ZeroPoint )/Sensitivity );
}
float getR ( float Rx, float Ry, float Rz ) //Calibrate for each new position to set gravity correction
//value, i.e. NIC.
{
float R = 0.0;
if ( Rz <= 0 ) // Sign to signify downwards and upwards acceleration respectively.
Sign = -1; // Downwards as in normal equations.
else
Sign = +1; // Upwards as in normal equations.
R = Sign * sqrt( sq(Rx) + sq(Ry) + sq(Rz) );
return R;
}
Possible reasons:
- My first reaction and theory was that this is due to noise but various denoising methods did not help. It still might be.
- My second theory was that this is due to Newton's third law: Every action has an equal and opposite reaction. As it occurs for both upwards and downwards motion, it seems to be the likeliest cause.
Can someone definitively tell me what the reason is? And maybe how I can work on fixing/removing it?
That maximum delay per loop I can allow is 166ms which results in 6 beats per seconds. Ideally, I want to be able to process double of that.
And no, I did not move the accelerometer in the opposite direction. For the first, It goes down and then hits the table to a stop. For the second, it goes up and I stop and keep it still for a while.
adxl335_DataSheet.pdf (427 KB)



