您的位置:首页 > 其它

【翻译】利用加速度求解位置的算法——三轴传感器

2015-03-30 19:21 901 查看
摘要

此文档描述并使用MMA7260QT三轴加速计和低功耗的9S08QG8八位单片机实现求解位置的算法 。

在今天先进的电子市场,有不少增加了许多特性和智能的多功能的产品。定位和游戏只是得益于获取到的位置信息的一部分市场。一个获取这种信息的可选方案是通过使用惯性传感器。从这些传感器中取得的信号需要进行一些处理,因为在加速度和位置之间没有一种直接转换。
为了获得位置,需要对加速度进行二次积分。本文介绍一种简单的算法实现加速度的二重积分。为了获取加速度的二重积分,一个简单的积分要进行两次,因为这样也可以顺便获取速度。
接下来要展示的算法,能够应该于任何传感轴,所以一维、二维、三维的位置都可以被计算出。当在获取三维位置信息时,要特别地除去重力加速度的影响。下面的算法实现还包括了一个二维系统的例子(比如鼠标)。

应用潜力
这种算法的应用潜力在于个人导航、汽车导航和(back-up)GPS、防盗设备、地图追踪、3D游戏、计算机鼠标等等。这类产品都需要用到求解位置信息的算法。
本文所介绍的算法在位移精度要求不是很严格的情况下很有用。其他的情况和影响特别是应用,当采用本文算法时,需要考虑一下。对最终程序进行微小的修改和调整,这种算法能够达到更高的精度。

理论知识和算法
理解本文算法的最好方法是回顾一下数学上的积分知识。
加速度是一个对象速度的变化速率。同时,速度是同样一个对象位置的变化速率。换句话说,速度是位置的导数,加速度是速度的导数,因此如下公式:

