10

センサーのデータに基づいて位置を計算する Android アプリケーションを開発しています

  1. 加速度計 --> 直線加速度を計算

  2. 磁力計 + 加速度計 --> 移動方向

初期位置は GPS (緯度 + 経度) から取得されます。

センサーの測定値に基づいて、スマートフォンの新しい位置を計算する必要があります。

私のアルゴリズムは次のとおりです-(ただし、正確な位置を計算していません):改善を手伝ってください。

注: 私のアルゴリズムコードはC#です(センサーデータをサーバーに送信しています-データはデータベースに保存されています。サーバー上の位置を計算しています)

すべての DateTime オブジェクトは TimeStamps を使用して計算されています - 01-01-1970 から

    var prevLocation = ServerHandler.getLatestPosition(IMEI);
    var newLocation = new ReceivedDataDTO()
                          {
                              LocationDataDto = new LocationDataDTO(),
                              UsersDto = new UsersDTO(),
                              DeviceDto = new DeviceDTO(),
                              SensorDataDto = new SensorDataDTO()
                          };

    //First Reading
    if (prevLocation.Latitude == null)
    {
        //Save GPS Readings
        newLocation.LocationDataDto.DeviceId = ServerHandler.GetDeviceIdByIMEI(IMEI);
        newLocation.LocationDataDto.Latitude = Latitude;
        newLocation.LocationDataDto.Longitude = Longitude;
        newLocation.LocationDataDto.Acceleration = float.Parse(currentAcceleration);
        newLocation.LocationDataDto.Direction = float.Parse(currentDirection);
        newLocation.LocationDataDto.Speed = (float) 0.0;
        newLocation.LocationDataDto.ReadingDateTime = date;
        newLocation.DeviceDto.IMEI = IMEI;
        // saving to database
        ServerHandler.SaveReceivedData(newLocation);
        return;
    }


    //If Previous Position not NULL --> Calculate New Position
   **//Algorithm Starts HERE**

    var oldLatitude = Double.Parse(prevLocation.Latitude);
    var oldLongitude = Double.Parse(prevLocation.Longitude);
    var direction = Double.Parse(currentDirection);
    Double initialVelocity = prevLocation.Speed;

    //Get Current Time to calculate time Travelling - In seconds
    var secondsTravelling = date - tripStartTime;
    var t = secondsTravelling.TotalSeconds;

    //Calculate Distance using physice formula, s= Vi * t + 0.5 *  a * t^2
    // distanceTravelled = initialVelocity * timeTravelling + 0.5 * currentAcceleration * timeTravelling * timeTravelling;
    var distanceTravelled = initialVelocity * t + 0.5 * Double.Parse(currentAcceleration) * t * t;

    //Calculate the Final Velocity/ Speed of the device.
    // this Final Velocity is the Initil Velocity of the next reading
    //Physics Formula: Vf = Vi + a * t
    var finalvelocity = initialVelocity + Double.Parse(currentAcceleration) * t;


    //Convert from Degree to Radians (For Formula)
    oldLatitude = Math.PI * oldLatitude / 180;
    oldLongitude = Math.PI * oldLongitude / 180;
    direction = Math.PI * direction / 180.0;

    //Calculate the New Longitude and Latitude
    var newLatitude = Math.Asin(Math.Sin(oldLatitude) * Math.Cos(distanceTravelled / earthRadius) + Math.Cos(oldLatitude) * Math.Sin(distanceTravelled / earthRadius) * Math.Cos(direction));
    var newLongitude = oldLongitude + Math.Atan2(Math.Sin(direction) * Math.Sin(distanceTravelled / earthRadius) * Math.Cos(oldLatitude), Math.Cos(distanceTravelled / earthRadius) - Math.Sin(oldLatitude) * Math.Sin(newLatitude));

    //Convert From Radian to degree/Decimal
    newLatitude = 180 * newLatitude / Math.PI;
    newLongitude = 180 * newLongitude / Math.PI;

これは私が得た結果です --> 電話は動いていませんでした。ご覧のとおり、速度は 27.3263111114502 です。 したがって、速度の計算に何か問題がありますが、何が原因かわかりません。

ここに画像の説明を入力

答え:

センサーに基づいて位置を計算する解決策を見つけました。以下に回答を投稿しました。

