java - Android - 加速度计跌倒检测算法

标签 java android algorithm math accelerometer

作为一个学校项目,我正在尝试使用智能手机的加速度计构建一个用于跌倒检测的应用程序(因此我还有很多地方需要改进)。 我正在做一些研究,我发现了这个 article通过一些计算,所以我试图将它们转换成一些代码。

我请了一位 friend 帮忙,他向我解释了如何进行这些计算。但考虑到我高中毕业已经几年了,而且我的数学也不是很好,我肯定我做错了一些事情。 所以我期待有人能帮助我。

这是我需要的和我已经拥有的,以防有人发现任何错误。

Feature SV (sum of the components of the acceleration vector)

return Math.abs(x) + Math.abs(y) + Math.abs(z);

Feature AV (angle variation)

    double anX = this.accelerometerValues.get(size -2).get(AccelerometerAxis.X) * this.accelerometerValues.get(size -1).get(AccelerometerAxis.X);
    double anY = this.accelerometerValues.get(size -2).get(AccelerometerAxis.Y) * this.accelerometerValues.get(size -1).get(AccelerometerAxis.Y);
    double anZ = this.accelerometerValues.get(size -2).get(AccelerometerAxis.Z) * this.accelerometerValues.get(size -1).get(AccelerometerAxis.Z);
    double an = anX + anY + anZ;

    double anX0 = Math.pow(this.accelerometerValues.get(size -2).get(AccelerometerAxis.X), 2);
    double anY0 = Math.pow(this.accelerometerValues.get(size -2).get(AccelerometerAxis.Y), 2);
    double anZ0 = Math.pow(this.accelerometerValues.get(size -2).get(AccelerometerAxis.Z), 2);
    double an0 = Math.sqrt(anX0 + anY0 + anZ0);

    double anX1 = Math.pow(this.accelerometerValues.get(size -1).get(AccelerometerAxis.X), 2);
    double anY1 = Math.pow(this.accelerometerValues.get(size -1).get(AccelerometerAxis.Y), 2);
    double anZ1 = Math.pow(this.accelerometerValues.get(size -1).get(AccelerometerAxis.Z), 2);
    double an1 = Math.sqrt(anX1 + anY1 + anZ1);

    double a = an / (an0 * an1);

    return (Math.pow(Math.cos(a), -1)) * (180 / Math.PI);

Feature C A (change in angle)

    double aX = this.accelerometerValues.get(0).get(AccelerometerAxis.X) * this.accelerometerValues.get(3).get(AccelerometerAxis.X);
    double aY = this.accelerometerValues.get(0).get(AccelerometerAxis.Y) * this.accelerometerValues.get(3).get(AccelerometerAxis.Y);
    double aZ = this.accelerometerValues.get(0).get(AccelerometerAxis.Z) * this.accelerometerValues.get(3).get(AccelerometerAxis.Z);

    double a0 = aX + aY + aZ;

    double a1 = (Math.sqrt(Math.pow(aX, 2)) + Math.sqrt(Math.pow(aY, 2)) + Math.sqrt(Math.pow(aZ, 2)));

    return (Math.pow(Math.cos(a0 / a1), -1)) * (180 / Math.PI);

我从第二次和第三次计算中得到了相同的返回,但似乎都不是预期的结果,但我找不到我做错了什么。

这是该类的代码,了解更多详细信息

import android.app.IntentService;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
import android.widget.Toast;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import br.com.aimcol.fallalertapp.activity.FallNotificationActivity;
import br.com.aimcol.fallalertapp.model.Elderly;
import br.com.aimcol.fallalertapp.model.Person;
import br.com.aimcol.fallalertapp.model.User;
import br.com.aimcol.fallalertapp.util.AccelerometerAxis;
import br.com.aimcol.fallalertapp.util.RuntimeTypeAdapterFactory;

public class FallDetectionService extends IntentService implements SensorEventListener {


    private static final int ACCELEROMETER_SAMPLING_PERIOD = 1000000;
    private static final double CSV_THRESHOLD = 23;
    private static final double CAV_THRESHOLD = 18;
    private static final double CCA_THRESHOLD = 65.5;

    private SensorManager mSensorManager;
    private Sensor mAccelerometer;
    private User user;
    private Gson gson;

    private Long lastSentInMillis;
    private Long minTimeToNotifyAgain = 3000000L;

    private List<Map<AccelerometerAxis, Double>> accelerometerValues = new ArrayList<>();


