Android用Accelerometer数据计算Velocity

时间:2017-11-03 13:38:40

标签: android performance accelerometer calculation

由于功耗GPS数据,我想用加速度计x,y和z数据来计算设备速度。我已经阅读了很多关于这个主题的问题,我尝试了很多设置来找到一个满意的解决方案来计算我的设备在车里时的速度。 它似乎很简单,但没有任何作用,这让我发疯。 尝试使用移除重力的Sensor.TYPE_LINEAR_ACCELERATION和Sensor.TYPE_ACCELEROMETER。在线性加速度数据上尝试低通滤波器。不幸的是,没有任何成功。 看起来计算的速度是正确的,但在我的车里测试计算出的速度不会高于约2米/秒。

在代码片段

下面
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);


public void onSensorChanged(SensorEvent event) {
    if (event.sensor == mAccelerometer) {

        if (timestamp != 0) {
            final float dT = (event.timestamp - timestamp) * NS2S;

            lax = event.values[0];
            lay = event.values[1];
            laz = event.values[2];

            vx = vxo + lax * dT ;
            vy = vyo + lay * dT ;
            vz = vzo + laz * dT ;

            speed = (float) (Math.sqrt(vx*vx + vy*vy + vz*vz)) ;
            if (speed < 0.01) {speed = 0 ; } 
            tv_speed.setText(String.valueOf(speed));

        }          
        timestamp = event.timestamp;
    }
}

希望有人可以提供帮助,非常感谢。

3 个答案:

答案 0 :(得分:0)

仅使用加速度计可以计算距离和速度,但有三个条件: 1.线性运动 - 轨迹必须是直的。 2.道路坡度必须保持不变。 3.您必须在开始前执行校准程序。

你可以在哪里使用这种方法 - 这取决于你......现在,怎么做:

我们需要一些东西,实现SensorEventListener接口。为了将来的使用,我们添加以下抽象类:

public abstract class Accelerometer implements SensorEventListener {

        protected float lastX;
        protected float lastY;
        protected float lastZ;
        public abstract Point getPoint();
        public void onAccuracyChanged(Sensor arg0, int arg1) {

     }
}

这将是我们的SensorEventListener:

public class XYZAccelerometer extends Accelerometer {


    private static final int BUFFER_SIZE = 500;
    // calibration
    private  float dX = 0;
    private  float dY = 0;
    private  float dZ = 0;
    // buffer variables
    private float X;
    private float Y;
    private float Z;
    private int cnt = 0;

    // returns last SenorEvent parameters
    public Point getLastPoint(){
        return new Point(lastX, lastY, lastZ, 1);
    }

    // returrns parameters, using buffer: average acceleration
    // since last call of getPoint(). 
    public Point getPoint(){

        if (cnt == 0){
            return new Point(lastX, lastY, lastZ, 1);
        }

        Point p =  new Point(X, Y, Z, cnt);

        reset();
        return p;
    }

    // resets buffer
    public void reset(){
        cnt = 0;
        X = 0;
        Y = 0;
        Z = 0;
    }


    public void onSensorChanged(SensorEvent se) {
        float x = se.values[SensorManager.DATA_X] + dX;
        float y = se.values[SensorManager.DATA_Y] + dY;
        float z = se.values[SensorManager.DATA_Z] + dZ;

        lastX = x;
        lastY = y;
        lastZ = z;

        X+= x;
        Y+= y;
        Z+= z;

        if (cnt < BUFFER_SIZE-1) {
            cnt++;
        } else
        {
            reset();
        }
    }

    public int getCnt(){
        return cnt;
    }

    public  void setdX(float dX) {
        this.dX = dX;
    }

    public  void setdY(float dY) {
        this.dY = dY;
    }

    public  void setdZ(float dZ) {
        this.dZ = dZ;
    }
}

每次实验前都必须调用校准加速度计。测量时不得更改手机方向。

要校准加速度计,我使用此类:

public class Calibrator {

    final static int UPDATE_INTERVAL = 400;
    final static int ITERATIONS = 5;
    Handler hRefresh;
    XYZAccelerometer acc;
    int eventNumber;
    private LinkedList calData;