ヘルプが必要な場合は、コメントを残してください

これは GPS と比較した結果です (注: GPS は赤色で表示されています) 。

ここに画像の説明を入力

4

6 に答える 6

9

あなたの何人かが言ったように、あなたは方程式を間違えましたが、それはエラーのほんの一部です.

  1. ニュートン - 非相対論的速度に対するダランベール物理学では、次のように規定されています。

    // init values
    double ax=0.0,ay=0.0,az=0.0; // acceleration [m/s^2]
    double vx=0.0,vy=0.0,vz=0.0; // velocity [m/s]
    double  x=0.0, y=0.0, z=0.0; // position [m]
    
    // iteration inside some timer (dt [seconds] period) ...
    ax,ay,az = accelerometer values
    vx+=ax*dt; // update speed via integration of acceleration
    vy+=ay*dt;
    vz+=az*dt;
     x+=vx*dt; // update position via integration of velocity
     y+=vy*dt;
     z+=vz*dt;
    
  2. センサーは回転できるため、方向を適用する必要があります。

    // init values
    double gx=0.0,gy=-9.81,gz=0.0; // [edit1] background gravity in map coordinate system [m/s^2]
    double ax=0.0,ay=0.0,az=0.0; // acceleration [m/s^2]
    double vx=0.0,vy=0.0,vz=0.0; // velocity [m/s]
    double  x=0.0, y=0.0, z=0.0; // position [m]
    double dev[9]; // actual device transform matrix ... local coordinate system
    (x,y,z) <- GPS position;
    
    // iteration inside some timer (dt [seconds] period) ...
    dev <- compass direction
    ax,ay,az = accelerometer values (measured in device space)
    (ax,ay,az) = dev*(ax,ay,az);  // transform acceleration from device space to global map space without any translation to preserve vector magnitude
    ax-=gx;    // [edit1] remove background gravity (in map coordinate system)
    ay-=gy;
    az-=gz;
    vx+=ax*dt; // update speed (in map coordinate system)
    vy+=ay*dt;
    vz+=az*dt;
     x+=vx*dt; // update position (in map coordinate system)
     y+=vy*dt;
     z+=vz*dt;
    
    • gx,gy,gzグローバル重力ベクトル (~9.81 m/s^2地球上)
    • コードでは、グローバルY軸が上を向いているのでgy=-9.81、残りは0.0
  3. 測定のタイミングが重要

    加速度計はできるだけ頻繁にチェックする必要があります (秒は非常に長い時間です)。精度を維持するために 10 ミリ秒を超えるタイマー期間を使用しないことをお勧めします。また、計算された位置を GPS 値でオーバーライドする必要がある場合もあります。コンパスの方向はあまり頻繁にチェックできませんが、適切なフィルタリングが必要です

  4. コンパスは常に正確ではありません

    コンパス値は、一部のピーク値に対してフィルター処理する必要があります。時々、悪い値を読み取ったり、電磁汚染や金属環境によってオフになったりすることがあります。その場合、移動中にGPSで方角を確認し、ある程度の修正を行うことができます。たとえば、GPS を毎分チェックし、GPS の方向をコンパスと比較して、一定の角度で常にずれている場合は、それを加算または減算します。

  5. なぜサーバー上で簡単な計算を行うのですか???

    オンラインでのトラフィックの浪費を嫌います。はい、サーバーにデータを記録できます(ただし、デバイス上のファイルの方が優れていると思います)が、なぜインターネット接続によって位置機能を制限するのですか??? 遅延は言うまでもありません...

【編集1】追記

上記のコードを少し編集しました。向きは、累積誤差を最小限に抑えるために、できる限り正確でなければなりません。

ジャイロはコンパスよりも優れています (または両方を使用する方が良いでしょう)。加速度はフィルタリングする必要があります。いくつかのローパス フィルタリングは問題ないはずです。重力を取り除いた後、ax、ay、az を使用可能な値に制限し、小さすぎる値を破棄します。低速に近い場合も完全に停止します(電車や真空中の動きでない場合)。これにより、ドリフトは減少しますが、他のエラーが増加するため、それらの間で妥協点を見つける必要があります。

その場でキャリブレーションを追加します。フィルタリングされacceleration = 9.81ているか、それに非常に近い場合、デバイスはおそらく静止しています(飛行機械でない限り)。向き・方向は、実際の重力方向によって補正できます。