#include <hidef.h>
#include "derivative.h"
#include "adc.h"
#include "buzzer.h"
#include "SCItx.h"
#pragma DATA_SEG MY_ZEROPAGE
unsigned char near Sample_X;
unsigned char near Sample_Y;
unsigned char near Sample_Z;
unsigned char near Sensor_Data[8];
unsigned char near countx,county ;
signed int near accelerationx[2], accelerationy[2];
signed long near velocityx[2], velocityy[2];
signed long near positionX[2];
signed long near positionY[2];
signed long near positionZ[2];
unsigned char near direction;
unsigned long near sstatex,sstatey;
#pragma DATA_SEG DEFAULT
void init(void);
void Calibrate(void);
void data_transfer(void);
void concatenate_data(void);
void movement_end_check(void);
void position(void);
void main (void)
{
init();
get_threshold();
do
{
position();
}while(1);
}
/*******************************************************************************
The purpose of the calibration routine is to obtain the value of the reference threshold.
It consists on a 1024 samples average in no-movement condition.
********************************************************************************/
void Calibrate(void)
{
unsigned int count1;
count1 = 0;
do{
ADC_GetAllAxis();
sstatex = sstatex + Sample_X; // Accumulate Samples
sstatey = sstatey + Sample_Y;
count1++;
}while(count1!=0x0400); // 1024 times
sstatex=sstatex>>10; // division between 1024
sstatey=sstatey>>10;
}
/*****************************************************************************************/
/******************************************************************************************
This function obtains magnitude and direction
In this particular protocol direction and magnitude are sent in separate variables.
Management can be done in many other different ways.
*****************************************************************************************/
void data_transfer(void)
{
signed long positionXbkp;
signed long positionYbkp;
unsigned int delay;
unsigned char posx_seg[4], posy_seg[4];
if (positionX[1]>=0) { //This line compares the sign of the X direction data
direction= (direction | 0x10); //if its positive the most significant byte
posx_seg[0]= positionX[1] & 0x000000FF; // is set to 1 else it is set to 8
posx_seg[1]= (positionX[1]>>8) & 0x000000FF; // the data is also managed in the
// subsequent lines in order to
posx_seg[2]= (positionX[1]>>16) & 0x000000FF; // be sent. The 32 bit variable must be
posx_seg[3]= (positionX[1]>>24) & 0x000000FF; // split into 4 different 8 bit
// variables in order to be sent via
// the 8 bit SCI frame
}
else {direction=(direction | 0x80);
positionXbkp=positionX[1]-1;
positionXbkp=positionXbkp^0xFFFFFFFF;
posx_seg[0]= positionXbkp & 0x000000FF;
posx_seg[1]= (positionXbkp>>8) & 0x000000FF;
posx_seg[2]= (positionXbkp>>16) & 0x000000FF;
posx_seg[3]= (positionXbkp>>24) & 0x000000FF;
}
if (positionY[1]>=0) { // Same management than in the previous case
direction= (direction | 0x08); // but with the Y data.
posy_seg[0]= positionY[1] & 0x000000FF;
posy_seg[1]= (positionY[1]>>8) & 0x000000FF;
posy_seg[2]= (positionY[1]>>16) & 0x000000FF;
posy_seg[3]= (positionY[1]>>24) & 0x000000FF;
}
else {direction= (direction | 0x01);
positionYbkp=positionY[1]-1;
positionYbkp=positionYbkp^0xFFFFFFFF;
posy_seg[0]= positionYbkp & 0x000000FF;
posy_seg[1]= (positionYbkp>>8) & 0x000000FF;
posy_seg[2]= (positionYbkp>>16) & 0x000000FF;
posy_seg[3]= (positionYbkp>>24) & 0x000000FF;
}
delay = 0x0100;
Sensor_Data[0] = 0x03;
Sensor_Data[1] = direction;
Sensor_Data[2] = posx_seg[3];
Sensor_Data[3] = posy_seg[3];
Sensor_Data[4] = 0x01;
Sensor_Data[5] = 0x01;
Sensor_Data[6] = END_OF_FRAME;
while (--delay);
SCITxMsg(Sensor_Data); // Data transferring function
while (SCIC2 & 0x08);
}
/*****************************************************************************************/
/******************************************************************************************
This function returns data format to its original state. When obtaining the magnitude and
direction of the position, an inverse two's complement is made. This function makes the two's
complement in order to return the data to it original state.
It is important to notice that the sensibility adjustment is greatly impacted here, the amount
of "ones" inserted in the mask must be equivalent to the "ones" lost in the shifting made in
the previous function upon the sensibility modification.
******************************************************************************************/
void data_reintegration(void)
{
if (direction >=10)
{positionX[1]= positionX[1]|0xFFFFC000;} // 18 "ones" inserted. Same size as the
//amount of shifts
direction = direction & 0x01;
if (direction ==1)
{positionY[1]= positionY[1]|0xFFFFC000;}
}
/******************************************************************************************
This function allows movement end detection. If a certain number of acceleration samples are
equal to zero we can assume movement has stopped. Accumulated Error generated in the velocity
calculations is eliminated by resetting the velocity variables. This stops position increment
and greatly eliminates position error.
******************************************************************************************/
void movement_end_check(void)
{
if (accelerationx[1]==0) //we count the number of acceleration samples that equals cero
{ countx++;}
else { countx =0;}
if (countx>=25) //if this number exceeds 25, we can assume that velocity is cero
{
velocityx[1]=0;
velocityx[0]=0;
}
if (accelerationy[1]==0) //we do the same for the Y axis
{ county++;}
else { county =0;}
if (county>=25)
{
velocityy[1]=0;
velocityy[0]=0;
}
}
/*****************************************************************************************/
/******************************************************************************************
This function transforms acceleration to a proportional position by integrating the
acceleration data twice. It also adjusts sensibility by multiplying the "positionX" and
"positionY" variables.
This integration algorithm carries error, which is compensated in the "movenemt_end_check"
subroutine. Faster sampling frequency implies less error but requires more memory. Keep in
mind that the same process is applied to the X and Y axis.
*****************************************************************************************/
void position(void)
{
unsigned char count2 ;
count2=0;
do{
ADC_GetAllAxis();
accelerationx[1]=accelerationx[1] + Sample_X; //filtering routine for noise attenuation
accelerationy[1]=accelerationy[1] + Sample_Y; //64 samples are averaged. The resulting
//average represents the acceleration of
//an instant
count2++;
}while (count2!=0x40); // 64 sums of the acceleration sample
accelerationx[1]= accelerationx[1]>>6; // division by 64
accelerationy[1]= accelerationy[1]>>6;
accelerationx[1] = accelerationx[1] - (int)sstatex; //eliminating zero reference
//offset of the acceleration data
accelerationy[1] = accelerationy[1] - (int)sstatey; // to obtain positive and negative
//acceleration
if ((accelerationx[1] <=3)&&(accelerationx[1] >= -3)) //Discrimination window applied
{accelerationx[1] = 0;} // to the X axis acceleration
//variable
if ((accelerationy[1] <=3)&&(accelerationy[1] >= -3))
{accelerationy[1] = 0;}
//first X integration:
velocityx[1]= velocityx[0]+ accelerationx[0]+ ((accelerationx[1] -accelerationx[0])>>1);
//second X integration:
positionX[1]= positionX[0] + velocityx[0] + ((velocityx[1] - velocityx[0])>>1);
//first Y integration:
velocityy[1] = velocityy[0] + accelerationy[0] + ((accelerationy[1] -accelerationy[0])>>1);
//second Y integration:
positionY[1] = positionY[0] + velocityy[0] + ((velocityy[1] - velocityy[0])>>1);
accelerationx[0] = accelerationx[1]; //The current acceleration value must be sent
//to the previous acceleration
accelerationy[0] = accelerationy[1]; //variable in order to introduce the new
//acceleration value.
velocityx[0] = velocityx[1]; //Same done for the velocity variable
velocityy[0] = velocityy[1];
positionX[1] = positionX[1]<<18; //The idea behind this shifting (multiplication)
//is a sensibility adjustment.
positionY[1] = positionY[1]<<18; //Some applications require adjustments to a
//particular situation
//i.e. mouse application
data_transfer();
positionX[1] = positionX[1]>>18; //once the variables are sent them must return to
positionY[1] = positionY[1]>>18; //their original state
movement_end_check();
positionX[0] = positionX[1]; //actual position data must be sent to the
positionY[0] = positionY[1]; //previous position
direction = 0; // data variable to direction variable reset
}
/*****************************************************************************************/


View Code

原理图



总结
本文档为利用加速度计实现定位算法提供了一些基本的概念。
这种特定的算法在对位移精度要求不是很严格中非常有用。其他注意事项和具体的应用程序的影响应该在实现这个示例时被考虑。

这种积分算法适合于低端嵌入式应用,因为它的简单性和少量的指令。它也没有涉及任何浮点运算。

原文链接:http://cache.freescale.com/files/sensors/doc/app_note/AN3397.pdf?fsrch=1&sr=2
本文链接:/article/7044966.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: