Sensor Fusion视频看起来很棒,但没有代码:http://www.youtube.com/watch?v=C7JQ7Rpwn2k
这是我的代码,只使用加速度计和指南针。我还在3个方向值上使用了一个卡尔曼滤波器,但是这里显示的代码太多了。最终,这工作得很好,但是结果要么太不稳定要么太滞后,这取决于我对结果做了什么以及我把过滤因子设置得有多低。
/** Just accelerometer and magnetic sensors */
public abstract class SensorsListener2
implements
SensorEventListener
{
/** The lower this is, the greater the preference which is given to previous values. (slows change) */
private static final float accelFilteringFactor = 0.1f;
private static final float magFilteringFactor = 0.01f;
public abstract boolean getIsLandscape();
@Override
public void onSensorChanged(SensorEvent event) {
Sensor sensor = event.sensor;
int type = sensor.getType();
switch (type) {
case Sensor.TYPE_MAGNETIC_FIELD:
mags[0] = event.values[0] * magFilteringFactor + mags[0] * (1.0f - magFilteringFactor);
mags[1] = event.values[1] * magFilteringFactor + mags[1] * (1.0f - magFilteringFactor);
mags[2] = event.values[2] * magFilteringFactor + mags[2] * (1.0f - magFilteringFactor);
isReady = true;
break;
case Sensor.TYPE_ACCELEROMETER:
accels[0] = event.values[0] * accelFilteringFactor + accels[0] * (1.0f - accelFilteringFactor);
accels[1] = event.values[1] * accelFilteringFactor + accels[1] * (1.0f - accelFilteringFactor);
accels[2] = event.values[2] * accelFilteringFactor + accels[2] * (1.0f - accelFilteringFactor);
break;
default:
return;
}
if(mags != null && accels != null && isReady) {
isReady = false;
SensorManager.getRotationMatrix(rot, inclination, accels, mags);
boolean isLandscape = getIsLandscape();
if(isLandscape) {
outR = rot;
} else {
// Remap the coordinates to work in portrait mode.
SensorManager.remapCoordinateSystem(rot, SensorManager.AXIS_X, SensorManager.AXIS_Z, outR);
}
SensorManager.getOrientation(outR, values);
double x180pi = 180.0 / Math.PI;
float azimuth = (float)(values[0] * x180pi);
float pitch = (float)(values[1] * x180pi);
float roll = (float)(values[2] * x180pi);
// In landscape mode swap pitch and roll and invert the pitch.
if(isLandscape) {
float tmp = pitch;
pitch = -roll;
roll = -tmp;
azimuth = 180 - azimuth;
} else {
pitch = -pitch - 90;
azimuth = 90 - azimuth;
}
onOrientationChanged(azimuth,pitch,roll);
}
}
private float[] mags = new float[3];
private float[] accels = new float[3];
private boolean isReady;
private float[] rot = new float[9];
private float[] outR = new float[9];
private float[] inclination = new float[9];
private float[] values = new float[3];
/**
Azimuth: angle between the magnetic north direction and the Y axis, around the Z axis (0 to 359). 0=North, 90=East, 180=South, 270=West
Pitch: rotation around X axis (-180 to 180), with positive values when the z-axis moves toward the y-axis.
Roll: rotation around Y axis (-90 to 90), with positive values when the x-axis moves toward the z-axis.
*/
public abstract void onOrientationChanged(float azimuth, float pitch, float roll);
}
我试图找出如何添加陀螺仪数据,但我就是做不对。http://developer . Android . com/reference/Android/hardware/sensor event . html上的google文档显示了一些从陀螺仪数据中获取增量矩阵的代码。这个想法似乎是,我会降低加速度计和磁传感器的滤波器,使它们真正稳定。这将跟踪长期的方向。
然后,我会从陀螺仪中保留最近的N δ矩阵的历史记录。每次我得到一个新的,我都会放下最古老的一个,把它们全部乘以得到一个最终的矩阵,我会乘以加速度计和磁性传感器返回的稳定矩阵。
这似乎行不通。或者,至少,我的实现是行不通的。其结果远比加速计的抖动大。增加陀螺仪历史记录的大小实际上会增加抖动,这让我觉得我没有从陀螺仪中计算出正确的值。
public abstract class SensorsListener3
implements
SensorEventListener
{
/** The lower this is, the greater the preference which is given to previous values. (slows change) */
private static final float kFilteringFactor = 0.001f;
private static final float magKFilteringFactor = 0.001f;
public abstract boolean getIsLandscape();
@Override
public void onSensorChanged(SensorEvent event) {
Sensor sensor = event.sensor;
int type = sensor.getType();
switch (type) {
case Sensor.TYPE_MAGNETIC_FIELD:
mags[0] = event.values[0] * magKFilteringFactor + mags[0] * (1.0f - magKFilteringFactor);
mags[1] = event.values[1] * magKFilteringFactor + mags[1] * (1.0f - magKFilteringFactor);
mags[2] = event.values[2] * magKFilteringFactor + mags[2] * (1.0f - magKFilteringFactor);
isReady = true;
break;
case Sensor.TYPE_ACCELEROMETER:
accels[0] = event.values[0] * kFilteringFactor + accels[0] * (1.0f - kFilteringFactor);
accels[1] = event.values[1] * kFilteringFactor + accels[1] * (1.0f - kFilteringFactor);
accels[2] = event.values[2] * kFilteringFactor + accels[2] * (1.0f - kFilteringFactor);
break;
case Sensor.TYPE_GYROSCOPE:
gyroscopeSensorChanged(event);
break;
default:
return;
}
if(mags != null && accels != null && isReady) {
isReady = false;
SensorManager.getRotationMatrix(rot, inclination, accels, mags);
boolean isLandscape = getIsLandscape();
if(isLandscape) {
outR = rot;
} else {
// Remap the coordinates to work in portrait mode.
SensorManager.remapCoordinateSystem(rot, SensorManager.AXIS_X, SensorManager.AXIS_Z, outR);
}
if(gyroUpdateTime!=0) {
matrixHistory.mult(matrixTmp,matrixResult);
outR = matrixResult;
}
SensorManager.getOrientation(outR, values);
double x180pi = 180.0 / Math.PI;
float azimuth = (float)(values[0] * x180pi);
float pitch = (float)(values[1] * x180pi);
float roll = (float)(values[2] * x180pi);
// In landscape mode swap pitch and roll and invert the pitch.
if(isLandscape) {
float tmp = pitch;
pitch = -roll;
roll = -tmp;
azimuth = 180 - azimuth;
} else {
pitch = -pitch - 90;
azimuth = 90 - azimuth;
}
onOrientationChanged(azimuth,pitch,roll);
}
}
private void gyroscopeSensorChanged(SensorEvent event) {
// This timestep's delta rotation to be multiplied by the current rotation
// after computing it from the gyro sample data.
if(gyroUpdateTime != 0) {
final float dT = (event.timestamp - gyroUpdateTime) * NS2S;
// Axis of the rotation sample, not normalized yet.
float axisX = event.values[0];
float axisY = event.values[1];
float axisZ = event.values[2];
// Calculate the angular speed of the sample
float omegaMagnitude = (float)Math.sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);
// Normalize the rotation vector if it's big enough to get the axis
if(omegaMagnitude > EPSILON) {
axisX /= omegaMagnitude;
axisY /= omegaMagnitude;
axisZ /= omegaMagnitude;
}
// Integrate around this axis with the angular speed by the timestep
// in order to get a delta rotation from this sample over the timestep
// We will convert this axis-angle representation of the delta rotation
// into a quaternion before turning it into the rotation matrix.
float thetaOverTwo = omegaMagnitude * dT / 2.0f;
float sinThetaOverTwo = (float)Math.sin(thetaOverTwo);
float cosThetaOverTwo = (float)Math.cos(thetaOverTwo);
deltaRotationVector[0] = sinThetaOverTwo * axisX;
deltaRotationVector[1] = sinThetaOverTwo * axisY;
deltaRotationVector[2] = sinThetaOverTwo * axisZ;
deltaRotationVector[3] = cosThetaOverTwo;
}
gyroUpdateTime = event.timestamp;
SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector);
// User code should concatenate the delta rotation we computed with the current rotation
// in order to get the updated rotation.
// rotationCurrent = rotationCurrent * deltaRotationMatrix;
matrixHistory.add(deltaRotationMatrix);
}
private float[] mags = new float[3];
private float[] accels = new float[3];
private boolean isReady;
private float[] rot = new float[9];
private float[] outR = new float[9];
private float[] inclination = new float[9];
private float[] values = new float[3];
// gyroscope stuff
private long gyroUpdateTime = 0;
private static final float NS2S = 1.0f / 1000000000.0f;
private float[] deltaRotationMatrix = new float[9];
private final float[] deltaRotationVector = new float[4];
//TODO: I have no idea how small this value should be.
private static final float EPSILON = 0.000001f;
private float[] matrixMult = new float[9];
private MatrixHistory matrixHistory = new MatrixHistory(100);
private float[] matrixTmp = new float[9];
private float[] matrixResult = new float[9];
/**
Azimuth: angle between the magnetic north direction and the Y axis, around the Z axis (0 to 359). 0=North, 90=East, 180=South, 270=West
Pitch: rotation around X axis (-180 to 180), with positive values when the z-axis moves toward the y-axis.
Roll: rotation around Y axis (-90 to 90), with positive values when the x-axis moves toward the z-axis.
*/
public abstract void onOrientationChanged(float azimuth, float pitch, float roll);
}
public class MatrixHistory
{
public MatrixHistory(int size) {
vals = new float[size][];
}
public void add(float[] val) {
synchronized(vals) {
vals[ix] = val;
ix = (ix + 1) % vals.length;
if(ix==0)
full = true;
}
}
public void mult(float[] tmp, float[] output) {
synchronized(vals) {
if(full) {
for(int i=0; i<vals.length; ++i) {
if(i==0) {
System.arraycopy(vals[i],0,output,0,vals[i].length);
} else {
MathUtils.multiplyMatrix3x3(output,vals[i],tmp);
System.arraycopy(tmp,0,output,0,tmp.length);
}
}
} else {
if(ix==0)
return;
for(int i=0; i<ix; ++i) {
if(i==0) {
System.arraycopy(vals[i],0,output,0,vals[i].length);
} else {
MathUtils.multiplyMatrix3x3(output,vals[i],tmp);
System.arraycopy(tmp,0,output,0,tmp.length);
}
}
}
}
}
private int ix = 0;
private boolean full = false;
private float[][] vals;
}
第二块代码包含我对第一块代码的修改,第一块代码将陀螺仪添加到组合中。
具体来说,加速度的过滤因子变小(使值更稳定)。MatrixHistory类跟踪在gyroscopeSensorChanged方法中计算的最后100个陀螺仪deltaRotationMatrix值。
我在这个网站上看到了很多关于这个话题的问题。他们帮助我走到了这一步,但是我不知道下一步该怎么做。我真的希望传感器融合的人刚刚在某个地方发布了一些代码。他显然把所有的东西都放在一起了。
对于在哪里可以找到完整代码的问题,这里有一个Android jelly bean的默认实现:https://android.googlesource.com/platform/frameworks/base//JB-release/services/sensor service/Start通过检查fusion.cpp/h.它使用修改的Rodrigues参数(接近欧拉角)而不是四元数。除了定向之外,卡尔曼滤波器还可以估计陀螺漂移。对于测量更新,它使用磁力计和加速度(比力),有点不正确。
要使用这些代码,你要么是一个巫师,要么了解INS和KF的基础知识。许多参数必须微调,以使过滤器工作。正如爱德华恰当地指出的那样,这些人以此为生。
至少在谷歌的galaxy nexus中,这个默认实现没有使用,被Invense的专有系统覆盖。
好吧,我要感谢你知道什么是卡尔曼滤波器。如果你愿意,我会编辑这篇文章,并给你我几年前写的代码,让你做你想做的事情。
但首先,我会告诉你为什么不需要它。
Android 传感器堆栈的现代实现使用传感器融合,如上所述 Stan。这只是意味着所有可用的数据 - 加速度,磁力,陀螺仪 - 都收集在一个算法中,然后所有输出都以Android传感器的形式读出。
编辑:我刚刚无意中发现了关于这个主题的精彩的谷歌技术演讲:Android设备上的传感器融合:运动处理的革命。如果你对这个话题感兴趣,45分钟的观看时间是值得的。
从本质上讲,传感器融合是一个黑匣子。我已经研究了Android实现的源代码,它是用C编写的一个大型卡尔曼滤波器。里面有一些非常好的代码,比我写过的任何过滤器都要复杂得多,而且可能比你正在写的要复杂得多。记住,这些家伙就是为了谋生。
我还知道,至少有一家芯片组制造商有自己的传感器融合实现。然后,设备制造商根据自己的标准在Android和供应商实现之间进行选择。
最后,正如Stan上面提到的,Invensense在芯片级有自己的传感器融合实现。
无论如何,归根结底,您设备中的内置传感器融合可能优于您或我可以拼凑的任何东西。所以你真正想做的是访问它。
在Android中,既有物理传感器,也有虚拟传感器。虚拟传感器是从可用的物理传感器合成的传感器。最着名的例子是TYPE_ORIENTATION它采用加速度计和磁力计并创建滚动/俯仰/航向输出。(顺便说一句,你不应该使用这个传感器,它有太多的限制。
但重要的是,较新版本的Android包含这两个新的虚拟传感器:
TYPE_GRAVITY是过滤出运动影响的加速度计输入TYPE_LINEAR_ACCELERATION是过滤出重力分量的加速度计。
这两个虚拟传感器通过加速度计输入和陀螺仪输入的组合进行合成。
另一个值得注意的传感器是TYPE_ROTATION_VECTOR,它是由加速度计、磁强计和陀螺仪合成的四元数。它表示设备的全三维方向,并过滤掉线性加速度的影响。
然而,对于大多数人来说,四元数有点抽象,因为您可能无论如何都要处理三维变换,所以您的最佳方法是通过sensor manager . getrotationmatrix()将TYPE_GRAVITY和TYPE_MAGNETIC_FIELD结合起来。
还有一点:如果你正在使用一个运行旧版本Android的设备,你需要检测到你没有接收到TYPE_GRAVITY事件,而是使用TYPE _ ACCELEROMETER。理论上,这将是一个使用自己的卡尔曼滤波器的地方,但如果你的设备没有内置传感器融合,它可能也没有陀螺仪。
无论如何,这里有一些示例代码来展示我是如何做到的。
// Requires 1.5 or above
class Foo extends Activity implements SensorEventListener {
SensorManager sensorManager;
float[] gData = new float[3]; // Gravity or accelerometer
float[] mData = new float[3]; // Magnetometer
float[] orientation = new float[3];
float[] Rmat = new float[9];
float[] R2 = new float[9];
float[] Imat = new float[9];
boolean haveGrav = false;
boolean haveAccel = false;
boolean haveMag = false;
onCreate() {
// Get the sensor manager from system services
sensorManager =
(SensorManager)getSystemService(Context.SENSOR_SERVICE);
}
onResume() {
super.onResume();
// Register our listeners
Sensor gsensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
Sensor asensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
Sensor msensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
sensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_GAME);
sensorManager.registerListener(this, asensor, SensorManager.SENSOR_DELAY_GAME);
sensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_GAME);
}
public void onSensorChanged(SensorEvent event) {
float[] data;
switch( event.sensor.getType() ) {
case Sensor.TYPE_GRAVITY:
gData[0] = event.values[0];
gData[1] = event.values[1];
gData[2] = event.values[2];
haveGrav = true;
break;
case Sensor.TYPE_ACCELEROMETER:
if (haveGrav) break; // don't need it, we have better
gData[0] = event.values[0];
gData[1] = event.values[1];
gData[2] = event.values[2];
haveAccel = true;
break;
case Sensor.TYPE_MAGNETIC_FIELD:
mData[0] = event.values[0];
mData[1] = event.values[1];
mData[2] = event.values[2];
haveMag = true;
break;
default:
return;
}
if ((haveGrav || haveAccel) && haveMag) {
SensorManager.getRotationMatrix(Rmat, Imat, gData, mData);
SensorManager.remapCoordinateSystem(Rmat,
SensorManager.AXIS_Y, SensorManager.AXIS_MINUS_X, R2);
// Orientation isn't as useful as a rotation matrix, but
// we'll show it here anyway.
SensorManager.getOrientation(R2, orientation);
float incl = SensorManager.getInclination(Imat);
Log.d(TAG, "mh: " + (int)(orientation[0]*DEG));
Log.d(TAG, "pitch: " + (int)(orientation[1]*DEG));
Log.d(TAG, "roll: " + (int)(orientation[2]*DEG));
Log.d(TAG, "yaw: " + (int)(orientation[0]*DEG));
Log.d(TAG, "inclination: " + (int)(incl*DEG));
}
}
}
六羟甲基三聚氰胺六甲醚;如果您手边正好有一个四元数库,那么只需接收TYPE_ROTATION_VECTOR并将其转换为数组可能会更简单。
更新时间:2018-09-18 10:16:51 功能说明 加速度和陀螺仪传感器驱动。lsm6dsl 是一款具有数字加速度计和数字陀螺仪功能的低功耗传感器,通过 I2C 协议进行数据交互。 硬件资源 DevelopKit 开发板上自带有 lsm6dsl 传感器(下图红色圆圈处),并连接到 STM32 的 I2C2 端口; 软件设计 驱动配置 开发板板级管脚配置(board.json) 示范:</s
我使用IMU实现了一种数学方法来计算位置,IMU给了我加速度计、陀螺仪、俯仰偏航和滚动数据。问题是IMU对我的项目不够坚固,所以我买了一个更精确的IMU。 问题是它没有给我俯仰偏航和侧倾数据,它只给加速度和陀螺仪数据。我知道计算俯仰和侧倾很容易,但我猜我需要一个磁力计来计算偏航值。 我需要使用IMU计算位置,所以我不知道这是最好的方法。 1)使用数学方法获取偏航数据(我不知道是否可以)。2)集成磁
jd.startGyroscope(Object object) 开始监听陀螺仪数据。 参数 Object object 属性 类型 默认值 必填 说明 interval string normal 否 监听陀螺仪数据回调函数的执行频率 success function 否 接口调用成功的回调函数 fail function 否 接口调用失败的回调函数 complete function 否 接口
判断手机或平板的倾斜状态 用法 Your browser does not support the video tag. 通过向不同方向倾斜手机,发出不同的动物叫声 工作原理 当所有的输入都是Yes的时候,与节点才输出Yes。
检测物体震动和倾斜方向。 用法 Your browser does not support the video tag. 案例:水平仪 说明:利用陀螺仪传感器检测物体的倾斜,通过神经元APP编程指示出该如何调整使得物体水平。 所需模块:电源、蓝牙模块、LED面板、陀螺仪传感器、磁吸板。 工作原理 当所有的输入都是Yes的时候,与节点才输出Yes。
陀螺仪模块可以检测物品的运动状态和倾斜角度,提供当前的角度和加速度信息;也可用于检测震动。 净重量:7.4g 体积:24×24×14mm 参数 俯仰角范围:±180° 翻滚角范围:±90° 加速度范围:±8g 工作电压:DC 5V 抗跌落能力:1.5m 工作温度:-10℃~55℃ 工作湿度:<95% 特点 支持姿态识别 可检测多种不同强度的震动及晃动