于 2013-11-04T09:32:10.853 に答える
5

加速度センサーやジャイロは位置計算には向いていません。
数秒後、エラーが信じられないほど高くなります。(二重積分が問題であることはほとんど覚えていません)。
センサー フュージョンに関するこのGoogle テック トーク ビデオを見てください。彼はなぜこれが不可能なのかを非常に詳しく説明しています。

于 2013-11-06T18:04:52.653 に答える
3

センサーを使用して計算した位置を解決した後、将来誰かが必要になった場合に備えて、ここにコードを投稿したいと思います。

注: これは、Samsung Galaxy S2 携帯電話でのみ確認されており、人が携帯電話を持って歩いている場合のみであり、車や自転車で移動する場合はテストされていません。

GPSと比較してみた結果です(赤線はGPS、青線はセンサーで算出した位置)

GPSと比較してみた結果です(赤線はGPS、青線はセンサーで算出した位置)

コードはあまり効率的ではありませんが、このコードを共有することが誰かの助けになり、正しい方向に向けられることを願っています。

私は2つの別々のクラスを持っていました:

  1. 計算位置
  2. カスタムセンサーサービス

    public class CalculatePosition {

            static Double earthRadius = 6378D;
            static Double oldLatitude,oldLongitude;
            static Boolean IsFirst = true;
    
            static Double sensorLatitude, sensorLongitude;
    
            static Date CollaborationWithGPSTime;
            public static float[] results;
    
    
    
            public static void calculateNewPosition(Context applicationContext,
                    Float currentAcceleration, Float currentSpeed,
                    Float currentDistanceTravelled, Float currentDirection, Float TotalDistance) {
    
    
                results = new float[3];
                if(IsFirst){
                    CollaborationWithGPSTime = new Date();
                    Toast.makeText(applicationContext, "First", Toast.LENGTH_LONG).show();
                    oldLatitude = CustomLocationListener.mLatitude;
                    oldLongitude = CustomLocationListener.mLongitude;
                    sensorLatitude = oldLatitude;
                    sensorLongitude = oldLongitude;
                    LivePositionActivity.PlotNewPosition(oldLongitude,oldLatitude,currentDistanceTravelled * 1000, currentAcceleration, currentSpeed, currentDirection, "GPSSensor",0.0F,TotalDistance);
                    IsFirst  = false;
                    return;
                } 
    
                Date CurrentDateTime = new Date();
    
                if(CurrentDateTime.getTime() - CollaborationWithGPSTime.getTime() > 900000){
                    //This IF Statement is to Collaborate with GPS position --> For accuracy --> 900,000 == 15 minutes
                    oldLatitude = CustomLocationListener.mLatitude;
                    oldLongitude = CustomLocationListener.mLongitude;
                    LivePositionActivity.PlotNewPosition(oldLongitude,oldLatitude,currentDistanceTravelled * 1000, currentAcceleration, currentSpeed, currentDirection, "GPSSensor", 0.0F, 0.0F);
                    return;
                }
    
                //Convert Variables to Radian for the Formula
                oldLatitude = Math.PI * oldLatitude / 180;
                oldLongitude = Math.PI * oldLongitude / 180;
                currentDirection = (float) (Math.PI * currentDirection / 180.0);
    
                //Formulae to Calculate the NewLAtitude and NewLongtiude
                Double newLatitude = Math.asin(Math.sin(oldLatitude) * Math.cos(currentDistanceTravelled / earthRadius) + 
                        Math.cos(oldLatitude) * Math.sin(currentDistanceTravelled / earthRadius) * Math.cos(currentDirection));
                Double newLongitude = oldLongitude + Math.atan2(Math.sin(currentDirection) * Math.sin(currentDistanceTravelled / earthRadius)
                        * Math.cos(oldLatitude), Math.cos(currentDistanceTravelled / earthRadius)
                        - Math.sin(oldLatitude) * Math.sin(newLatitude));
    
                //Convert Back from radians
                newLatitude = 180 * newLatitude / Math.PI;
                newLongitude = 180 * newLongitude / Math.PI;
                currentDirection = (float) (180 * currentDirection / Math.PI);
    
                //Update old Latitude and Longitude
                oldLatitude = newLatitude;
                oldLongitude = newLongitude;
    
                sensorLatitude = oldLatitude;
                sensorLongitude = oldLongitude;
    
                IsFirst = false;
                //Plot Position on Map
                LivePositionActivity.PlotNewPosition(newLongitude,newLatitude,currentDistanceTravelled * 1000, currentAcceleration, currentSpeed, currentDirection, "Sensor", results[0],TotalDistance);
    
    
    
    
    
        }
    }
    

    public class CustomSensorService extends サービスの実装 SensorEventListener{

    static SensorManager sensorManager;
    static Sensor mAccelerometer;
    private Sensor mMagnetometer;
    private Sensor mLinearAccelertion;
    
    static Context mContext;
    
    private static float[] AccelerometerValue;
    private static float[] MagnetometerValue;
    
    public static  Float currentAcceleration = 0.0F;
    public static  Float  currentDirection = 0.0F;
    public static Float CurrentSpeed = 0.0F;
    public static Float CurrentDistanceTravelled = 0.0F;
    /*---------------------------------------------*/
    float[] prevValues,speed;
    float[] currentValues;
    float prevTime, currentTime, changeTime,distanceY,distanceX,distanceZ;
    float[] currentVelocity;
    public static CalculatePosition CalcPosition;
    /*-----FILTER VARIABLES-------------------------*-/
     * 
     * 
     */
    
    public static Float prevAcceleration = 0.0F;
    public static Float prevSpeed = 0.0F;
    public static Float prevDistance = 0.0F;
    
    public static Float totalDistance;
    
    TextView tv;
    Boolean First,FirstSensor = true;
    
    @Override
    public void onCreate(){
    
        super.onCreate();
        mContext = getApplicationContext();
        CalcPosition =  new CalculatePosition();
        First = FirstSensor = true;
        currentValues = new float[3];
        prevValues = new float[3];
        currentVelocity = new float[3];
        speed = new float[3];
        totalDistance = 0.0F;
        Toast.makeText(getApplicationContext(),"Service Created",Toast.LENGTH_SHORT).show();
    
        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
    
        mAccelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mMagnetometer = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        //mGyro = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
        mLinearAccelertion = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
    
        sensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
        sensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_NORMAL);
        //sensorManager.registerListener(this, mGyro, SensorManager.SENSOR_DELAY_NORMAL);
        sensorManager.registerListener(this, mLinearAccelertion, SensorManager.SENSOR_DELAY_NORMAL);
    
    }
    
    @Override
    public void onDestroy(){
        Toast.makeText(this, "Service Destroyed", Toast.LENGTH_SHORT).show();
        sensorManager.unregisterListener(this);
        //sensorManager = null;
        super.onDestroy();
    }
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // TODO Auto-generated method stub
    
    }
    
    @Override
    public void onSensorChanged(SensorEvent event) {
    
        float[] values = event.values;
        Sensor mSensor = event.sensor;
    
        if(mSensor.getType() == Sensor.TYPE_ACCELEROMETER){
            AccelerometerValue = values;
        }
    
        if(mSensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION){           
            if(First){
                prevValues = values;
                prevTime = event.timestamp / 1000000000;
                First = false;
                currentVelocity[0] = currentVelocity[1] = currentVelocity[2] = 0;
                distanceX = distanceY= distanceZ = 0;
            }
            else{
                currentTime = event.timestamp / 1000000000.0f;
    
                changeTime = currentTime - prevTime;
    
                prevTime = currentTime;
    
    
    
                calculateDistance(event.values, changeTime);
    
                currentAcceleration =  (float) Math.sqrt(event.values[0] * event.values[0] + event.values[1] * event.values[1] + event.values[2] * event.values[2]);
    
                CurrentSpeed = (float) Math.sqrt(speed[0] * speed[0] + speed[1] * speed[1] + speed[2] * speed[2]);
                CurrentDistanceTravelled = (float) Math.sqrt(distanceX *  distanceX + distanceY * distanceY +  distanceZ * distanceZ);
                CurrentDistanceTravelled = CurrentDistanceTravelled / 1000;
    
                if(FirstSensor){
                    prevAcceleration = currentAcceleration;
                    prevDistance = CurrentDistanceTravelled;
                    prevSpeed = CurrentSpeed;
                    FirstSensor = false;
                }
                prevValues = values;
    
            }
        }
    
        if(mSensor.getType() == Sensor.TYPE_MAGNETIC_FIELD){
            MagnetometerValue = values;
        }
    
        if(currentAcceleration != prevAcceleration || CurrentSpeed != prevSpeed || prevDistance != CurrentDistanceTravelled){
    
            if(!FirstSensor)
                totalDistance = totalDistance + CurrentDistanceTravelled * 1000;
            if (AccelerometerValue != null && MagnetometerValue != null && currentAcceleration != null) {
                //Direction
                float RT[] = new float[9];
                float I[] = new float[9];
                boolean success = SensorManager.getRotationMatrix(RT, I, AccelerometerValue,
                        MagnetometerValue);
                if (success) {
                    float orientation[] = new float[3];
                    SensorManager.getOrientation(RT, orientation);
                    float azimut = (float) Math.round(Math.toDegrees(orientation[0]));
                    currentDirection =(azimut+ 360) % 360;
                    if( CurrentSpeed > 0.2){
                        CalculatePosition.calculateNewPosition(getApplicationContext(),currentAcceleration,CurrentSpeed,CurrentDistanceTravelled,currentDirection,totalDistance);
                    }
                }
                prevAcceleration = currentAcceleration;
                prevSpeed = CurrentSpeed;
                prevDistance = CurrentDistanceTravelled;
            }
        }
    
    }
    
    
    @Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return null;
    }
    public void calculateDistance (float[] acceleration, float deltaTime) {
        float[] distance = new float[acceleration.length];
    
        for (int i = 0; i < acceleration.length; i++) {
            speed[i] = acceleration[i] * deltaTime;
            distance[i] = speed[i] * deltaTime + acceleration[i] * deltaTime * deltaTime / 2;
        }
        distanceX = distance[0];
        distanceY = distance[1];
        distanceZ = distance[2];
    }
    

    }