    public Calibrator(Handler hRefresh, XYZAccelerometer acc, int eventNumber) {
        this.hRefresh = hRefresh;
        this.acc = acc;
        this.eventNumber = eventNumber;
    }

    public void calibrate() {
        final Timer calTimer = new Timer();
        calData = new LinkedList();
        acc.setdX(0);
        acc.setdY(0);
        acc.setdZ(0);

        calTimer.scheduleAtFixedRate(
                new TimerTask() {

                    public void run() {
                        addCalData(calData);
                        if (calData.size() > ITERATIONS) {
                            calTimer.cancel();
                            try {
                                calSensor(calData);
                            } catch (Exception ex) {
                                try {
                                    throw ex;
                                } catch (Exception ex1) {
                                     hRefresh.sendEmptyMessage(5);
                                }
                            }
                            hRefresh.sendEmptyMessage(eventNumber);
                        }
                    }
                },
                0,
                UPDATE_INTERVAL);
    }

    private void addCalData(LinkedList cD) {
        Point p = acc.getPoint();
        cD.add(p);
        acc.reset();
    }

    private void calSensor(LinkedList cD) throws Exception {
        if (cD.size() < ITERATIONS-1) {
            throw new Exception("not enough data to calibrate");
        }
        float x = 0;
        float y = 0;
        float z = 0;
        // Don't use first measure
        for (int i = 1; i < cD.size(); ++i) {
            x += cD.get(i).getX();
            y += cD.get(i).getY();
            z += cD.get(i).getZ();
        }

        x = x / (cD.size() - 1);
        y = y / (cD.size() - 1);
        z = z / (cD.size() - 1);

        acc.setdX(-x);
        acc.setdY(-y);
        acc.setdZ(-z);
    }
}

维护类以保存一个度量的数据

public class Point {
    private float x = 0;
    private float y = 0;
    private float z = 0;
    private int cnt = 1;

    public float getX() {
        return x/(float)cnt;
    }

    public float getY() {
        return y/(float)cnt;
    }

    public float getZ() {
        return z/(float)cnt;
    }

    public Point(float x, float y, float z, int cnt) {
        this.x = x;
        this.y = y;
        this.z = z;
        this.cnt = cnt;
    }


    public float getForce(){
        return getX()*getX()+getY()*getY()+getZ()*getZ();
    }
}

用于处理度量数据的类

public class MeasurePoint {
    private float x;
    private float y;
    private float z;
    private float speedBefore;
    private float speedAfter;
    private float distance;
    private float acceleration;
    private long interval;
    private Point averagePoint;

    public MeasurePoint(float x, float y, float z, float speedBefore, long interval, Point averagePoint) {
        this.x = x;
        this.y = y;
        this.z = z;
        this.speedBefore = speedBefore;
        this.interval = interval;
        this.averagePoint = averagePoint;
        speedAfter = 0;
        calc();
    }

    private void calc(){
        //Acceleration as projection of current vector on average
        acceleration = this.x*averagePoint.getX() +
                        this.y*averagePoint.getY() +
                        this.z*averagePoint.getZ();
        acceleration = acceleration / ((float)Math.sqrt(averagePoint.getForce()));
        float t = ((float)interval / 1000f);
        speedAfter = speedBefore + acceleration * t;
        distance = speedBefore*t + acceleration*t*t/2;

    }

    public String getStoreString(){
        String s = "write here whatever you want";
        return s;
    }

// add getters
}

这一个 - 存储和保存数据数组

public class MeasureData {
    // points from accelerometr
    private LinkedList accData;
    private LinkedList data;
    // timer interval of generating points
    private long interval;

    public MeasureData(long interval) {
        this.interval = interval;
        accData = new LinkedList ();
        data = new LinkedList ();
    }

    public void addPoint(Point p){
        accData.add(p);
    }

    public void process(){

        for(int i = 0; i < accData.size(); ++i){
            Point p = accData.get(i);
            float speed = 0;

            if(i > 0){
                speed = data.get(i-1).getSpeedAfter();
            }
            data.add(new MeasurePoint(p.getX(), p.getY(), p.getZ(), speed, interval, getAveragePoint()));
        }
    }

    public boolean saveExt(Context con, String fname) throws Throwable {

        try {

            File file = new File(con.getExternalFilesDir(null), fname);
            FileOutputStream os = new FileOutputStream(file);
            OutputStreamWriter out = new OutputStreamWriter(os);


            for (int i = 0; i < data.size(); ++i) {
                MeasurePoint m = data.get(i);
                out.write(m.getStoreString());
            }

            out.close();
        } catch (Throwable t) {
            throw (t);
        }
        return true;
    }

    private Point getAveragePoint() {
        float x = 0;
        float y = 0;
        float z = 0;

        for(int i = 0; i < accData.size(); ++i){
            Point p = accData.get(i);
            x += p.getX();
            y += p.getY();
            z += p.getZ();
        }

        return new Point(x, y, z, 1);
    }

    public float getLastSpeed(){
        return data.getLast().getSpeedAfter();
    }

    public float getLastSpeedKm(){
        float ms = getLastSpeed();
        return ms*3.6f;
    }
}

最后,如何在你的活动中使用所有这些(我清理了很多,对不起,如果它不会编译 - 填写免费写在评论中:

public class TestActivity extends Activity {


    static final int TIMER_DONE = 2;
    static final int START = 3;
    static final int CAL_TIMER_DONE = 4;
    static final int ERROR = 5;

    private StartCatcher mStartListener;
    private XYZAccelerometer xyzAcc;
    private SensorManager mSensorManager;
    private static final long UPDATE_INTERVAL = 500;
    private static final long MEASURE_TIMES = 20;
    private Timer timer;
    private TextView tv;
    private Button testBtn;
    int counter;
    private MeasureData mdXYZ;


    /** handler for async events*/
    Handler hRefresh = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case TIMER_DONE:

                    onMeasureDone();
                    String es1 = Float.toString(Math.round(mdXYZ.getLastSpeedKm()*100)/100f);
                    tv.append(" END SPEED " + es1 + " " + es2 + " \n");
                    enableButtons();
                    break;
                case START:
                    tv.append(" START");
                    timer = new Timer();
                    timer.scheduleAtFixedRate(
                            new TimerTask() {

                                public void run() {
                                    dumpSensor();
                                }
                            },
                            0,
                            UPDATE_INTERVAL);

                    break;
                case ERROR:
                    Toast.makeText(getApplicationContext(), "ERROR", Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    };

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        tv = (TextView) findViewById(R.id.txt);
        testBtn = (Button) findViewById(R.id.btn);
    }

    @Override
    protected void onResume() {
        super.onResume();
        tv.append("\n ..");
        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        setAccelerometer();
        setStartCatcher();
        mSensorManager.registerListener(xyzAcc,
                mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                SensorManager.SENSOR_DELAY_GAME);

    }

    @Override
    protected void onPause() {
        mSensorManager.unregisterListener(xyzAcc);
        super.onPause();
    }


    public void onButtonTest(View v) {
        disableButtons();
        mdXYZ = new MeasureData(UPDATE_INTERVAL);
        counter = 0;
        tv.setText("");
        tv.append("Calibrating");
        Calibrator cal = new Calibrator(hRefresh, xyzAcc, START);
        cal.calibrate();

    }

    void dumpSensor() {
        ++counter;
        mdXYZ.addPoint(xyzAcc.getPoint());

        hRefresh.sendEmptyMessage(TICK);

        if (counter > MEASURE_TIMES) {
            timer.cancel();
            hRefresh.sendEmptyMessage(TIMER_DONE);
        }

    }

    private void enableButtons() {
        testBtn.setEnabled(true);

    }


    private void setAccelerometer() {
        xyzAcc = new XYZAccelerometer();
        mSensorManager.registerListener(xyzAcc,
                mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                SensorManager.SENSOR_DELAY_UI);
    }


    private void disableButtons() {
        testBtn.setEnabled(false);
    }

    private void onMeasureDone() {
        try {
            mdXYZ.process();
            long now = System.currentTimeMillis();
            mdXYZ.saveExt(this, Long.toString(now) + ".csv");
        } catch (Throwable ex) {
            Toast.makeText(this, ex.getMessage().toString(), Toast.LENGTH_SHORT);
        }
    }
}

<serviceLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <serviceButton 
        android:id="@+id/btn"
        android:text="TEST"
        android:layout_width="300px"
        android:layout_height="200px"
        android:onClick="onButtonTest"  />

        <serviceTextView  
    android:id = "@+id/txt"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text=":"
    />
<service/LinearLayout>

答案 1 :(得分:0)

虽然在developer.android.com网站上已明确说明

  

您可以使用线性加速度计来查看您的汽车行驶速度&#34;

但我没有找到任何证据表明这是真的。(在测试或示例或代码中)。

现在,不幸的是,我确信用线性加速度计计算汽车的速度是不可能的。

答案 2 :(得分:0)

请参阅下面的代码以使用加速度计获取速度

public class SensorTestActivity extends Activity implements SensorEventListener {
    double calibration = Double.NaN;
    private SensorManager sensorManager;
    private boolean color = false;
    private TextView view;
    private long lastUpdate;

    float appliedAcceleration = 0;
    float currentAcceleration = 0;
    float velocity = 0;
    Date lastUpdatedate;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        view = findViewById(R.id.textView);
//        view.setBackgroundColor(Color.GREEN);
        lastUpdatedate = new Date(System.currentTimeMillis());
        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        lastUpdate = System.currentTimeMillis();
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
//        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
//            getAccelerometer(event);
//        }

        double x = event.values[0];
        double y = event.values[1];
        double z = event.values[2];
        double a = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2));
        if (calibration == Double.NaN)
            calibration = a;
        else {
            updateVelocity();
            currentAcceleration = (float)a;
        }

    }

    private void getAccelerometer(SensorEvent event) {
        float[] values = event.values;
        // Movement
        float x = values[0];
        float y = values[1];
        float z = values[2];

        float accelationSquareRoot = (x * x + y * y + z * z)
                / (SensorManager.GRAVITY_EARTH * SensorManager.GRAVITY_EARTH);
        long actualTime = event.timestamp;
        if (accelationSquareRoot >= 2) //
        {
            if (actualTime - lastUpdate < 200) {
                return;
            }
            lastUpdate = actualTime;
//            Toast.makeText(this, "Device was shuffed", Toast.LENGTH_SHORT)
//                    .show();
            if (color) {
                view.setBackgroundColor(Color.GREEN);
            } else {
                view.setBackgroundColor(Color.RED);
            }
            color = !color;
            view.setText("SPEEDDDDD=== "+accelationSquareRoot);
//            Log.i("SensorTestActivity","SPEEDDDDD=== "+accelationSquareRoot+"     ");
        }
    }

    private void updateVelocity() {
        // Calculate how long this acceleration has been applied.
        Date timeNow = new Date(System.currentTimeMillis());
        long timeDelta = timeNow.getTime()-lastUpdatedate.getTime();
        lastUpdatedate.setTime(timeNow.getTime());

        // Calculate the change in velocity at the
        // current acceleration since the last update.
        float deltaVelocity = appliedAcceleration * (timeDelta/1000);
        appliedAcceleration = currentAcceleration;

        // Add the velocity change to the current velocity.
        velocity += deltaVelocity;

        final double mph = (Math.round(100*velocity / 1.6 * 3.6))/100;
        Log.i("SensorTestActivity","SPEEDDDDD=== "+mph+"     "+velocity);
    }

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

    }

    @Override
    protected void onResume() {
        super.onResume();
        // register this class as a listener for the orientation and
        // accelerometer sensors
        sensorManager.registerListener(this,
                sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                SensorManager.SENSOR_DELAY_NORMAL);
    }

    @Override
    protected void onPause() {
        // unregister listener
        super.onPause();
        sensorManager.unregisterListener(this);
    }
}