CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Acceleration to speed conversion problem

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Gerhard



Joined: 30 Aug 2007
Posts: 144
Location: South Africa

View user's profile Send private message Send e-mail

Acceleration to speed conversion problem
PostPosted: Tue Apr 15, 2008 4:29 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Apr 15, 2008 4:42 pm     Reply with quote

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

View user's profile Send private message Send e-mail

PostPosted: Tue Apr 15, 2008 4:50 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Apr 15, 2008 5:34 pm     Reply with quote

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

View user's profile Send private message Send e-mail

PostPosted: Tue Apr 15, 2008 5:45 pm     Reply with quote

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

View user's profile Send private message

Re: Acceleration to speed conversion problem
PostPosted: Tue Apr 15, 2008 5:52 pm     Reply with quote

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

View user's profile Send private message Send e-mail

PostPosted: Tue Apr 15, 2008 6:09 pm     Reply with quote

Silly mistake
thanks for pointing it out.
Gerhard



Joined: 30 Aug 2007
Posts: 144
Location: South Africa

View user's profile Send private message Send e-mail

PostPosted: Tue Apr 15, 2008 6:28 pm     Reply with quote

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







PostPosted: Wed Apr 16, 2008 3:16 am     Reply with quote

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

View user's profile Send private message AIM Address

PostPosted: Wed Apr 16, 2008 4:00 am     Reply with quote

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








PostPosted: Wed Apr 16, 2008 5:07 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Apr 16, 2008 5:08 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Apr 16, 2008 6:10 am     Reply with quote

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







PostPosted: Wed Apr 16, 2008 7:24 am     Reply with quote

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







PostPosted: Wed Apr 16, 2008 9:52 am     Reply with quote

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.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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