воскресенье, 23 октября 2011 г.

Android: computing speed and distance using accelerometer






It's possible to compute distance and speed using only accelerometer, but with three conditions:
1. Linear movement - trajectory must be straight.
2. Slope of the road must be constant.
3. You must perform calibration procedure before start.

Where can you use this method with such restrictions - it's up to you... Now, how to do it:

1. We need something, implementing SensorEventListener interface. For the future use, let's add following abstract class:
  
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) {
    }
}
 

and this will be our 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;
    }
}
 

Calibrating accelerometer must be called before each experiment. Phone orientation must not be changed while measuring.

To calibrate accelerometer, i use this class:

  
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);
    }
}

 

maintenance class to keep data of one measure

  
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();
    }
}
 

And class to process data of measure
  
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
}
 

This one - to store and save data array
  
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;
    }
}

 

And, finally, how to use all this in your activity(I cleaned it up a lot, sorry if it will not complie - fill free to write it in comments:

  
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>


77 комментариев:

Lork комментирует...

Hi Maep i had seen your post on stackoverflow(http://stackoverflow.com/questions/7858759/android-type-linear-acceleration-sensor-what-does-it-show) do you found the reason to use TYPE_LINEAR_ACCELERATION instead of TYPE_ACCELEROMETER ? Watching your code here seems not...I'm in your similar situation, i need the acceleration vector whitout gravity!!!! Thanx!

Unknown комментирует...

hi! no,TYPE_LINEAR_ACCELERATION still is wired thing i can't understand:)

sravan комментирует...

hi can u place full project for finding speed and travelled distance .

sravan комментирует...

please send me entire project to sravan0@yahoo.com

Gaurav Wable комментирует...

where is getSpeedAfter() method.
And StartCatcher Class.

Unknown комментирует...

please send my this poject to suno@ynet.sk

Sumant комментирует...

Getting problem for StartCatcher Class. So can you plz send me your project on sumant4943@gmail.com.

Unknown комментирует...

Please send me project to fox902008@gmail.com.
I can't find getSpeedAfter() method and StartCatcher Class.
Please help.

Me, Myself and More... комментирует...

can you please send me your code to r.apurva14@gmail.com

randomessa комментирует...

hello! can I have the full project as well. Thank you.

accelgyro комментирует...

Hello mate!

Could you please send me whole project ?

Thanks a lot!

accelgyro@gmail.com

Cheers!

Unknown комментирует...

Hi! Could you please send the source code of this project to me? Thanks.

bennychen0123@gmail.com

Unknown комментирует...

Hi there,great work! Can you please send me your full project @ aaditya.adi.91@gmail.com.

Unknown комментирует...

hey..could u please send me your project
at fayazy116@gmail.com

Unknown комментирует...

heyy,could you please send me your project at fayazy116@gmail.com

Unknown комментирует...
Этот комментарий был удален автором.
Unknown комментирует...

Can you please send full project to seenivasanv57@gmail.com?

Unknown комментирует...
Этот комментарий был удален автором.
Unknown комментирует...
Этот комментарий был удален автором.
Unknown комментирует...

Я так понял что ты из Росии ) Я твою статью на хабре еще видел.
Судя по твоему проекту с разгоном до 100, у тебя получилось это реализовать )

Я хочу сделать таксометр, и меня в принципе устраивает как он работает через GPS, но есть момент, что при остановке машины он бывает долго думает, и простой начинает считать по разному, то через 5 сек, то через 15.

