Is it possible to measure the how far the phone travels vertically

前端 未结 3 1422
没有蜡笔的小新
没有蜡笔的小新 2021-02-09 05:02

I\'m just wondering if it is possible / where would I get started in measuring the upwards and downwards movement of an Android device. I\'d need it to be as accurate as possibl

相关标签:
3条回答
  • 2021-02-09 05:47

    Case 1: Using GPS

    GPS built in in almost all android devices can provide position with best possible accuracy of about 2-3 meters horizontally and 4-5 meters vertically. If that vertical accuracy is ok for your case, you can get altitude before the movement starts, and when it ends, compare them and get vertical movement.

    If you need better accuracy, no android customer level device GPS can provide it. There are special professional GPS devices (used by land surveyors, costing 1000s of dollars, and very rarely running android os) that can provide cm-level accuracy, but this is not what we are talking about here.

    Ofcource, you need to be outdoors in order to have good GPS reception.

    Case 2: Using motion sensors

    If one knows initial speed, acceleration, and time can calculate distance moved. Have a look at this http://www.dummies.com/education/science/physics/finding-distance-using-initial-velocity-time-and-acceleration/

    We have

    distance=start speed+ (1/2) * acceleration * time^2

    In our case we know time (actually time-span) from the devices inbuilt clock, and we can get acceleration using the device's sensors. Note that TYPE_ACCELEROMETER sensor gives us acceleration force along each axis including gravity, so we have to subtract the gravitational force (we get it from TYPE_GRAVITY sensor). Keep in mind that when someone picks up a device, his hand doesn't necessarily moves in steady speed or acceleration. Therefore, changes in speed and acceleration must be taken into consideration.

    Concerning start speed there are several cases.

    In case it is zero (user is standing in a building, picks phone from a table and moves it to his ear) you just omit it from the formulas.

    In case it not zero so that

    speed=device speed+user's "vehicle" speed

    , we have to use GPS to calculate it. If the user is in a lifting helicopter we can do this. In case he is in a elevator or going up the stairs of a building we cannot because there is no GPS reception there. Things get more complicated in case the helicopter or the elevator accelerates too because there is no way to automatically separate helicopter/elevator acceleration from "user moving the device" acceleration. Same when the user is walking. In that case continuous changes of speed, acceleration and direction take place as he does his steps and the body moves up and down.

    I confess that I have no idea on the actual accuracy one can get in calculated distance by using the motion sensors method. And as you see from what I am writing there is no way I think to find a solution that works under every circumstances. But in the special case that the user is still and just moves the device vertically you can do something I believe.

    P.S. I am not getting into details on how to get GPS data or Motion sensor data from the device sensors because you can easily google it and find help on that. I believe what is more important is to understand the maths and physics that get involved in the devices vertical movement.

    0 讨论(0)
  • 2021-02-09 05:54

    Before going to solution, I would split the solution in small assets and put the puzzle for solution

    1. We need to listen to phone sensors
    2. We need to check phone is in vertical position or not
    3. We need to make a time counter that register the phone vertically and count up
    4. We need to display the time on screen

    For step one, I would implement SensorEventListener to our class, this will allow us to use onSensorChanged method.

    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            listenToSensors(event);
        }
    }
    
    private void listenToSensors(SensorEvent event) {
        if (isPhoneVertical(event)) {
            timeCounter();
            if (mStatus) {
                mStartTime = getStartTime();
                mStatus = false;
            }
        } else {
            if (!mStatus) {
                mTotalTime = getDiffTime() + mTotalTime;
                mStatus = true;
            }
        }
    }
    

    For step two, I have built a method called isPhoneVertical to check if our phone is in vertical way or not, it is primarily checking the y axis. You can change the steep degree by changing maxVertical. Less value her less steep, 0 means the phone should almost be 100% vertical. For my test it is set to 3.

    private boolean isPhoneVertical(SensorEvent event) {
        float[] values = event.values;
        double y = values[1];
        // do not change this value
        double yAxisInitValue = 10.0;
        double verMargin = yAxisInitValue - maxVertical;
    
        return y >= verMargin;
    }
    

    For step 3 I have made few method to check start time and stop time and update a global variable that keep track of time in seconds.

    private long getStartTime() {
        return System.currentTimeMillis() / 1000;
    }
    
    private long getDiffTime() {
        return getStartTime() - mStartTime;
    }
    

    For step 4 I have made a regular runOnUiThread to update the time on screen.

    private void timeCounter() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mView1.setText("Phone has been vertical for: " + getDiffTime() + " seconds");
                mView2.setText("The total time: " + (mTotalTime + getDiffTime()) + "");
            }
        });
    }
    

    That said this solution is to illustrate how this goal can be reached, I am sure it can be done different ways. But I wanted to show the logic behind the solution.

    And here is a screen shot of the app that counts the time for each time the phone is vertical and total time it has been vertical.

    The solution including some explanations:

    MainActivity.java

    public class MainActivity extends Activity implements SensorEventListener {
        private SensorManager mSensorManager;
        private Sensor mAccelerometer;
        private TextView mView1, mView2;
        private long mStartTime;
        private long mTotalTime;
        private boolean mStatus = true;
        // less value her less steep, 0 means the phone should almost be 100% vertical
        // try it out
        private double maxVertical = 3.0;
    
        @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_main);
            mView1 = (TextView) findViewById(R.id.textView1);
            mView2 = (TextView) findViewById(R.id.textView2);
    
            mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
            mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        }
    
        @Override
        public void onSensorChanged(SensorEvent event) {
            if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
                listenToSensors(event);
            }
        }
    
        private void listenToSensors(SensorEvent event) {
            if (isPhoneVertical(event)) {
                timeCounter();
                if (mStatus) {
                    mStartTime = getStartTime();
                    mStatus = false;
                }
            } else {
                if (!mStatus) {
                    mTotalTime = getDiffTime() + mTotalTime;
                    mStatus = true;
                }
            }
        }
    
        // This method return true only for specific phone orientation
        // y axis for vertical orientation
        private boolean isPhoneVertical(SensorEvent event) {
            float[] values = event.values;
            double y = values[1];
            // do not change this value
            double yAxisInitValue = 10.0;
            double verMargin = yAxisInitValue - maxVertical;
    
            return y >= verMargin;
        }
    
        private long getStartTime() {
            return System.currentTimeMillis() / 1000;
        }
    
        private long getDiffTime() {
            return getStartTime() - mStartTime;
        }
    
        // update steps in user interface
        private void timeCounter() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mView1.setText("Phone has been vertical for: " + getDiffTime() + " seconds");
                    mView2.setText("The total time: " + (mTotalTime + getDiffTime()) + "");
                }
            });
        }
    
        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
    
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            mSensorManager.registerListener(this,
                    mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                    SensorManager.SENSOR_DELAY_NORMAL);
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            // if you want to collect data while mobile screen off, just disable the
            // following line, the app will still collecting sensor data
            mSensorManager.unregisterListener(this);
        }
    }
    

    Activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="com.example.maytham.verticaltravels.MainActivity">
    
        <TextView
            android:id="@+id/textView1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_alignParentTop="true"
            android:textSize="20sp"
            android:text="TextView1" />
    
        <TextView
            android:id="@+id/textView2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_below="@+id/textView1"
            android:layout_marginTop="57dp"
            android:textSize="20sp"
            android:text="TextView2" />
    </RelativeLayout>
    

    I will leave some link for reading as well.

    • https://developer.android.com/reference/android/hardware/SensorManager.html
    • http://www.livescience.com/40103-accelerometer-vs-gyroscope.html
    • https://physics.stackexchange.com/questions/41653/how-do-i-get-the-total-acceleration-from-3-axes
    • https://code.tutsplus.com/tutorials/how-to-recognize-user-activity-with-activity-recognition--cms-25851
    • http://www.vogella.com/tutorials/AndroidSensor/article.html
    • How to get Android phone orientation matching human orientation?

    If you think I might add more information that can help you further with your question please let me know. It is also unclear if you was looking for walking detection/counter, if that is some thing you are interesting in, look at following answers/links.

    • Activity Recognition Api
    • How to detect walking with Android accelerometer
    • how to calculate exact foot step count using accelerometer in android?
    • Detect Movement Accurately using Accelerometer in Android

    and GitHub source codes

    • https://github.com/bagilevi/android-pedometer
    • https://github.com/tartakynov/robowalk
    0 讨论(0)
  • 2021-02-09 05:58

    Before,We have to know about sensor API which is we are using.

    Android Sensor API

    Android sensor API provides many classes and interface. The important classes and interfaces of sensor API are as follows:

    1) SensorManager class

    The android.hardware.SensorManager class provides methods :

    to get sensor instance,
    to access and list sensors,
    to register and unregister sensor listeners etc.
    

    You can get the instance of SensorManager by calling the method getSystemService() and passing the SENSOR_SERVICE constant in it.

    SensorManager sm = (SensorManager)getSystemService(SENSOR_SERVICE);  
    

    2) Sensor class

    The android.hardware.Sensor class provides methods to get information of the sensor such as sensor name, sensor type, sensor resolution, sensor type etc.

    3) SensorEvent class

    Its instance is created by the system. It provides information about the sensor.

    4) SensorEventListener interface

    It provides two call back methods to get information when sensor values (x,y and z) change or sensor accuracy changes. Public and abstract methods Description

      void onAccuracyChanged(Sensor sensor, int accuracy)
    
        //it is called when sensor accuracy is changed.
    
        *void onSensorChanged(SensorEvent event)*
    
        //it is called when sensor values are changed.
    

    Note: assume *X=horizontal side,Y=Vertical side and Z=elevation * of the device.

    create a simple xml like active_main.xml

     <RelativeLayout xmlns:androclass="http://schemas.android.com/apk/res/android"  
            xmlns:tools="http://schemas.android.com/tools"  
            android:layout_width="match_parent"  
            android:layout_height="match_parent"  
            tools:context=".MainActivity" >  
    
            <TextView  
                android:id="@+id/textView1"  
                android:layout_width="wrap_content"  
                android:layout_height="wrap_content"  
                android:layout_alignParentLeft="true"  
                android:layout_alignParentTop="true"  
                android:layout_marginLeft="92dp"  
                android:layout_marginTop="114dp"  
                android:text="TextView" />  
    
        </RelativeLayout>  
    

    Next create a simple activity like MainActivity.java to measure directions like downward/upwards.

    import android.app.Activity;  
    import android.os.Bundle;  
    import android.widget.TextView;  
    import android.widget.Toast;  
    import android.hardware.SensorManager;  
    import android.hardware.SensorEventListener;  
    import android.hardware.SensorEvent;  
    import android.hardware.Sensor;  
    import java.util.List;  
    public class MainActivity extends Activity {  
        SensorManager sm = null;  
        TextView textView1 = null;  
        List list;  
    
        SensorEventListener sel = new SensorEventListener(){  
            public void onAccuracyChanged(Sensor sensor, int accuracy) {}  
            public void onSensorChanged(SensorEvent event) {  
                float[] values = event.values;  
                textView1.setText("x: "+values[0]+"\ny: "+values[1]+"\nz: "+values[2]);  
            }  
        };  
    
        @Override  
        public void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.activity_main);  
    
            /* Get a SensorManager instance */  
            sm = (SensorManager)getSystemService(SENSOR_SERVICE);  
    
            textView1 = (TextView)findViewById(R.id.textView1);  
    
            list = sm.getSensorList(Sensor.TYPE_ACCELEROMETER);  
            if(list.size()>0){  
                sm.registerListener(sel, (Sensor) list.get(0), SensorManager.SENSOR_DELAY_NORMAL);  
            }else{  
                Toast.makeText(getBaseContext(), "Error: No Accelerometer.", Toast.LENGTH_LONG).show();  
            }  
        }  
    
        @Override  
        protected void onStop() {  
            if(list.size()>0){  
              sm.unregisterListener(sel);  
            }  
            super.onStop();  
        }  
    }  
    

    Output seems like:

    X=0.0[No Move On Horizontal]

    Y=9.77622[Moved on Vertically]

    Z=0.813417[Move on Elevation]

    0 讨论(0)
提交回复
热议问题