【问题标题】:How to detect movement of an android device?如何检测安卓设备的移动?
【发布时间】:2013-01-12 13:11:31
【问题描述】:

我需要有关如何检测 Android 设备移动量的建议。假设我将手机放在桌子或床上,然后如果有人敲击桌子或坐在床上或躺在床上,那么我想检测 android 设备的移动。

实际上我知道 android 有运动传感器 API,但我不知道使用哪种传感器以及哪种传感器类型最适合这种类型的运动检测。

如果有人可以分享一些基本的演示代码,我会很高兴。

【问题讨论】:

  • 使用加速度计(加速度、运动)和磁力计(指南针)。如果你真的很好奇,也许是勒克斯传感器和接近传感器。

标签: android sensors motion


【解决方案1】:

我一直在用类似的想法来测量手机的位移。我发现 LINEAR ACCELERATION(和 ACCELERATION)不够准确,无法正确测量位移。

这段代码应该会更好一些:

(初始化)

private SensorManager sensorManager;
private Sensor accelerometer;
double[] maxAccelerations = new double[3];
double[] position = new double[3];
long[] times = new long[3];
// time combined with maxAcceleration can approximate the change in position,
// with the formula Δpos = (maxAcceleration * time ^ 2) / 6
long currentTime;

(onCreate)

sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
if (sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION) != null) {
    accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
        sensorManager.registerListener(this, accelerometer, sensorManager.SENSOR_DELAY_FASTEST);
}
currentTime = System.currentTimeMillis();
for(int i=0;i<3;i++){
    times[i]=currentTime;
}
else{
    throw "Error";
    //Which will throw an error, if not the error that is expected. ?
}

(onSensorChanged 和 onAccuracyChanged)

@Override
public void onAccuracyChanged(Sensor ignore, int thisFunction) {
}

@Override
public void onSensorChanged(SensorEvent event) {
    if (event.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) {
        for(int i=0;i<3;i++){
            if(Math.abs(event.values[i])<0.01){
                // Note: this is to try to prevent accelerating time from being counted when the phone is stationary. 0.01 should be
                // changed to an appropriate sensitivity level that can be calculated by finding an average noise level when the phone is stationary.
                times[i]=System.currentTimeMillis();
            }
            if(event.values[i]>maxAccelerations[i] && maxAccelerations[i]>=0){
                maxAccelerations[i]=event.values[i];
            }
            else if(event.values[i]<maxAccelerations[i] && maxAccelerations[i]<=0){
                maxAccelerations[i]=event.values[i];
            }
            else if(event.values[i]>0 && maxAccelerations[i]<0){
                currentTime = System.currentTimeMillis();
                position[i]+=maxAccelerations[i] * (times[i]-currentTime)*(times[i]-currentTime) / 6;
                times[i]=currentTime;
                maxAccelerations[i]=event.values[i];
            }
            else if(event.values[i]<0 && maxAccelerations[i]>0){
                currentTime = System.currentTimeMillis();
                position[i]+=maxAccelerations[i] * (times[i]-currentTime)*(times[i]-currentTime) / 6;
                times[i]=currentTime;
                maxAccelerations[i]=event.values[i];
            }
        }
    }
}

