最近在做室内定位相关研究,希望通过手机传感器数据判断人的姿态和手机位置。之前很多研究只用到了加速度数据,但是判别的准确度偏低。本次我们的研究准备将加速度传感器、角速度传感器、方位传感器、压力传感器等数据结合起来,用于提高判别的准确度。
具体如何采集这些数据,网上并没有相关代码。在这里把自己的实现方法分享出来,供大家参考。
首先是建立加速度的类AccData(本文只涉及传感器数据采集相关代码,AccData类中从数据库获取数据的代码已经略去):

/**
 * Created by Maolin Liu on 2016/3/12.
 */
public class AccData {
    public long timestamp;
    public double ax;
    public double ay;
    public double az;

    public static final String TABLE_NAME = "AccData";
    public static final String _ID = "_id";
    public static final String TIMESTAMP = "timestamp";
    public static final String AX = "ax";
    public static final String AY = "ay";
    public static final String AZ = "az";

    //生成表的sql语句
    public static String tableCreateSQL() {
        StringBuffer sql = new StringBuffer();
        sql.append("create table ");
        sql.append(TABLE_NAME);
        sql.append(" (");
        sql.append(_ID);
        sql.append(" integer primary key autoincrement,");
        sql.append(TIMESTAMP);
        sql.append(" long,");
        sql.append(AX);
        sql.append(" double,");
        sql.append(AY);
        sql.append(" double,");
        sql.append(AZ);
        sql.append(" double");
        sql.append(" )");
        return sql.toString();
    }

    public long insertDataBase(SQLiteDatabase sqLiteDatabase) {
        ContentValues values = new ContentValues();
        values.put(TIMESTAMP, timestamp);
        values.put(AX, ax);
        values.put(AY, ay);
        values.put(AZ, az);
        return(sqLiteDatabase.insert(TABLE_NAME, null, values));
    }

    public AccData clone() {
        AccData accData = new AccData();
        accData.timestamp = timestamp;
        accData.ax = ax;
        accData.ay = ay;
        accData.az = az;
        return accData;
    }
}

然后是加速度的SQLiteOpenHelper类,即AccDBOpenHelper:

/**
 * Created by Maolin Liu on 2016/3/12.
 */
public class AccDBOpenHelper extends SQLiteOpenHelper {
    private static final String DBNAME = "Acc Data.db3";
    private static final int VERSION = 1;
    public AccDBOpenHelper(Context context){
        super(context,DBNAME,null,VERSION);
    }

    public AccDBOpenHelper(Context context, String name,SQLiteDatabase.CursorFactory factory, int version){
        super(context, name, factory, version);
        // TODO Auto-generated constructor stub
    }

    @Override
    public void onCreate(SQLiteDatabase db){
        db.execSQL(AccData.tableCreateSQL());
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
        System.out.println( "----------AccDBOpenHelper onUpdata Called----------" + oldVersion + "-->" + newVersion);
    }
}

按照和上面类似的方法为角速度数据、方位数据、压力数据建立数据类和对应的SQLiteOpenHelper类。
下面是具体的实现了。如果将采集数据的的代码放在Activity中,用户体验不是特别好。所以我们在后台Service中进行数据采集。需要注意几个地方,第一,往数据库中存储大量数据的时候最好添加事务,这样会提高存储效率;第二,方向数据要通过计算获得,而不是通过传感器直接获取,如果需要用到azimuth、pitch和roll数据需要特别注意这一点(详情点击:Android Orientation Sensor(方向传感器)详解与应用)。第三,API等级在19以上,可以自由设置传感器的采样频率,注意单位是微秒(10 -6秒)。
下面是Service具体代码:

/**
 * Created by Maolin Liu on 2016/3/12.  
 */
public class DataAcquireService extends Service {
    private SensorManager sensorManager;
    private MySensorBinder mySensorBinder;
    private Timer updateTimer;

    private AccDBOpenHelper accDBOpenHelper;
    private SQLiteDatabase accDB;
    private List<AccData> accDataList;
    private AccData accData;
    private float accValues[];

    private GyrDBOpenHelper gyrDBOpenHelper;
    private SQLiteDatabase gyrDB;
    private List<GyrData> gyrDataList;
    private GyrData gyrData;
    private float gyrValues[];

    private MagAndOriDBOpenHelper magAndOriDBOpenHelper;
    private SQLiteDatabase magAndOriDB;
    private List<MagAndOriData> magAndOriDataList;
    private MagAndOriData magAndOriData;