    public FallDetectionService() {
        super(".FallDetectionService");
    }

    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public FallDetectionService(String name) {
        super(name);
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public final void onAccuracyChanged(Sensor sensor,
                                        int accuracy) {
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        // Axis of the rotation sample, not normalized yet.
        double x = event.values[0];
        double y = event.values[1];
        double z = event.values[2];

        if (this.isFallDetected(x, y, z)) {
            if (this.isOkayToNotifyAgain()) {
                this.lastSentInMillis = System.currentTimeMillis();
                Toast.makeText(this, "Fall", Toast.LENGTH_LONG).show();
                FallNotificationActivity.startFallNotificationActivity(this, this.gson.toJson(this.user));
            }
        }
    }

    private boolean isFallDetected(double x,
                                double y,
                                double z) {

        double acceleration = this.calculateAcceleration(x, y, z);// - SensorManager.GRAVITY_EARTH;
        this.addAccelerometerValuesToList(x, y, z, acceleration);

        String msg = new StringBuilder("x: ").append(x).append(" y: ").append(y).append(" z: ").append(z).append(" acc: ").append(acceleration).toString();
        Log.d("FDS-Acc-Values", msg);

        if (acceleration > CSV_THRESHOLD) {
//            double angleVariation = this.calculateAngleVariation();
//            if (angleVariation > CAV_THRESHOLD) {
//                double changeInAngle = this.calculateChangeInAngle();
//                if (changeInAngle > CCA_THRESHOLD) {
                    Log.d("FDS-Fall-Happened", msg);
                   return true;
//                }
//            }
        }
        return false;
    }

    private void addAccelerometerValuesToList(double x,
                                              double y,
                                              double z,
                                              double acceleration) {
        if(this.accelerometerValues.size() >= 4) {
            this.accelerometerValues.remove(0);
        }
        Map<AccelerometerAxis, Double> map = new HashMap<>();
        map.put(AccelerometerAxis.X, x);
        map.put(AccelerometerAxis.Y, y);
        map.put(AccelerometerAxis.Z, z);
        map.put(AccelerometerAxis.ACCELERATION, acceleration);
        this.accelerometerValues.add(map);
    }

    private double calculateAcceleration(double x,
                                         double y,
                                         double z) {
        return Math.abs(x) + Math.abs(y) + Math.abs(z);
    }

    private double calculateAngleVariation() {
        int size = this.accelerometerValues.size();
        if (size < 2){
            return -1;
        }

        double anX = this.accelerometerValues.get(size -2).get(AccelerometerAxis.X) * this.accelerometerValues.get(size -1).get(AccelerometerAxis.X);
        double anY = this.accelerometerValues.get(size -2).get(AccelerometerAxis.Y) * this.accelerometerValues.get(size -1).get(AccelerometerAxis.Y);
        double anZ = this.accelerometerValues.get(size -2).get(AccelerometerAxis.Z) * this.accelerometerValues.get(size -1).get(AccelerometerAxis.Z);
        double an = anX + anY + anZ;

//        double an = this.accelerometerValues.get(size -2).get(AccelerometerAxis.ACCELERATION) * this.accelerometerValues.get(size -1).get(AccelerometerAxis.ACCELERATION);

        double anX0 = Math.pow(this.accelerometerValues.get(size -2).get(AccelerometerAxis.X), 2);
        double anY0 = Math.pow(this.accelerometerValues.get(size -2).get(AccelerometerAxis.Y), 2);
        double anZ0 = Math.pow(this.accelerometerValues.get(size -2).get(AccelerometerAxis.Z), 2);
        double an0 = Math.sqrt(anX0 + anY0 + anZ0);

        double anX1 = Math.pow(this.accelerometerValues.get(size -1).get(AccelerometerAxis.X), 2);
        double anY1 = Math.pow(this.accelerometerValues.get(size -1).get(AccelerometerAxis.Y), 2);
        double anZ1 = Math.pow(this.accelerometerValues.get(size -1).get(AccelerometerAxis.Z), 2);
        double an1 = Math.sqrt(anX1 + anY1 + anZ1);

        double a = an / (an0 * an1);

        return (Math.pow(Math.cos(a), -1)) * (180 / Math.PI); //cosseno inverso? Ou cosseno ^-1?
    }

    private double calculateChangeInAngle() {
        int size = this.accelerometerValues.size();
        if (size < 4){
            return -1;
        }
        double aX = this.accelerometerValues.get(0).get(AccelerometerAxis.X) * this.accelerometerValues.get(3).get(AccelerometerAxis.X);
        double aY = this.accelerometerValues.get(0).get(AccelerometerAxis.Y) * this.accelerometerValues.get(3).get(AccelerometerAxis.Y);
        double aZ = this.accelerometerValues.get(0).get(AccelerometerAxis.Z) * this.accelerometerValues.get(3).get(AccelerometerAxis.Z);

        double a0 = aX + aY + aZ;

        double a1 = (Math.sqrt(Math.pow(aX, 2)) + Math.sqrt(Math.pow(aY, 2)) + Math.sqrt(Math.pow(aZ, 2)));

        return (Math.pow(Math.cos(a0 / a1), -1)) * (180 / Math.PI);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        if (this.user == null) {
            String userJson = intent.getStringExtra(User.USER_JSON);
            this.user = this.gson.fromJson(userJson, User.class);
        }
    }

    @Override
    public int onStartCommand(Intent intent,
                              int flags,
                              int startId) {

        if (this.gson == null) {
            RuntimeTypeAdapterFactory<Person> runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory
                    .of(Person.class, "type")
                    .registerSubtype(Elderly.class, Elderly.class.getSimpleName());
            this.gson = new GsonBuilder().registerTypeAdapterFactory(runtimeTypeAdapterFactory).create();
        }

        if (this.user == null) {
            String userJson = intent.getStringExtra(User.USER_JSON);
            this.user = this.gson.fromJson(userJson, User.class);
        }

        this.mSensorManager = (SensorManager) super.getSystemService(Context.SENSOR_SERVICE);
        this.mAccelerometer = this.mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        if (this.mAccelerometer == null) {
            throw new RuntimeException("Acelerometro não encontrado");
        }

        this.mSensorManager.registerListener(this, this.mAccelerometer, ACCELEROMETER_SAMPLING_PERIOD);

        return Service.START_STICKY;
    }

    private boolean isOkayToNotifyAgain() {
        return this.lastSentInMillis == null || (this.lastSentInMillis + this.minTimeToNotifyAgain) < System.currentTimeMillis();
    }

    public static void startFallDetectionService(String userJson,
                                                 Context context) {

        Intent fallDetectionServiceIntent = new Intent(context, FallDetectionService.class);
        fallDetectionServiceIntent.putExtra(User.USER_JSON, userJson);
        context.startService(fallDetectionServiceIntent);
    }

    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    protected boolean testFallDetection(List<Map<AccelerometerAxis, Double>> values) {
        for (Map<AccelerometerAxis, Double> value : values) {
            if (this.isFallDetected(
                    value.get(AccelerometerAxis.X),
                    value.get(AccelerometerAxis.Y),
                    value.get(AccelerometerAxis.Z))) {

                return true;
            }
        }
        return false;
    }
}

Ps:如有错误请见谅。英语不是我的第一语言,我有点生疏。

Ps2:对不起我的变量名。

Ps3:代码在github上,如果您想查看其余代码,或者如果您不想在此处发布回复,而是想提出拉取请求或其他内容,请随意。

最佳答案

这个问题在这里可能不合适。 SO 不是一个代码审查网站。

这里有一些要考虑的评论:

