Oto fragment kodu, który otrzymałem (Java, Android). Algorytm pobiera bardzo duże wartości początkowe dla zakresu filtrów i stopniowo je zmniejsza, i filtruje ruch przez porównywanie danych wejściowych z poprzednim zakresem filtrowania i odrzuca 10 ostatnich zmierzonych wartości, jeśli wykryje ruch.
Działa najlepiej, gdy telefon leży nadal na stole, ale nadal działa poprawnie, gdy telefon jest przenoszony i obracany.
class GyroscopeListener implements SensorEventListener
{
// Noise filter with sane initial values, so user will be able
// to move gyroscope during the first 10 seconds, while the noise is measured.
// After that the values are replaced by noiseMin/noiseMax.
final float filterMin[] = new float[] { -0.05f, -0.05f, -0.05f };
final float filterMax[] = new float[] { 0.05f, 0.05f, 0.05f };
// The noise levels we're measuring.
// Large initial values, they will decrease, but never increase.
float noiseMin[] = new float[] { -1.0f, -1.0f, -1.0f };
float noiseMax[] = new float[] { 1.0f, 1.0f, 1.0f };
// The gyro data buffer, from which we care calculating min/max noise values.
// The bigger it is, the more precise the calclations, and the longer it takes to converge.
float noiseData[][] = new float[200][noiseMin.length];
int noiseDataIdx = 0;
// When we detect movement, we remove last few values of the measured data.
// The movement is detected by comparing values to noiseMin/noiseMax of the previous iteration.
int movementBackoff = 0;
// Difference between min/max in the previous measurement iteration,
// used to determine when we should stop measuring, when the change becomes negligilbe.
float measuredNoiseRange[] = null;
// How long the algorithm is running, to stop it if it does not converge.
int measurementIteration = 0;
public GyroscopeListener(Context context)
{
SensorManager manager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
if (manager == null && manager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) == null)
return;
manager.registerListener(gyro, manager.getDefaultSensor(Sensor.TYPE_GYROSCOPE),
SensorManager.SENSOR_DELAY_GAME);
}
public void onSensorChanged(final SensorEvent event)
{
boolean filtered = true;
final float[] data = event.values;
if(noiseData != null)
collectNoiseData(data);
for(int i = 0; i < 3; i++)
{
if(data[i] < filterMin[i])
{
filtered = false;
data[i] -= filterMin[i];
}
else if(data[i] > filterMax[i])
{
filtered = false;
data[i] -= filterMax[i];
}
}
if(filtered)
return;
// Use the filtered gyroscope data here
}
void collectNoiseData(final float[] data)
{
for(int i = 0; i < noiseMin.length; i++)
{
if(data[i] < noiseMin[i] || data[i] > noiseMax[i])
{
// Movement detected, this can converge our min/max too early, so we're discarding last few values
if(movementBackoff < 0)
{
int discard = 10;
if(-movementBackoff < discard)
discard = -movementBackoff;
noiseDataIdx -= discard;
if(noiseDataIdx < 0)
noiseDataIdx = 0;
}
movementBackoff = 10;
return;
}
noiseData[noiseDataIdx][i] = data[i];
}
movementBackoff--;
if(movementBackoff >= 0)
return; // Also discard several values after the movement stopped
noiseDataIdx++;
if(noiseDataIdx < noiseData.length)
return;
measurementIteration++;
if(measurementIteration > 5)
{
// We've collected enough data to use our noise min/max values as a new filter
System.arraycopy(noiseMin, 0, filterMin, 0, filterMin.length);
System.arraycopy(noiseMax, 0, filterMax, 0, filterMax.length);
}
if(measurementIteration > 15)
{
// Finish measuring if the algorithm cannot converge in a long time
noiseData = null;
measuredNoiseRange = null;
return;
}
noiseDataIdx = 0;
boolean changed = false;
for(int i = 0; i < noiseMin.length; i++)
{
float min = 1.0f;
float max = -1.0f;
for(int ii = 0; ii < noiseData.length; ii++)
{
if(min > noiseData[ii][i])
min = noiseData[ii][i];
if(max < noiseData[ii][i])
max = noiseData[ii][i];
}
// Increase the range a bit, for safe conservative filtering
float middle = (min + max)/2.0f;
min += (min - middle) * 0.2f;
max += (max - middle) * 0.2f;
// Check if range between min/max is less then the current range, as a safety measure,
// and min/max range is not jumping outside of previously measured range
if(max - min < noiseMax[i] - noiseMin[i] && min >= noiseMin[i] && max <= noiseMax[i])
{
// Move old min/max closer to the measured min/max, but do not replace the values altogether
noiseMin[i] = (noiseMin[i] + min * 4.0f)/5.0f;
noiseMax[i] = (noiseMax[i] + max * 4.0f)/5.0f;
changed = true;
}
}
if(!changed)
return;
// Determine when to stop measuring - check that the previous min/max range is close enough to the current one
float range[] = new float[noiseMin.length];
for(int i = 0; i < noiseMin.length; i++)
range[i] = noiseMax[i] - noiseMin[i];
if(measuredNoiseRange == null)
{
measuredNoiseRange = range;
return; // First iteration, skip further checks
}
for(int i = 0; i < range.length; i++)
{
if(measuredNoiseRange[i]/range[i] > 1.2f)
{
measuredNoiseRange = range;
return;
}
}
// We converged to the final min/max filter values, stop measuring
System.arraycopy(noiseMin, 0, filterMin, 0, filterMin.length);
System.arraycopy(noiseMax, 0, filterMax, 0, filterMax.length);
noiseData = null;
measuredNoiseRange = null;
}
public void onAccuracyChanged(Sensor s, int a)
{
}
}
Należy pamiętać, że samo ignorowanie sygnałów o niskiej amplitudzie nie jest filtrowaniem górnoprzepustowym. To ignorowanie sygnałów niskiej częstotliwości. I nie ma nic na temat filtrowania Kalmana, które uniemożliwia jego użycie w tej aplikacji. Przyjrzyj się temu dalej. To ma zrobić dokładnie to, co zamierzacie. – Gene
Zgadzam się z filtrem górnoprzepustowym, moje sformułowanie jest nieprawidłowe.Proszę wskazać mi przykładowy kod filtra Kalmana, który dotyczy tylko żyroskopu, a nie przyspieszeniomierza, ponieważ z tego, co dotychczas odkryłem, wymaga on zarówno wydajnej pracy, jak i nie dbam o kąt telefonu do horyzontu, ani o obracanie to dokładny kąt w przód iw tył nie uzyska takiej samej wartości kąta obliczeniowego, potrzebuję tylko kątów, aby nie dryfować i nie potrząsać, gdy telefon jest nieruchomy. – pelya
@pelya i tak bez kątów magnetometru będzie dryfować po obróceniu urządzenia. Czy to jest w porządku? – pawelzieba