我有一个 3D 传感器,可以测量 v(x,y,z) 数据。我只使用 x 和 y 数据。仅平滑 x 和 y 就足够了。
如果我使用日志来显示数据,它会显示如下内容: (时间) 0.1 ... (数据记录) x = 1.1234566667 (时间) 0.2 ... (数据记录) x = 1.1245655666 (时间)0.3 ...(数据记录)x = 1.2344445555
数据实际上更准确,但我想在 1.1234 值和 1.2344 值之间进行平滑,因为对我来说这是相同的,我可以使用整数,仅显示“x= 1”,但我也需要小数,然后,我需要在这里显示一种“平滑”值。
有人有什么想法吗?我正在使用 C# 进行编程,但并非所有功能都可以正常工作,因此我需要构建自己的功能。
最简单的方法是对数据进行移动平均。也就是说,保存一系列传感器数据读数并对其进行平均。像这样的东西(伪代码):
data_X = [0,0,0,0,0];
function read_X () {
data_X.delete_first_element();
data_X.push(get_sensor_data_X());
return average(data_X);
}
这样做时需要权衡。使用的数组越大,结果越平滑,但结果与实际读数之间的滞后越大。例如:
/\_/\
/\/ \_/\
Sensor reading: __/\/ \/\
\/\ _/\___________
\/
_
__/ \_
___/ \__
Small array: ___/ \_/\_ _
\ __/ \________
\_/
____
__/ \__
__/ \__
Large array: _______/ \__ __
\_ / \__
\_/
(forgive my ASCII-ART but I'm hoping it's good enough for illustration).
如果您想要快速响应但想要良好的平滑效果,那么您将使用数组的加权平均值。这基本上是数字信号处理(大写DSP),与其名称相反,它与模拟设计更密切相关。这是一篇关于它的简短维基百科文章(带有良好的外部链接,如果您想走这条路,您应该阅读):http://en.wikipedia.org/wiki/Digital_filter
这里有一些关于低通滤波器的代码,可能适合您的需求:低通滤波器软件?。请注意,在该答案的代码中,他使用的是大小为 4 的数组(或信号处理术语中的阶数 4,因为此类滤波器称为四阶滤波器,它实际上可以通过四阶多项式方程进行建模: ax^4 + bx^3 + cx^2 + dx).
所以我来到这里寻求解决同样的问题(Android 中的传感器输入平滑),这就是我想到的:
/*
* time smoothing constant for low-pass filter
* 0 ≤ α ≤ 1 ; a smaller value basically means more smoothing
* See: http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization
*/
static final float ALPHA = 0.2f;
protected float[] accelVals;
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
accelVals = lowPass( event.values, accelVals );
// use smoothed accelVals here; see this link for a simple compass example:
// http://www.codingforandroid.com/2011/01/using-orientation-sensors-simple.html
}
/**
* @see http://en.wikipedia.org/wiki/Low-pass_filter#Algorithmic_implementation
* @see http://en.wikipedia.org/wiki/Low-pass_filter#Simple_infinite_impulse_response_filter
*/
protected float[] lowPass( float[] input, float[] output ) {
if ( output == null ) return input;
for ( int i=0; i<input.length; i++ ) {
output[i] = output[i] + ALPHA * (input[i] - output[i]);
}
return output;
}
感谢 @slebetman 将我指向维基百科链接,在阅读了一点之后,该链接吸引了我到维基百科低通滤波器文章中的算法。我不会发誓我拥有最好的算法(甚至是正确的!),但轶事证据似乎表明它正在发挥作用。
平滑传感器数据的方法有很多,具体取决于传感器的类型以及适合的类比。 我在我的项目中使用了这些算法:
代码:
HPF-高通滤波器
private float[] highPass(float x, float y, float z) {
float[] filteredValues = new float[3];
gravity[0] = ALPHA * gravity[0] + (1 – ALPHA) * x;
gravity[1] = ALPHA * gravity[1] + (1 – ALPHA) * y;
gravity[2] = ALPHA * gravity[2] + (1 – ALPHA) * z;
filteredValues[0] = x – gravity[0];
filteredValues[1] = y – gravity[1];
filteredValues[2] = z – gravity[2];
return filteredValues;
}
LPF-低通滤波器
private float[] lowPass(float x, float y, float z) {
float[] filteredValues = new float[3];
filteredValues[0] = x * a + filteredValues[0] * (1.0f – a);
filteredValues[1] = y * a + filteredValues[1] * (1.0f – a);
filteredValues[2] = z * a + filteredValues[2] * (1.0f – a);
return filteredValues;
}
MAA-移动平均线
private final int SMOOTH_FACTOR_MAA = 2;//increase for better results but hits cpu bad
public ArrayList<Float> processWithMovingAverageGravity(ArrayList<Float> list, ArrayList<Float> gList) {
int listSize = list.size();//input list
int iterations = listSize / SMOOTH_FACTOR_MAA;
if (!AppUtility.isNullOrEmpty(gList)) {
gList.clear();
}
for (int i = 0, node = 0; i < iterations; i++) {
float num = 0;
for (int k = node; k < node + SMOOTH_FACTOR_MAA; k++) {
num = num + list.get(k);
}
node = node + SMOOTH_FACTOR_MAA;
num = num / SMOOTH_FACTOR_MAA;
gList.add(num);//out put list
}
return gList;
}
这是一个基于 iOS 的事件处理指南的 MotionEvents 部分中的逻辑的示例。
float ALPHA = 0.1;
protected float[] lowPass( float[] input, float[] output ) {
if ( output == null ) return input;
for ( int i=0; i<input.length; i++ ) {
output[i] = (input[i] * ALPHA) + (ouptut[i] * (1.0 - ALPHA));
}
return output;
}
@thom_nic 的低通滤波器代码中有一个小但非常重要的拼写错误,这种实现的结果有很大不同。
protected float[] lowPass( float[] input, float[] output ) {
if ( output == null ) return input;
for ( int i=0; i<input.length; i++ ) {
output[i] = output[i] + ALPHA * (input[i] - output[i]); // ERROR HERE
}
return output;
}
您可以在这里找到正确的代码wikipedia Low-pass_filter,C代码如下:
protected float[] lowPass( float[] input, float[] output ) {
if ( output == null ) return input;
output[0] = input[0];
for (int i=1; i<input.length; i++) {
output[i] = output[i-1] + ALPHA * (input[i] - output[i-1]);
}
return output;
}
请注意替换的索引(i -> i-1)。
在这里挖掘一个老问题,但如果你在 .NET 领域,你可以使用 RX 来为你做到这一点。
例如,将 RX 与 WebClient.DownloadFileAsync 结合使用来计算“平滑”下载速度:
double interval = 2.0; // 2 seconds
long bytesReceivedSplit = 0;
WebClient wc = new WebClient();
var downloadProgress = Observable.FromEventPattern<
DownloadProgressChangedEventHandler, DownloadProgressChangedEventArgs>(
h => wc.DownloadProgressChanged += h,
h => wc.DownloadProgressChanged -= h)
.Select(x => x.EventArgs);
downloadProgress.Sample(TimeSpan.FromSeconds(interval)).Subscribe(x =>
{
Console.WriteLine((x.BytesReceived - bytesReceivedSplit) / interval);
bytesReceivedSplit = x.BytesReceived;
});
Uri source = new Uri("http://someaddress.com/somefile.zip");
wc.DownloadFileAsync(source, @"C:\temp\somefile.zip");
显然,间隔越长,平滑度就越大,但等待初始读数的时间也就越长。
这些技术是否优于简单的零历史算法 平均值 := (新 + (N-1) * 平均值) / N ? 当 N=1 时平均值 = 新 使 N 任意大...