|
|
View previous topic :: View next topic |
Author |
Message |
Gerhard
Joined: 30 Aug 2007 Posts: 144 Location: South Africa
|
Acceleration to speed conversion problem |
Posted: Tue Apr 15, 2008 4:29 pm |
|
|
I have a 3 axis accelerometer that gives 1.333v at -1g and 1.666v at 0g and 1.999v at 1g. This is a 333mv per g change.
Problem is that i get the correct measurement for acceleration but when i try to convert this by finding the iterative integral of acceleration over time i do not get the correct value for speed and i almost get no value.
The adc value is also fluctuating quite a bit and that causes the accelerative measurement at 1g to jump between 8.7 and 10.5m/ss.
This is the code i have written and would apreciate some insite into a better way to do it.
Code: | #include <16F886.H>
#device adc=10
#fuses INTRC, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock = 4000000)
#include <Flex_LCD420.c>
//===================================
void main()
{
float z1 = 0;
float x1,y1,dx1,vx2,vx3;
int16 adc_value;
lcd_init();
printf(lcd_putc, "\f");
delay_ms(500);
set_TRIS_A(0xff);
setup_adc_ports(sAN0|sAN1|sAN2);
setup_adc( ADC_CLOCK_DIV_8 );
while(1)
{
set_adc_channel(0); //X direction
delay_us(20);
x1 = (float)((read_adc()-327.118)*0.1407); //gives adc value as acceleration as ex 9.85m/ss for 1g gravity
dx1 = (x1-vx2); //difference between previous and current acceleration
vx2 = x1; //sets previous acceleration to current acceleration
vx3 = vx3+(dx1*0.67); //difference between current and previous
//acceleration da times time to get speed
set_adc_channel(1); //Y direction
delay_us(20);
y1 = (float)(read_adc()*5)/1023;
y1 = (y1-1.66)*29.579;
set_adc_channel(2); //Z direction
delay_us(20);
z1 = (float)(read_adc()*5)/1023;
z1 = (z1-1.66)*29.579;
delay_ms(50);
lcd_gotoxy(1,1);
printf(lcd_putc, "X Direction");
lcd_gotoxy(16,1);
printf(lcd_putc,"%3.2f", x1);
lcd_gotoxy(1,2);
lcd_putc("X speed ");
lcd_gotoxy(14,2);
printf(LCD_PUTC, "%3.2f",vx3);
lcd_gotoxy(1,3);
lcd_putc("Z Direction");
lcd_gotoxy(15,3);
printf(LCD_PUTC, "%3.2f",z1);
delay_ms(500);
}
} |
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Apr 15, 2008 4:42 pm |
|
|
1. Post the manufacturer and part number of the accelerometer.
2. Post a description of the external connections and components between
the PIC and the accelerometer. Are you using any op-amp buffers
between the accelerometer and the PIC ? |
|
|
Gerhard
Joined: 30 Aug 2007 Posts: 144 Location: South Africa
|
|
Posted: Tue Apr 15, 2008 4:50 pm |
|
|
The accelerometer is a DE-ACCM3D Buffered ±3g Tri-axis Accelerometer
available from dimension engineering
http://www.dimensionengineering.com/DE-ACCM3D.htm
There are no external conections required. The x output is conected directly to A0 and Y to A1 and Z to A2 on the pic with a 5v supply to both the pic and the accelerometer. I do not use any opamps or buffers as they are already built into the accelerometer |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Apr 15, 2008 5:34 pm |
|
|
Quote: |
The adc value is also fluctuating quite a bit and that causes the
accelerative measurement at 1g to jump between 8.7 and 10.5m/ss. |
Do a test to see which device is causing the noise -- the PIC or
the accelerometer.
1. Disconnect two of the accelerometer inputs from the PIC, and connect
those two input pins to ground. So now only one A/D input is
connected to the accelerometer.
2. Edit your program so that it only reads one A/D channel. Display the
result as a 16-bit hex (or unsigned decimal) value. Don't do any math
operations on the result.
3. Run the program and write down the range of fluctuation.
4. Now disconnect the accelerometer from the PIC and substitute a 1K
trimpot. Adjust the trimpot so it gives roughly the same output value
as the accelerometer and note the amount of fluctuation (if any).
If the fluctuation is coming from the accelerometer, then check if your
table is vibrating, or if some calibration is required, or check the data
sheet of the ADXL device for any tips or hints, or contact the vendor
of the module. |
|
|
Gerhard
Joined: 30 Aug 2007 Posts: 144 Location: South Africa
|
|
Posted: Tue Apr 15, 2008 5:45 pm |
|
|
Is there a reason that it is recommended to use an external oscillator rather then internal oscillator especially when doing A/D conversions?
I will do those tests and post the result tomorrow as it is 01:40 already in SA |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
Re: Acceleration to speed conversion problem |
Posted: Tue Apr 15, 2008 5:52 pm |
|
|
Gerhard wrote: | Code: |
set_adc_channel(0); //X direction
delay_us(20);
x1 = (float)((read_adc()-327.118)*0.1407); //gives adc value as acceleration as ex 9.85m/ss for 1g gravity
dx1 = (x1-vx2); //difference between previous and current acceleration
vx2 = x1; //sets previous acceleration to current acceleration
vx3 = vx3+(dx1*0.67); //difference between current and previous
|
|
The problem is you are not integrating the acceleration. You are differentiating it and then integrating it, which cancels out. vx3 is essentially the same as x1 * (some factor).
If analog channel 0 is acceleration in the x direction, then what is the point of finding out how much the acceleration changed from the previous sample? If you want to integrate x1, then just do this:
Code: |
vx3 = vx3 + x1 * factor;
|
where factor is a factor the relates to the mass that is being accelerated.
Robert Scott
Real-Time Specialties |
|
|
Gerhard
Joined: 30 Aug 2007 Posts: 144 Location: South Africa
|
|
Posted: Tue Apr 15, 2008 6:09 pm |
|
|
Silly mistake
thanks for pointing it out. |
|
|
Gerhard
Joined: 30 Aug 2007 Posts: 144 Location: South Africa
|
|
Posted: Tue Apr 15, 2008 6:28 pm |
|
|
I changed it and also added the part to get the displacement but still the speed and displacement values gives me 0.00 although the acceleration is changes corectly.
Code: | #include <16F886.H>
#device adc=10
#fuses INTRC, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock = 4000000)
#include <Flex_LCD420.c>
//===================================
void main()
{
float z1 = 0;
float x1,y1,dx1,vx,vx1,s;
int16 adc_value;
lcd_init();
printf(lcd_putc, "\f");
delay_ms(500);
set_TRIS_A(0xff);
setup_adc_ports(sAN0|sAN1|sAN2);
setup_adc( ADC_CLOCK_DIV_8 );
while(1)
{
set_adc_channel(0); //X direction
delay_us(20);
x1 = (float)((read_adc()-327.118)*0.1407); //gives adc value as acceleration as ex 9.85m/ss for 1g gravity
vx = vx1+(x1*0.67);
vx1 = vx;
s = vx1*0.67+0.5*x1*.4489;
set_adc_channel(1); //Y direction
delay_us(20);
y1 = (float)(read_adc()*5)/1023;
y1 = (y1-1.66)*29.579;
set_adc_channel(2); //Z direction
delay_us(20);
z1 = (float)(read_adc()*5)/1023;
z1 = (z1-1.66)*29.579;
delay_ms(50);
lcd_gotoxy(1,1);
printf(lcd_putc, "X Direction");
lcd_gotoxy(16,1);
printf(lcd_putc,"%3.2f", x1);
lcd_gotoxy(1,2);
lcd_putc("X speed ");
lcd_gotoxy(14,2);
printf(LCD_PUTC, "%3.2f",vx);
lcd_gotoxy(1,3);
lcd_putc("X Displacement");
lcd_gotoxy(15,3);
printf(LCD_PUTC, "%3.2f",s);
delay_ms(500);
}
} | [/code] |
|
|
Ttelmah Guest
|
|
Posted: Wed Apr 16, 2008 3:16 am |
|
|
Lots of little things to try.
You don't say whether you are using the internal regulator, or another source?. What are you using for Vref to the PIC?. You really need to be able to feed both devices from the same voltage at this point, since otherwise a tiny shift in either voltage will be seen as a small acceleration, and when integrating, such small changes, if continued for a long time, will resut in huge errors. Ideally, feed the PIC's ADC Vref, from the 3.3v output on the board.
There is a tiny (and common), error in the ADC conversions for the channels. On most ADC's, the output is V/((2^Nbits)-1) per step. So for a 10bit ADC off 5v, is 5/1023. However the PIC ADC is unusual in this regard, with where the change levels occur, is most closely aproximated, by V/(2^Nbits). There is an application note about this on the MicroChip site (for the older 16 chips), but the basic principle of basically all the PIC ADC's, is the same.
Now, there is no point in using a float value for the subtraction from the ADC reading, since the incoming value to the calculation, can only ever be at integer steps. Better, to consider some form of fractional damping algorithm, at the moment of the reading, which then can give you a long term floating point value. Consider something like:
Code: |
int16 adsum[3];
float Gvals[3];
float Gzero[3];
float Factor[3];
int16 temp;
float tempflt;
int count,axis;
//Now really, you _need_ to calibrate the 'zero' values accurately
//yourself, rather than just taking constant values. Otherwise even a
//tiny error in these, will lead to integration errors...
//However setting these 'in theory' for now. - assuming Vref=3.3v
Gzero[0] = 616.36732;
Gzero[1] = Gzero[2] = 512;
Factor[0] = Factor[1] = Factor[2] = 9.677646E-3; //G per step
while (true) {
for (axis=0;axis<3;axis++) {
set_adc_channel(axis);
for (count=0;count<4;count++) {
//Now sum four readings for each channel
delay_us(10);
adsum[axis]+=read_adc();
}
temp=adsum[axis]/4;
adsum[axis]-=(temp*4);
//This arithmetic is quick, because it is performed all in integer.
//Key though is, that 'adsum', _long term_, will build a slightly
//better approximation to the real value being presented on the
//input.
Gvals[axis]=((adsum[axis]*0.25)-Gzero[axis])*factor;
//Note that in float arithmetic, a multiply, is much faster than a divide
//Hence multiplying by the _reciprocal_ of required factors.
}
//At this point the three G values, are in Gvals[0] to Gvals[2] (for X,
//Y, Z), and should be significantly more repeatable and accurate than
//your current values, without taking much more time (the time saved
//in the floating point arithmetic, will help to make up for the extra
//sampling.
}
|
There should be a _massive_improvement, from using the units Vref output. Otherwise any change in the PIC's supply, will really spoil the figures. The averaging, should improve it further.
Best Wishes |
|
|
Douglas Kennedy
Joined: 07 Sep 2003 Posts: 755 Location: Florida
|
|
Posted: Wed Apr 16, 2008 4:00 am |
|
|
Speed is the scalar integration of acceleration with respect to time. The incremental increases in speed as a result of acceleration within a small time increment ( small enough so that the acceleration is reasonably constant) can be approximated by summation ( the prior time intervals speed has added to it the small increment from the acceleration). Gravity is 32ft per sec per sec. The speed of a falling object from rest is 32 ft per sec after one second and 64 ft per second after two seconds. This gravity example has too rough a time interval ( one second)to estimate instantaneous speed even though gravity( acceleration was absolutely constant and time was absolutely accurate).The instantaneous speed error will be from the acceleration readings themselves and from the time interval measurement. The PIC must measure the acceleration to best precision and the time interval to equal accuracy. To get the best integration (speed) the time interval must be small but within that interval the acceleration must be read with the maximum precision. The speed derived will suffer from a tendency to accumulate error with the passage of time. |
|
|
Guest
|
|
Posted: Wed Apr 16, 2008 5:07 am |
|
|
Hello I am working on similar kind of project, and yes the Acceleration value does fluctuate +/- 0.3 m/s2.
I am still trying to make it perfect. Note that you will ahve to calibrate the accelemeter PERFECTLY!
Here is a small part of my code. May be it will help you. It might not be the best code though. lol
Please note I am generating timer0 (RTCC) interrupt after every 200ms.
Code: |
#define Time 0.2 //200ms
#define Y_cal_mid 1.71493
#define X_cal_mid 1.67049
#define Z_cal_mid 2.05554
#define Z_cal_mid_1 2.05554-0.323
#define cal_G_force_Y 0.338
#define cal_G_force_X 0.346
#define cal_G_force_Z 0.323
#INT_RTCC FAST
void read_analog_pin()
{
set_timer0(3035); //3035-32 for 200ms
Time_monitor++;
setup_adc_ports(AN0_to_AN2 | VSS_VREF);
delay_us(10);
for(adc_loop=0; adc_loop<10; adc_loop++)
{
set_adc_channel(0);
delay_us(10);
ADC_filter[adc_loop] = read_adc();
delay_us(10);
set_adc_channel(1);
delay_us(10);
ADC_filter1[adc_loop] = read_adc();
delay_us(10);
set_adc_channel(2);
delay_us(10);
ADC_filter2[adc_loop] = read_adc();
delay_us(10);
}
calculation();
}
void calculation()
{
ADC_sum = 0;
for(adc_loop=0; adc_loop<10; adc_loop++)
{
ADC_sum = ADC_filter[adc_loop] + ADC_sum;
}
Dig_Acc_Y = ADC_sum / 10;
ADC_sum = 0;
for(adc_loop=0; adc_loop<10; adc_loop++)
{
ADC_sum = ADC_filter1[adc_loop] + ADC_sum;
}
Dig_Acc_X = ADC_sum / 10;
ADC_sum = 0;
for(adc_loop=0; adc_loop<10; adc_loop++)
{
ADC_sum = ADC_filter2[adc_loop] + ADC_sum;
}
Dig_Acc_Z = ADC_sum / 10;
Y_Actual_Acc = ((( Y_cal_mid - ((Dig_Acc_Y/1023) * 3.03))/ cal_G_force_Y) * 9.80665);
X_Actual_Acc = ((( X_cal_mid - ((Dig_Acc_X/1023) * 3.03))/ cal_G_force_X) * 9.80665);
Z_Actual_Acc = (((Z_cal_mid_1 - ((Dig_Acc_Z/1023) * 3.03))/ cal_G_force_Z) * 9.80665);
Inst_Velocity = Y_Actual_Acc * Time; // m/s
New_velocity = New_velocity + Inst_Velocity; // m/s
Standard_velocity = New_Velocity * 2.2369; // mph
Recent_distance_travelled = fabs(New_velocity * Time); // m
Cummulative_Distance_travelled = (Cummulative_Distance_travelled + Recent_distance_travelled); |
|
|
|
Izzy
Joined: 27 May 2007 Posts: 106
|
|
Posted: Wed Apr 16, 2008 5:08 am |
|
|
Sorry I forgot to log in before.
Please note that my calibration is not perfect yet and will differ from yours. |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
|
Posted: Wed Apr 16, 2008 6:10 am |
|
|
Gerhard wrote: | I changed it and also added the part to get the displacement but still the speed and displacement values gives me 0.00 although the acceleration is changes corectly.
|
Are you saying that printf(lcd_putc,"%3.2f", x1); is printing out reasonable values for x1, but that printf(LCD_PUTC, "%3.2f",vx); is always printing out 0.00? That does not seem possible, since vx is just an accumulation of x1 * 0.67.
I don't understand your use of vx and vx1. Why are you using two variables when only one variable is needed?
Also it is bad practice to use local variables before they have been initialized. vx and vx1 are local variables and your first reference to vx1 is to use it in the calculation of vx.
Robert Scott
Real-Time Specialties |
|
|
GERHARD L Guest
|
|
Posted: Wed Apr 16, 2008 7:24 am |
|
|
I got the readings to come in correctly so thanks to all. I am trying something different now regarding interupt timer so as to be able to get the correct timing between readings of the accelerometer.
My biggest problem was decoupling caps and that solved the fluctuation. |
|
|
Izzy Prad Guest
|
|
Posted: Wed Apr 16, 2008 9:52 am |
|
|
GERHARD L wrote: | I got the readings to come in correctly so thanks to all. I am trying something different now regarding interupt timer so as to be able to get the correct timing between readings of the accelerometer.
My biggest problem was decoupling caps and that solved the fluctuation. |
Hello,
I know you may think decoupling a cap might solve the fluctuation, but I have noticed decoupleing a cap give you lil higher reading which will end up giving you wrong data. The data will be off by little bit.
Atleast thats what I experienced. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|