    private PreDBOpenHelper preDBOpenHelper;
    private SQLiteDatabase preDB;
    private List<PreData> preDataList;
    private PreData preData;
    private float preValue;

    private float magValues[];

    private boolean isRecord ;             //开始存储或者关闭存储的Boolean值
    public static final String ACTION_STATE_COLLECT_SERVICE = "edu.geosis.service.DATA_ACQUIRE_SERVICE";

    @Override
    public IBinder onBind(Intent intent) {
        System.out.println("DataAcquireService is binded ! ");
        return mySensorBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        initializeVariables();             
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("DataAcquireService is started ! ");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        if (accDB != null && accDB.isOpen()) {
            accDB.close();
        }
        if (gyrDB != null && gyrDB.isOpen()) {
            gyrDB.close();
        }
        if (magAndOriDB != null && magAndOriDB.isOpen()) {
            magAndOriDB.close();
        }
        if (preDB != null && preDB.isOpen()) {
            preDB.close();
        }

        sensorManager.unregisterListener(mySensorListener);      
        System.out.println("DataAcquireService is destroyed ! ");
        super.onDestroy();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        System.out.println("DataAcquireService is onUnbinded ! ");
        return super.onUnbind(intent);
    }

    @Override
    public void onRebind(Intent intent) {
        System.out.println("DataAcquireService is onRebinded ! ");
        super.onRebind(intent);
    }

    private void initializeVariables() {

        mySensorBinder = new MySensorBinder();
        sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);


        accDBOpenHelper = new AccDBOpenHelper(this, "Acc Data.db3", null, 1);       //存储在默认路径下
        accDB = accDBOpenHelper.getReadableDatabase();
        accDataList = new ArrayList<AccData>();
        accData = new AccData();

        gyrDBOpenHelper = new GyrDBOpenHelper(this, "Gyr Data.db3", null, 1);
        gyrDB = gyrDBOpenHelper.getReadableDatabase();
        gyrDataList = new ArrayList<GyrData>();
        gyrData = new GyrData();

        magAndOriDBOpenHelper = new MagAndOriDBOpenHelper(this, "MagAndOri Data.db3", null, 1);     
        magAndOriDB = magAndOriDBOpenHelper.getReadableDatabase();
        magAndOriDataList = new ArrayList<MagAndOriData>();
        magAndOriData = new MagAndOriData();

        preDBOpenHelper = new PreDBOpenHelper(this, "Pre Data.db3", null, 1);
        preDB = preDBOpenHelper.getReadableDatabase();
        preDataList = new ArrayList<PreData>();
        preData = new PreData();