Мне не важно правильно ли датчик будет определять скорость свыше 20 км/ч, но мне важно чтобы аксселерометр быстро реагировал на то, когда я остановился или сбросил скорость до значение меньше за 20 км в час.. Это реально сделать комбинируя данные с ГПС и датчика? Если да не мог ли бы ты помочь мне немного.. (Я твои сурцы пытался использовать, но после калибровки он мне выдает 20км/час скорость лежа на столе, что то там не то наверно, или руки кривые

Мне конечно эта тема интересна, но я сейчас не могу себе позволить что-то неделями учить, проекты горят, думать долго нельзя. Если не сложно напиши на e-mail shutter@inbox.com или напиши в скайп rumzik92, очень надеюсь на то, что поможешь... Заранее спасибо

Unknown комментирует...

Please send me entire project to sridhar.474@gmail.com

Анонимный комментирует...

Hi Meap, would you please send me the entire project source code?

With Thanks and Regards,
Arshad

Анонимный комментирует...

sorry I forgot to mention my email.
m99arshad@gmail.com

Wuth Thanks and Regards,
Arshad

Unknown комментирует...
Этот комментарий был удален автором.
Unknown комментирует...

Dear Maep,

This is my final year project, I got to be honest, mobile development is new to me (I'm mostly experienced with C programming on MCUs) but I want to learn and develop my skills in this field.

I want to develop a Vibration Analysis application. I want to use it to measure the vibration in an Elevator so one can analyze the riding comfort.

First, the phone will be put on the flooring in the middle of the cabin, the measurement should be along the Elevator's total travel (up to 1min ride) and at least following to 2 axis (X and Y, if I can measure following Z too, it will be great). Once those data are captured during this 1min, I want to plot them on a separate graphs (first graph: X position vs time, second graph: Y position vs time). Also, I might add the feature of measuring the elevator speed while traveling inside the cabin.

My questions are:

1- Should I use the acceleration or the Linear-acceleration sensor?
2- Can the accelerometer (on Samsung Note 2 for example) detect small vibrations? like ones that occurs in an elevator (around 15 m-g)

I want to know how hard can this project be, and if it's feasible or not. Can You send me the project to Anthony.A87@live.com so I can have a place to start with

Many Thanks

Unknown комментирует...

Hi! Can you send the entire project to badcipry92@yahoo.com ?
I'm having difficulties! Thanks!

Unknown комментирует...

Hi! Please, Can you send the entire project to eubeju@gmail.com ?
Thanks!

MUFIX комментирует...

Could tou share the project on

Ahmed.salem@mufix.org

Unknown комментирует...

Hi there,great work! Can you please send me your full project
hadi.dehghani.t@gmail.com

Unknown комментирует...

Please send me project to weihao0220@gmail.com

Unknown комментирует...
Этот комментарий был удален автором.
LihinWorld комментирует...

Hi Meap, would you please send me the entire project source code to my email address lichienmine@gmail.com ?

With Thanks and Regards,
Lichien

Unknown комментирует...

Hi,
send me please entire project
Hoodrij@gmail.com

IT DEVELOPEMENT комментирует...

Hi,
can u send me code on star.ankit90@gmail.com

Thanks!

IT DEVELOPEMENT комментирует...
Этот комментарий был удален автором.
Unknown комментирует...

please send my this poject to
emily2.3kim@gmail.com

Unknown комментирует...

Hi. plz Send me this project to cbmoon8@naver.com

I really need your full code..!
thnx.

Unknown комментирует...

Can you please send me whole project on bodemarkovic@gmail.com

Thank you in advance! It is very nice project.

Unknown комментирует...

Can anyone send me whole project on filip100janovski@gmail.com

Unknown комментирует...

Hi there,great work! Can you please send me your full project to deepak.d.hosur@gmail.com

Unknown комментирует...

geeting problem: where is getSpeedAfter() method.
And StartCatcher Class.
Please send me full code to deepak.d.hosur@gmail.com

Анонимный комментирует...

Hi! I have some problems.. Can you please send me whole project on a.mekhedenko@gmail.com
Thank you very much!

3r4t3434 комментирует...

can I please have the project

tarazansafak@gmail.com

thanks!

Unknown комментирует...

Please send me the project:
znaglis@gmail.com
Thanks in advance

Hoang.Mirs комментирует...

Can you send me your project?
My email: hoang.mirs@gmail.com

Anuj Sharma комментирует...

Thanks for such a nice tutorial, But I am not able to run this on my machine. Please send whole project to my mail address anujs1991@gmail.com and kindly upload your project to github and post its link so that most of user can take benefit and praise your work.
Thanks.

Sudhakar V комментирует...

Hi,

In calibration what you are doing, can you please elaborate.

Thanks
Sudhakar

FullMusic комментирует...

Desde colombia , envia correo rapiaven1890@gmail.com, gracias

abhi комментирует...
Этот комментарий был удален автором.
Technology introduction  комментирует...

Hey share the full source

Unknown комментирует...

plz plz plz.....plz send me the whole project on my email: agarwalsarthak22@gmail.com
i need it urgently. Thank you in advance. :)

Unknown комментирует...

URGENT ANYONE!!

Could you please send me to fgrass91@gmail.com I would be really thankful.
I couldn't get it to work.
I really would need that and it would help me a lot.

THANK YOU!!

Unknown комментирует...

could you please send source code project to prakash.cse1988@gmail.com

Unknown комментирует...

could you please send source code project to kskung12@gmail.com

Unknown комментирует...

kindly send the whole project on the given id.it will be very useful for
ahmad-arif121@hotmail.com

Unknown комментирует...

Hey share the StartCatcher java file to my mail id : snarendran89@gmail.com

hemant комментирует...

hiiii can u please send me whole code on sharmagautam422@gmail.com

Unknown комментирует...

HI, could someone send me the Whole Project, I really need this startu understanding of all handles ALL.

Tks in advance
Marco Santos **** marrco.santos@gmail.com

Sanjay Krishna комментирует...

Hi I'm having some difficulties. can u pls send the whole project to sanjaykrishna031@gmail.com.Thanks

Nguyễn Văn Năm комментирует...

Dear Sir,

Can you please share with me this project?

I researching about calculate distance while walking in android. Thank you for help!

Unknown комментирует...

Dear Sir,
Could you send me this project to danielwilson8654@gmail.com

pat комментирует...

Nice project. Please send the source simopatrick.simpat@gmail.com

Unknown комментирует...

Great work. Can you please send it to ozanadgzl.job@gmail.com

Unknown комментирует...
Этот комментарий был удален автором.
Unknown комментирует...

Awesome work. Can you please send me your code creedphilip.1994@gmail.com

neusi комментирует...

Hi your Code is brilliant,

would you please send me the whole Project, too?
@ code@neusinger.de

Thanks a alot!

Sneha Vajjaramatti комментирует...

hi nice explination. can u please share code to svajjaramatti@gmail.com
thank u in advance

Unknown комментирует...

Hi, can you please send code to jesusmartinezdiaz8@gmail.com
thank you in advance

Kiyingi Raymond комментирует...

Hi,this extra ordinary.
help me forward that whole sourcecode to my email raymondkiyingi03@gmail.com.

Siranjeevi комментирует...

Share the source code on siran10289@gmail.com

Unknown комментирует...

could you please share the complete code jahanzeb.naeem@hotmail.com

Unknown комментирует...

Hi, could you please send code to :
ahmedberriche1993@gmail.com
Thank you!!

Unknown комментирует...

Hi! Could you please send the source code of this project to me? Thanks
hiral.stegowl@gmail.com

Unknown комментирует...

Hi! Could you please send the source code of this project to me? Thanks
abrados.1@gmail.com

Unknown комментирует...

Можно ли получить исходный код проекта. Почта lana.chebotareva.95@mail.ru

Lucas Coelho комментирует...

Please send me project to lucascoelhosi96@gmail.com

bebo_mb комментирует...

пишет...
Please send me project to mohamed.bahaa.embaby@gmail.com