  1. 您列出的第一个公式不是您在代码中表达的内容。公式中没有平方根或平方和,但出于某种原因将它们放入代码中。文章说SV应该是直线加速度 vector 各分量的绝对值之和。那不是你的代码所说的。
  2. 我不清楚第二个公式中 n 和 n+1 的含义。那些是连续的时间点吗?两个 vector 的点积很容易计算 - 但对于哪两个 vector 呢?
  3. 第三个公式要求在四秒窗口内计算加速度 vector 的平均值。我没有看到任何地方这样做。平均值中涉及的 vector 数量取决于您的采样率。

我建议您多读几遍这篇文章。

我还建议您将它们封装到函数中,并为它们编写一些好的单元测试。看看您是否可以重现论文中的结果。

关于java - Android - 加速度计跌倒检测算法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51370722/

相关文章:

java - 表的外键值在 Hibernate 5 中不会自动更新

android - 从android中的txt文件读取unicode文本时附加'a'字符

javascript - 如何编写Javascript代码为任意字符串的输入创建小于或等于20字节的哈希字符串?

java - 为什么ServiceLoader总是重新实例化服务?

java - 如何解决解析问题?

java - JTable 选择的单元格多于单击的单元格

android-layout - Android 平板电脑分辨率和密度支持

Android:如何控制音量:如果音量按钮向上,SeekBar向上

c++ - 在标准算法中,通过引用捕获哪里更正确?

algorithm - 数字数组的最优冒泡排序算法