是否有可能测量手机垂直移动的距离

我只是想知道是否有可能/我会在哪里开始测量Android设备的向上和向下移动。 我需要它尽可能准确。

我很遗憾去哪里看到我已经看到有一些方法在这里 ,看着这些旧的问题,他们提到这是相当困难的,但我想知道是否有任何改进之后,在新的Android版本。

下面的图片是我希望手机进入的方向的另一个例子。

在这里输入图像说明

Solutions Collecting From Web of "是否有可能测量手机垂直移动的距离"

在解决之前,我会将解决scheme拆分成小型资产,并将其解决

  1. 我们需要听电话传感器
  2. 我们需要检查手机是否处于垂直位置
  3. 我们需要build立一个时间计数器来垂直注册电话并计数
  4. 我们需要在屏幕上显示时间

对于第一步,我将实现SensorEventListener到我们的类,这将允许我们使用onSensorChanged方法。

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

对于第二步,我已经build立了一个名为isPhoneVertical的方法来检查我们的手机是否处于垂直方向,主要是检查y轴。 你可以通过改变maxVertical改变陡度。 价值不那么陡峭,0意味着手机应该几乎100%垂直。 对于我的testing,它被设置为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; } 

对于第3步,我已经做了几个方法来检查开始时间和停止时间,并更新一个全局variables,以秒为单位跟踪时间。

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

对于第4步我做了一个正常runOnUiThread更新屏幕上的时间。

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

这就是说这个解决scheme是为了说明如何达到这个目标,我相信它可以通过不同的方式来完成。 但是我想展示解决scheme背后的逻辑。

这是一个应用程序的屏幕截图,用于计算每次手机垂直时间和垂直总时间。

在这里输入图像说明

解决scheme包括一些解释:

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> 

我也会留下一些链接以供阅读。

如果您认为我可能会添加更多可以帮助您进一步解决问题的信息,请告诉我。 目前还不清楚你是否正在寻找步行检测/计数器,如果这是你有趣的事情,看看下面的答案/链接。

  • 活动识别API
  • 如何检测与Android加速计走
  • 如何在Android中使用加速计计算精确的脚步数?
  • 在Android中使用Accelerometer准确检测移动

和GitHub源代码

之前,我们必须知道我们正在使用的传感器API。

Android传感器API

Android传感器API提供了许多类和接口。 传感器API的重要接口如下:

1)SensorManager类

android.hardware.SensorManager类提供了一些方法:

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

您可以通过调用方法getSystemService()并传递SENSOR_SERVICE常量来获取SensorManager的实例。

 SensorManager sm = (SensorManager)getSystemService(SENSOR_SERVICE); 

2)传感器类

android.hardware.Sensor类提供获取传感器信息的方法,如传感器名称,传感器types,传感器分辨率,传感器types等

3)SensorEvent类

它的实例是由系统创build的。 它提供有关传感器的信息。

4)SensorEventListener接口

它提供了两种callback方法来获取传感器值(x,y和z)变化或传感器准确度变化时的信息。 公共抽象方法描述

  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. 

注意:假设* X =水平边,Y =垂直边和Z =标高*。

创build一个简单的XML像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> 

接下来创build一个像MainActivity.java这样简单的活动来测量向下/向上的方向。

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

输出看起来像:

X = 0.0 [不水平移动]

Y = 9.77622 [垂直移动]

Z = 0.813417 [在高程上移动]

案例1:使用GPS

几乎所有的Android设备都内置GPS,可以提供水平方向2-3米,垂直方向4-5米的精度。 如果您的情况下垂直精度正常,您可以在运动开始之前获得高度,并在结束时比较它们并获得垂直运动。

如果您需要更好的准确性,GPS没有可以提供的客户级设备。 有特殊的专业GPS设备(土地测量师使用,花费1000美元,很less运行的Android操作系统),可以提供厘米级别的准确性,但这不是我们这里所说的。

为了有良好的GPS接收,你需要在室外。

情况2:使用运动传感器

如果知道初始速度,加速度和时间可以计算移动的距离。 看看这个http://www.dummies.com/education/science/physics/finding-distance-using-initial-velocity-time-and-acceleration/

我们有

距离=开始速度+(1/2)*加速度*时间^ 2

在我们的例子中,我们知道设备内置时钟的时间(实际上是时间跨度),我们可以使用设备的传感器获得加速度。 请注意TYPE_ACCELEROMETER传感器给我们沿每个轴的加速力, 包括重力 ,所以我们必须减去引力(我们从TYPE_GRAVITY传感器得到它)。 请记住,当有人拿起设备时,他的手并不一定以稳定的速度或加速度移动。 因此,必须考虑速度和加速度的变化。

关于起跑速度,有几种情况。

如果它是零(用户站在build筑物内,从桌子上拿起电话并移动到他的耳朵),您只需从公式中省略。

如果它不是零,那么

速度=设备速度+用户的“车辆”速度

,我们必须使用GPS来计算它。 如果用户在起重直升机上,我们可以这样做。 如果他在电梯里,或者上楼,我们不能,因为那里没有GPS接收。 在直升机或电梯加速的情况下,情况变得更加复杂,因为没有办法自动地将直升机/电梯加速度与“用户移动设备”加速度分开。 当用户走路时也一样。 在那种情况下,随着他的脚步和身体的上下移动,会发生速度,加速度和方向的不断变化。

我承认,我不知道使用运动传感器方法计算距离的实际精度。 正如你从我正在写的东西看到的那样,我想没有办法find在任何情况下都有效的解决scheme。 但是,在用户仍然只是垂直移动设备的特殊情况下,您可以做一些我相信的事情。

PS我没有深入了解如何从设备传感器获取GPS数据或运动传感器数据的细节,因为您可以轻松地谷歌并find帮助。 我相信更重要的是了解涉及设备垂直运动的math和物理。