android - sensores - sensor acelerómetro
¿Cómo calcular el conteo exacto de pasos de pie usando el acelerómetro en Android? (5)
Estoy desarrollando alguna aplicación como Runtastic Podometer usando el algoritmo https://code.google.com/p/pedometer/ pero no obtengo ninguna similitud entre los resultados.
mi código es el siguiente:
public void onSensorChanged(SensorEvent event)
{
Sensor sensor = event.sensor;
synchronized (this)
{
if (sensor.getType() == Sensor.TYPE_ORIENTATION) {}
else {
int j = (sensor.getType() == Sensor.TYPE_ACCELEROMETER) ? 1 : 0;
if (j == 1) {
float vSum = 0;
for (int i=0 ; i<3 ; i++) {
final float v = mYOffset + event.values[i] * mScale[j];
vSum += v;
}
int k = 0;
float v = vSum / 3;
//Log.e("data", "data"+v);
float direction = (v > mLastValues[k] ? 1 : (v < mLastValues[k] ? -1 : 0));
if (direction == - mLastDirections[k]) {
// Direction changed
int extType = (direction > 0 ? 0 : 1); // minumum or maximum?
mLastExtremes[extType][k] = mLastValues[k];
float diff = Math.abs(mLastExtremes[extType][k] - mLastExtremes[1 - extType][k]);
if (diff > mLimit) {
boolean isAlmostAsLargeAsPrevious = diff > (mLastDiff[k]*2/3);
boolean isPreviousLargeEnough = mLastDiff[k] > (diff/3);
boolean isNotContra = (mLastMatch != 1 - extType);
if (isAlmostAsLargeAsPrevious && isPreviousLargeEnough && isNotContra) {
for (StepListener stepListener : mStepListeners) {
stepListener.onStep();
}
mLastMatch = extType;
}
else {
Log.i(TAG, "no step");
mLastMatch = -1;
}
}
mLastDiff[k] = diff;
}
mLastDirections[k] = direction;
mLastValues[k] = v;
}
}
}
}
para registrar sensores:
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(
Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(mStepDetector,mSensor,SensorManager.SENSOR_DELAY_NORMAL);
en el algoritmo tengo diferentes niveles de sensibilidad como vacío público
setSensitivity(float sensitivity) {
mLimit = sensitivity; // 1.97 2.96 4.44 6.66 10.00 15.00 22.50 33.75 50.62
}
en varios niveles de sensibilidad mi resultado es:
sensitivity rantastic pedometer my app
10.00 3870 5500
11.00 3000 4000
11.15 3765 4576
13.00 2000 890
11.30 754 986
No estoy obteniendo ningún patrón adecuado para que coincida con el requisito. Según mi análisis esta aplicación está utilizando Sensor.TYPE_MAGNETIC_FIELD para el cálculo de pasos, por favor, hágame saber algún algoritmo para que pueda cumplir con el requisito
Esta es mi realización. Fue escrito hace aproximadamente 1.5-2 años. Y realmente no recuerdo todo esto que escribí. Pero funcionó. Y funcionó bien para mis necesidades.
Sé que esta es realmente una gran clase (algunos métodos se eliminan), pero puede ser útil. Si no, simplemente eliminaré esta respuesta ...
public class StepDetector implements SensorEventListener
{
public static final int MAX_BUFFER_SIZE = 5;
private static final int Y_DATA_COUNT = 4;
private static final double MIN_GRAVITY = 2;
private static final double MAX_GRAVITY = 1200;
public void onSensorChanged(final SensorEvent sensorEvent)
{
final float[] values = sensorEvent.values;
final Sensor sensor = sensorEvent.sensor;
if (sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
{
magneticDetector(values, sensorEvent.timestamp / (500 * 10 ^ 6l));
}
if (sensor.getType() == Sensor.TYPE_ACCELEROMETER)
{
accelDetector(values, sensorEvent.timestamp / (500 * 10 ^ 6l));
}
}
private ArrayList<float[]> mAccelDataBuffer = new ArrayList<float[]>();
private ArrayList<Long> mMagneticFireData = new ArrayList<Long>();
private Long mLastStepTime = null;
private ArrayList<Pair> mAccelFireData = new ArrayList<Pair>();
private void accelDetector(float[] detectedValues, long timeStamp)
{
float[] currentValues = new float[3];
for (int i = 0; i < currentValues.length; ++i)
{
currentValues[i] = detectedValues[i];
}
mAccelDataBuffer.add(currentValues);
if (mAccelDataBuffer.size() > StepDetector.MAX_BUFFER_SIZE)
{
double avgGravity = 0;
for (float[] values : mAccelDataBuffer)
{
avgGravity += Math.abs(Math.sqrt(
values[0] * values[0] + values[1] * values[1] + values[2] * values[2]) - SensorManager.STANDARD_GRAVITY);
}
avgGravity /= mAccelDataBuffer.size();
if (avgGravity >= MIN_GRAVITY && avgGravity < MAX_GRAVITY)
{
mAccelFireData.add(new Pair(timeStamp, true));
}
else
{
mAccelFireData.add(new Pair(timeStamp, false));
}
if (mAccelFireData.size() >= Y_DATA_COUNT)
{
checkData(mAccelFireData, timeStamp);
mAccelFireData.remove(0);
}
mAccelDataBuffer.clear();
}
}
private void checkData(ArrayList<Pair> accelFireData, long timeStamp)
{
boolean stepAlreadyDetected = false;
Iterator<Pair> iterator = accelFireData.iterator();
while (iterator.hasNext() && !stepAlreadyDetected)
{
stepAlreadyDetected = iterator.next().first.equals(mLastStepTime);
}
if (!stepAlreadyDetected)
{
int firstPosition = Collections.binarySearch(mMagneticFireData, accelFireData.get(0).first);
int secondPosition = Collections
.binarySearch(mMagneticFireData, accelFireData.get(accelFireData.size() - 1).first - 1);
if (firstPosition > 0 || secondPosition > 0 || firstPosition != secondPosition)
{
if (firstPosition < 0)
{
firstPosition = -firstPosition - 1;
}
if (firstPosition < mMagneticFireData.size() && firstPosition > 0)
{
mMagneticFireData = new ArrayList<Long>(
mMagneticFireData.subList(firstPosition - 1, mMagneticFireData.size()));
}
iterator = accelFireData.iterator();
while (iterator.hasNext())
{
if (iterator.next().second)
{
mLastStepTime = timeStamp;
accelFireData.remove(accelFireData.size() - 1);
accelFireData.add(new Pair(timeStamp, false));
onStep();
break;
}
}
}
}
}
private float mLastDirections;
private float mLastValues;
private float mLastExtremes[] = new float[2];
private Integer mLastType;
private ArrayList<Float> mMagneticDataBuffer = new ArrayList<Float>();
private void magneticDetector(float[] values, long timeStamp)
{
mMagneticDataBuffer.add(values[2]);
if (mMagneticDataBuffer.size() > StepDetector.MAX_BUFFER_SIZE)
{
float avg = 0;
for (int i = 0; i < mMagneticDataBuffer.size(); ++i)
{
avg += mMagneticDataBuffer.get(i);
}
avg /= mMagneticDataBuffer.size();
float direction = (avg > mLastValues ? 1 : (avg < mLastValues ? -1 : 0));
if (direction == -mLastDirections)
{
// Direction changed
int extType = (direction > 0 ? 0 : 1); // minumum or maximum?
mLastExtremes[extType] = mLastValues;
float diff = Math.abs(mLastExtremes[extType] - mLastExtremes[1 - extType]);
if (diff > 8 && (null == mLastType || mLastType != extType))
{
mLastType = extType;
mMagneticFireData.add(timeStamp);
}
}
mLastDirections = direction;
mLastValues = avg;
mMagneticDataBuffer.clear();
}
}
public static class Pair implements Serializable
{
Long first;
boolean second;
public Pair(long first, boolean second)
{
this.first = first;
this.second = second;
}
@Override
public boolean equals(Object o)
{
if (o instanceof Pair)
{
return first.equals(((Pair) o).first);
}
return false;
}
}
}
Estoy usando detección de pasos en mi instrumento para caminar. Obtengo buenos resultados de detección de pasos. Yo uso achartengine para trazar los datos del acelerómetro. Eche un vistazo aquí: https://github.com/MichalDanielDobrzanski/WalkingSynth/blob/master/app/src/main/java/com/dobi/walkingsynth/accelerometer/AccelerometerProcessing.java Lo que hago:
- Análisis del vector de magnitud para el sensor acelerómetro.
- Estableciendo un nivel de umbral modificable . Cuando la señal del acelerómetro está por encima, lo cuento como un paso.
- Configuración del tiempo de estado inactivo (para detección de pasos) después del primer cruce del umbral.
El punto 3. se calcula:
- ajuste arbitrario del tempo máximo de nuestra caminata (por ejemplo, 120bpm)
- si 60bpm - 1000msec por paso, luego 120bpm - 500msec por paso
- el acelerómetro pasa datos con cierta frecuencia deseada (SENSOR_DELAY_NORMAL, SENSOR_DELAY_GAME, etc.). Cuando DELAY_GAME: T ~ = 20 ms (esto está incluido en la documentación de Android)
- n - muestras para omitir (después de pasar el umbral)
- n = 500 mseg / t
- n = 500/20 = 25 (muchos de ellos. Puede ajustar este valor).
- después de eso, el umbral se activa .
Eche un vistazo a esta imagen: mi aplicación
Lo primero que debe hacer es decidir sobre un algoritmo. Por lo que sé, hay aproximadamente tres formas de detectar pasos usando acelerómetros que se describen en la literatura:
Usa el teorema de Pitágoras para calcular la magnitud del vector de aceleración de cada muestra desde el acelerómetro. Low-pass filtra la señal de magnitud para eliminar el ruido de alta frecuencia y luego busca picos y valles en la señal filtrada. Es posible que deba agregar requisitos adicionales para eliminar los falsos positivos. Esta es, de lejos, la forma más sencilla de detectar pasos, también es la forma en que la mayoría de los podómetros ordinarios, si no todos, del tipo que puede comprar en una tienda de deportes funcionan de la misma manera.
Use Pythagoras como en (1), luego ejecute la señal a través de una FFT y compare la salida de la FFT con las salidas conocidas de caminar. Esto requiere que tengas acceso a una cantidad bastante grande de datos de entrenamiento.
Alimente los datos del acelerómetro en un algoritmo que utilice alguna técnica adecuada de aprendizaje automático, por ejemplo, una red neuronal o una transformación wavelet digital. Por supuesto, puede incluir otros sensores en este enfoque. Esto también requiere que tengas acceso a una cantidad bastante grande de datos de entrenamiento.
Una vez que se haya decidido por un algoritmo, probablemente desee utilizar algo como Matlab o SciPy para probar su algoritmo en su computadora utilizando grabaciones que haya realizado en teléfonos Android. Vuelque los datos del acelerómetro a un archivo cvs en su teléfono, haga un registro de la cantidad de pasos que representa el archivo, copie el archivo en su computadora y ejecute su algoritmo en los datos para ver si obtiene el conteo de pasos correcto. De esta forma puede detectar problemas con el algoritmo y corregirlos.
Si esto suena difícil, entonces la mejor forma de acceder a una buena detección de pasos es esperar hasta que lleguen más teléfonos con el contador de pasos incorporado que habilita KitKat.
Una diferencia principal que detecté entre su implementación y el código en el proyecto grepcode es la forma en que registra el oyente.
Tu codigo:
mSensorManager.registerListener(mStepDetector,
mSensor,
SensorManager.SENSOR_DELAY_NORMAL);
Su código:
mSensorManager.registerListener(mStepDetector,
mSensor,
SensorManager.SENSOR_DELAY_FASTEST);
Esta es una gran diferencia. SENSOR_DELAY_NORMAL
está destinado a cambios de orientación y, por lo tanto, no es tan rápido (¿se ha notado alguna vez que gira el dispositivo y el dispositivo realmente gira? Esto se debe a que esta es una funcionalidad que no necesita ser súper rápida (eso probablemente sea bastante molesto incluso). La velocidad a la que obtienes actualizaciones no es tan alta).
Por otro lado, SENSOR_DELAY_FASTEST
está diseñado para cosas como podómetros: desea los datos del sensor lo más rápido y con la mayor frecuencia posible, por lo que sus cálculos de pasos serán lo más precisos posible.
Intenta cambiar a la velocidad SENSOR_DELAY_FASTEST
y prueba de nuevo. Debería hacer una gran diferencia.
https://github.com/bagilevi/android-pedometer
Espero que esto sea útil