【讨论】:

    【解决方案2】:

    如果您要查找手机的位移,则需要找到 作用在手机上的线性加速度,而不是重力加速度

    android 有一个内置转换器可以找到作用在您手机上的 LINEAR ACCELERATION

    https://github.com/yuvaramsingh94/AndroidSensorTestCode/tree/master

    这是一段代码,您可以在其中看到如何获取 LINEAR ACCELERATION 的原始值

    【讨论】:

    • 你的源没有检测到替换,它只是显示移动与否。
    【解决方案3】:

    此代码用于步行检测(由@anthropomo代码修改)

    获得更平滑的值。

    //初始化

    private SensorManager sensorMan;
    private Sensor accelerometer;
    
    private float[] mGravity;
    private double mAccel;
    private double mAccelCurrent;
    private double mAccelLast;
    
    private boolean sensorRegistered = false;
    

    // onCreate

        sensorMan = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
        accelerometer = sensorMan.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mAccel = 0.00f;
        mAccelCurrent = SensorManager.GRAVITY_EARTH;
        mAccelLast = SensorManager.GRAVITY_EARTH;
    
        sensorMan.registerListener(this, accelerometer,
                SensorManager.SENSOR_DELAY_NORMAL);
        sensorRegistered = true;
    

    // onSensorChanged

    private int hitCount = 0;
    private double hitSum = 0;
    private double hitResult = 0;
    
    private final int SAMPLE_SIZE = 50; // change this sample size as you want, higher is more precise but slow measure.
    private final double THRESHOLD = 0.2; // change this threshold as you want, higher is more spike movement
    
    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            mGravity = event.values.clone();
            // Shake detection
            double x = mGravity[0];
            double y = mGravity[1];
            double z = mGravity[2];
            mAccelLast = mAccelCurrent;
            mAccelCurrent = Math.sqrt(x * x + y * y + z * z);
            double delta = mAccelCurrent - mAccelLast;
            mAccel = mAccel * 0.9f + delta;
    
            if (hitCount <= SAMPLE_SIZE) {
                hitCount++;
                hitSum += Math.abs(mAccel);
            } else {
                hitResult = hitSum / SAMPLE_SIZE;
    
                Log.d(TAG, String.valueOf(hitResult));
    
                if (hitResult > THRESHOLD) {
                    Log.d(TAG, "Walking");
                } else {
                    Log.d(TAG, "Stop Walking");
                }
    
                hitCount = 0;
                hitSum = 0;
                hitResult = 0;
            }
        }
    }
    

    【讨论】:

    • 我不明白为什么要乘以 0.9:mAccel = mAccel * 0.9f + delta。 mAccel有什么用?
    【解决方案4】:

    我使用了以下类:

    public class MovementDetector implements SensorEventListener {
    
    protected final String TAG = getClass().getSimpleName();
    
    private SensorManager sensorMan;
    private Sensor accelerometer;
    
    private MovementDetector() {
    }
    
    private static MovementDetector mInstance;
    
    public static MovementDetector getInstance() {
        if (mInstance == null) {
            mInstance = new MovementDetector();
            mInstance.init();
        }
        return mInstance;
    }
    
    //////////////////////
    private HashSet<Listener> mListeners = new HashSet<MovementDetector.Listener>();
    
    private void init() {
        sensorMan = (SensorManager) GlobalData.getInstance().getContext().getSystemService(Context.SENSOR_SERVICE);
        accelerometer = sensorMan.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
    }
    
    public void start() {
        sensorMan.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
    }
    
    public void stop() {
        sensorMan.unregisterListener(this);
    }
    
    public void addListener(Listener listener) {
        mListeners.add(listener);
    }
    
    /* (non-Javadoc)
     * @see android.hardware.SensorEventListener#onSensorChanged(android.hardware.SensorEvent)
     */
    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) {
    
            float x = event.values[0];
            float y = event.values[1];
            float z = event.values[2];
    
            float diff = (float) Math.sqrt(x * x + y * y + z * z);
            if (diff > 0.5) // 0.5 is a threshold, you can test it and change it
                Log.d(TAG,"Device motion detected!!!!");
            for (Listener listener : mListeners) {
                listener.onMotionDetected(event, diff);
            }
        }
    
    }
    
    /* (non-Javadoc)
     * @see android.hardware.SensorEventListener#onAccuracyChanged(android.hardware.Sensor, int)
     */
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // TODO Auto-generated method stub
    
    }
    
    public interface Listener {
        void onMotionDetected(SensorEvent event, float acceleration);
        }
    }
    

    用法:

    关于我的活动onCrate()

            MovementDetector.getInstance().addListener(new MovementDetector.Listener() {
    
            @Override
            public void onMotionDetected(SensorEvent event, float acceleration) {
    
                mMotionDetectionTextView.setText("Acceleration: ["+String.format("%.3f",event.values[0])+","+String.format("%.3f",event.values[1])+","+String.format("%.3f",event.values[2])+"] "+String.format("%.3f", acceleration));
                if (acceleration > SettingsHelper.getInstance().getMotionDetectionThreshold()){
                    mMotionDetectionTextView.setTextColor(Color.RED);
                } else {
                    mMotionDetectionTextView.setTextColor(Color.WHITE);
                }
    
            }
        });
    

    关于我的活动onResume()

    MovementDetector.getInstance().start();
    

    关于我的活动onPause()

    MovementDetector.getInstance().stop();
    

    【讨论】:

    • GlobalData 是什么?
    • 我创建的一个包含应用程序上下文实例的对象。只需使用上下文。
    • 但是上述加速度计的使用是否可以节省电量(并且在没有运动时会让设备进入睡眠状态)?根据谷歌文档,只有“显着运动”传感器是省电的,可以用来从睡眠中唤醒。
    • @ransh 这取决于你想做什么。这里你只是注册到加速度计输出,它与省电无关。您在 onPause/onResume 活动中从加速度计注册/取消注册。当设备处于睡眠状态时,应用程序无论如何都不在前台。 “重要运动传感器”理论上是用于其他用途的不同传感器(例如,我不会在增强现实应用中使用它)。
    • @Pinhassi,谢谢,但从我的阅读来看,加速度计似乎与节能有关。有人提到他们使用唤醒锁 - 这会阻止节能。
    【解决方案5】:

    一定要使用加速度计:

    // Start with some variables
    private SensorManager sensorMan;
    private Sensor accelerometer;
    
    private float[] mGravity;
    private float mAccel;
    private float mAccelCurrent;
    private float mAccelLast;
    
    // In onCreate method
    sensorMan = (SensorManager)getSystemService(SENSOR_SERVICE);
    accelerometer = sensorMan.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    mAccel = 0.00f;
    mAccelCurrent = SensorManager.GRAVITY_EARTH;
    mAccelLast = SensorManager.GRAVITY_EARTH;
    
    // And these:
    
    @Override
    public void onResume() {
        super.onResume();
        sensorMan.registerListener(this, accelerometer,
            SensorManager.SENSOR_DELAY_UI);
    }
    
    @Override
    protected void onPause() {
        super.onPause();
        sensorMan.unregisterListener(this);
    }
    
    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER){
            mGravity = event.values.clone();
            // Shake detection
            float x = mGravity[0];
            float y = mGravity[1];
            float z = mGravity[2];
            mAccelLast = mAccelCurrent;
            mAccelCurrent = FloatMath.sqrt(x*x + y*y + z*z);
            float delta = mAccelCurrent - mAccelLast;
            mAccel = mAccel * 0.9f + delta;
                // Make this higher or lower according to how much
                // motion you want to detect
            if(mAccel > 3){ 
            // do something
            }
        }
    
    }
    
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // required method
    }
    

    【讨论】:

    • 当我顺利移动时,这不起作用。它仅在我摇晃时才有效。如何才能平稳运动??而且当手机向左或向右倾斜时,它也不工作
    • @BornToWin 这个特殊的代码 sn-p 旨在检测“摇晃”,即相对较大/较快的运动。看看if(mAccel &gt; 3)这一行。如果您将该数字减少到 1 或 0.5 或 0.25 或更低,您可以在较低的阈值下“做某事”。如果你想检测倾斜,谷歌“Android 检测倾斜”。它是相关的,但又不同。
    • 它可以正常工作,但无法正确检测平板移动。为了让它工作,我需要在 registerListener 处实现一个 HandlerThread 作为最后一个参数。
    • 请记住,对于 API > 23,您必须使用 (float)Math.sqrt(x*x + y*y + z*z); 来计算加速度。
    • 你能解释一下公式 mAccel = mAccel *0.9f + delta 吗? 'if (mAccel > 3)' 中的 3 对应于什么单位?对不起,我是使用加速度计的新手
    【解决方案6】:

    虽然我没有演示代码(因为您不够具体),但这里有一个好的开始:http://developer.android.com/guide/topics/sensors/sensors_motion.html(以及左侧的其他项目)。

    【讨论】:

      猜你喜欢
      • 2017-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-06
      • 1970-01-01
      相关资源
      最近更新 更多