        isRecord = false;
    }

    /**
     * Service对应的Activity通过下面的Binder类对采集数据的过程进行控制
     **/
    public class MySensorBinder extends Binder {

        public void startRecord() {
            isRecord = true;
        }

        public void stopRecord() {
            isRecord = false;
        }

        public void clearRecord() {
            accDataList.clear();
            gyrDataList.clear();
            magAndOriDataList.clear();
            preDataList.clear();
        }

        public boolean insertDB() {
            //make clear whether it is "||" or "&&"
            if (accDataList.isEmpty() && gyrDataList.isEmpty() && preDataList.isEmpty()&& magAndOriDataList.isEmpty()) {
                return false;
            } else {
                //存储时开启事务能大大加快存储速度,否则一条一条地存储特别费时
                accDB.beginTransaction();
                try {
                    for (AccData accData: accDataList) {
                        accData.insertDataBase(accDB);
                    }
                    accDB.setTransactionSuccessful();
                }
                catch(Exception e) {
                    e.printStackTrace();
                    System.out.println("accData cannot insert into the database !");
                }
                finally {
                    accDB.endTransaction();
                }

                gyrDB.beginTransaction();
                try {
                    for (GyrData gyrData: gyrDataList) {
                        gyrData.insertDataBase(gyrDB);
                    }
                    gyrDB.setTransactionSuccessful();
                }
                catch(Exception e) {
                    e.printStackTrace();
                }
                finally {
                    gyrDB.endTransaction();
                }

                magAndOriDB.beginTransaction();
                try {
                    for (MagAndOriData magAndOriData: magAndOriDataList) {
                        magAndOriData.insertDataBase(magAndOriDB);
                    }
                    magAndOriDB.setTransactionSuccessful();
                }
                catch(Exception e) {
                    e.printStackTrace();
                }
                finally {
                    magAndOriDB.endTransaction();
                }

                preDB.beginTransaction();
                try {
                    for (PreData preData: preDataList) {
                        preData.insertDataBase(preDB);
                    }
                    preDB.setTransactionSuccessful();
                }
                catch(Exception e) {
                    e.printStackTrace();
                }
                finally {
                    preDB.endTransaction();
                }

                return true;
            }
        }

        public void registerListener() {
            boolean result =   sensorManager.registerListener(mySensorListener,
                    sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                    SensorManager.SENSOR_DELAY_GAME);                              //最后一个参数用于控制传感器数据获取的频率,频率可以自由调整
            Log.d("maolin",""+result);   //LOG查看监听是否成功注册

            sensorManager.registerListener(mySensorListener,
                    sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE),
                    SensorManager.SENSOR_DELAY_GAME);

            sensorManager.registerListener(mySensorListener,
                    sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
                    SensorManager.SENSOR_DELAY_GAME);

            sensorManager.registerListener(mySensorListener,
                    sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE),
                    SensorManager.SENSOR_DELAY_GAME);


        }
        public void unregisterListener() {
            sensorManager.unregisterListener(mySensorListener);
            System.out.println("StateCollectService listener is unregistered ! ");
        }

    }

    //下面是传感器变化监听的关键类
    private SensorEventListener mySensorListener = new SensorEventListener() {
        @Override
        public void onSensorChanged(SensorEvent event) {
            switch(event.sensor.getType()) {
                case Sensor.TYPE_ACCELEROMETER:
                    accValues = event.values.clone();
                    synchronized (accData) {
                        accData.timestamp = System.currentTimeMillis();
                        accData.ax = accValues[0];
                        accData.ay = accValues[1];
                        accData.az = accValues[2];

                        if (isRecord) {
                            accDataList.add(accData.clone());
                        }
                    }
                    break;
                case Sensor.TYPE_GYROSCOPE:
                    gyrValues = event.values.clone();
                    synchronized (gyrData) {
                        gyrData.timestamp = System.currentTimeMillis();
                        gyrData.gx = gyrValues[0];
                        gyrData.gy = gyrValues[1];
                        gyrData.gz = gyrValues[2];

                        if (isRecord) {
                            gyrDataList.add(gyrData.clone());
                        }
                    }
                    break;
                case Sensor.TYPE_PRESSURE:
                    preValue = event.values[0];
                    synchronized (preData) {
                        preData.timestamp = System.currentTimeMillis();
                        preData.pressure = preValue;

                        if (isRecord) {
                            preDataList.add(preData.clone());
                        }
                    }
                    break;
                case Sensor.TYPE_MAGNETIC_FIELD:               //加速度、陀螺仪和气压计都可以直接获得,但是磁力计和方位角数据要通过计算才能得到
                    magValues = event.values;
                    break;
            }
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int i) {
        }
    };
}

然后就是操作界面DataAcquireActivity :

/**
 * Created by GeoSIS on 2016/3/12.
 */
public class DataAcquireActivity extends Activity {

    private Chronometer chronometer = null;    //添加一个计时器,知道采集的时长;
    private Button btnStart,btnEnd;

    private ServiceConnection dataAcquireConnection;
    private DataAcquireService.MySensorBinder mySensorBinder;
    private ArrayList<Long> savedTime;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_data_acquire);

        initializeVariables();
        initializeButton();
    }

    @Override
    protected void onStart() {
        super.onStart();
    }

    @Override
    protected void onRestart() {
        super.onRestart();
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

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

    @Override
    protected void onStop() {
        super.onStop();
    }

    @Override
    protected void onDestroy() {

        unbindService(dataAcquireConnection);
        super.onDestroy();
    }

    private void initializeVariables() {
        // TODO Auto-generated method stub

        chronometer = (Chronometer)findViewById(R.id.chronometer);
        btnStart    = (Button)findViewById(R.id.start_pick_up);
        btnEnd      = (Button)findViewById(R.id.end_pick_up);

    }

    private void initializeButton() {
        // TODO Auto-generated method stub
        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                chronometer.setBase(SystemClock.elapsedRealtime());
                chronometer.start();

                mySensorBinder.registerListener();
                mySensorBinder.startRecord();

                btnStart.setClickable(false);                                                 btnStart.setBackgroundColor(getResources().getColor(R.color.yellow));
            }
        });
        btnEnd.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                chronometer.stop();

                mySensorBinder.stopRecord();
                mySensorBinder.insertDB();
                mySensorBinder.clearRecord();
                mySensorBinder.unregisterListener();

               finish();
            }
        });
    }
}

Activity对应的XML文件非常简单,上面只有一个计时器(Chronometer)、一个开始采集的按钮(Button)和一个结束采集的按钮(Button),这里就不列出来了。