編集:

public static void PlotNewPosition(Double newLatitude, Double newLongitude, Float currentDistance, 
        Float currentAcceleration, Float currentSpeed, Float currentDirection, String dataType) {

    LatLng newPosition = new LatLng(newLongitude,newLatitude);

    if(dataType == "Sensor"){
        tvAcceleration.setText("Speed: " + currentSpeed + " Acceleration: " + currentAcceleration + " Distance: " + currentDistance +" Direction: " + currentDirection + " \n"); 
        map.addMarker(new MarkerOptions()
        .position(newPosition)
        .title("Position")
        .snippet("Sensor Position")
        .icon(BitmapDescriptorFactory
                .fromResource(R.drawable.line)));
    }else if(dataType == "GPSSensor"){
        map.addMarker(new MarkerOptions()
        .position(newPosition)
        .title("PositionCollaborated")
        .snippet("GPS Position"));
    }
    else{
        map.addMarker(new MarkerOptions()
        .position(newPosition)
        .title("Position")
        .snippet("New Position")
        .icon(BitmapDescriptorFactory
                .fromResource(R.drawable.linered)));
    }
    map.moveCamera(CameraUpdateFactory.newLatLngZoom(newPosition, 18));
}
于 2014-05-19T21:52:19.703 に答える
0

よくわかりませんが、私の最善の推測は、この部分の周りです。

Double initialVelocity = prevLocation.Speed;
var t = secondsTravelling.TotalSeconds;
var finalvelocity = initialVelocity + Double.Parse(currentAcceleration) * t;

prevLocation で速度が 27.326... で、t==0 および currentAcceleration ==0 (アイドル状態であると言ったように) であるとします。

var finalvelocity = 27.326 + 0*0;
var finalvelocity == 27.326

finalvelocity が currentlocation の速度になる場合、previouslocation = currentlocation となります。これは、最終速度が下がらない可能性があることを意味します。しかし、繰り返しになりますが、ここにはかなりの仮定があります。

于 2013-11-01T13:37:22.960 に答える
-2

あなたは自分自身にそれを難し​​くしているようです。Google Play Service Location APIを使用するだけで、位置、方向、速度などに正確に簡単にアクセスできるはずです。

サーバー側で作業する代わりに、それを使用することを検討します。

于 2013-11-01T13:06:25.250 に答える