Android-傳感器-計步

參考文章:http://www.lxweimin.com/p/5d57f7fd84fa
本文對原文計步項目進(jìn)行了精簡,移除了進(jìn)程服務(wù)和計時、守護(hù)進(jìn)程、數(shù)據(jù)庫保存等等,方便擴(kuò)展功能。

本文源碼:https://github.com/lioilwin/StepOrient
Android4.4以上版本,有些手機有計步傳感器可以直接使用,
而有些手機沒有,但有加速度傳感器,也可以實現(xiàn)計步功能(需要計算加速度波峰波谷來判斷人走一步)!

image

一.調(diào)用


public class MainActivity extends AppCompatActivity implements StepSensorBase.StepCallBack{
    .........
    @Override
    public void Step(int stepNum) {
        //  計步回調(diào)
        stepText.setText("步數(shù):" + stepNum);
    }
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);     
        setContentView(R.layout.activity_main);
        stepText = (TextView) findViewById(R.id.step_text);
        
        // 開啟計步監(jiān)聽, 分為加速度傳感器、或計步傳感器
        stepSensor = new StepSensorPedometer(this, this);
        if (!stepSensor.registerStep()) {
            Toast.makeText(this, "計步傳傳感器不可用!", Toast.LENGTH_SHORT).show();
            stepSensor = new StepSensorAcceleration(this, this);
            if (!stepSensor.registerStep()) {
                Toast.makeText(this, "加速度傳感器不可用!", Toast.LENGTH_SHORT).show();
            }
        }
    }
    .......
 }
 
 /**
 * 計步傳感器抽象類,子類分為加速度傳感器、或計步傳感器
 */
public abstract class StepSensorBase implements SensorEventListener {
    private Context context;
    protected StepCallBack stepCallBack;
    protected SensorManager sensorManager;
    protected static int CURRENT_SETP = 0;
    protected boolean isAvailable = false;

    public StepSensorBase(Context context, StepCallBack stepCallBack) {
        this.context = context;
        this.stepCallBack = stepCallBack;
    }

    public interface StepCallBack {
        /**
         * 計步回調(diào)
         */
        void Step(int stepNum);
    }

    /**
     * 開啟計步
     */
    public boolean registerStep() {
        if (sensorManager != null) {
            sensorManager.unregisterListener(this);
            sensorManager = null;
        }
        sensorManager = SensorUtil.getInstance().getSensorManager(context);
        registerStepListener();
        return isAvailable;
    }

    /**
     * 注冊計步監(jiān)聽器
     */
    protected abstract void registerStepListener();

    /**
     * 注銷計步監(jiān)聽器
     */
    public abstract void unregisterStep();
}
 

二.直接使用計步傳感器實現(xiàn)計步


/**
 * 計步傳感器
 */
public class StepSensorPedometer extends StepSensorBase {
    private final String TAG = "StepSensorPedometer";
    private int lastStep = -1;
    private int liveStep = 0;
    private int increment = 0;
    private int sensorMode = 0; // 計步傳感器類型

    public StepSensorPedometer(Context context, StepCallBack stepCallBack) {
        super(context, stepCallBack);
    }

    @Override
    protected void registerStepListener() {
        Sensor detectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);
        Sensor countSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
        if (sensorManager.registerListener(this, detectorSensor, SensorManager.SENSOR_DELAY_GAME)) {
            isAvailable = true;
            sensorMode = 0;
            Log.i(TAG, "計步傳感器Detector可用!");
        } else if (sensorManager.registerListener(this, countSensor, SensorManager.SENSOR_DELAY_GAME)) {
            isAvailable = true;
            sensorMode = 1;
            Log.i(TAG, "計步傳感器Counter可用!");
        } else {
            isAvailable = false;
            Log.i(TAG, "計步傳感器不可用!");
        }
    }

    @Override
    public void unregisterStep() {
        sensorManager.unregisterListener(this);
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        liveStep = (int) event.values[0];
        if (sensorMode == 0) {
            Log.i(TAG, "Detector步數(shù):"+liveStep);
            StepSensorBase.CURRENT_SETP += liveStep;
        } else if (sensorMode == 1) {
            Log.i(TAG, "Counter步數(shù):"+liveStep);
            StepSensorBase.CURRENT_SETP = liveStep;
        }
        stepCallBack.Step(StepSensorBase.CURRENT_SETP);
    }

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

三.使用加速度傳感器實現(xiàn)計步


/**
 * 加速度傳感器
 */
public class StepSensorAcceleration extends StepSensorBase {
    private final String TAG = "StepSensorAcceleration";
    //存放三軸數(shù)據(jù)
    final int valueNum = 5;
    //用于存放計算閾值的波峰波谷差值
    float[] tempValue = new float[valueNum];
    int tempCount = 0;
    //是否上升的標(biāo)志位
    boolean isDirectionUp = false;
    //持續(xù)上升次數(shù)
    int continueUpCount = 0;
    //上一點的持續(xù)上升的次數(shù),為了記錄波峰的上升次數(shù)
    int continueUpFormerCount = 0;
    //上一點的狀態(tài),上升還是下降
    boolean lastStatus = false;
    //波峰值
    float peakOfWave = 0;
    //波谷值
    float valleyOfWave = 0;
    //此次波峰的時間
    long timeOfThisPeak = 0;
    //上次波峰的時間
    long timeOfLastPeak = 0;
    //當(dāng)前的時間
    long timeOfNow = 0;
    //當(dāng)前傳感器的值
    float gravityNew = 0;
    //上次傳感器的值
    float gravityOld = 0;
    //動態(tài)閾值需要動態(tài)的數(shù)據(jù),這個值用于這些動態(tài)數(shù)據(jù)的閾值
    final float initialValue = (float) 1.7;
    //初始閾值
    float ThreadValue = (float) 2.0;

    //初始范圍
    float minValue = 11f;
    float maxValue = 19.6f;

    /**
     * 0-準(zhǔn)備計時   1-計時中  2-正常計步中
     */
    private int CountTimeState = 0;
    public static int TEMP_STEP = 0;
    private int lastStep = -1;
    //用x、y、z軸三個維度算出的平均值
    public static float average = 0;
    private Timer timer;
    // 倒計時3.5秒,3.5秒內(nèi)不會顯示計步,用于屏蔽細(xì)微波動
    private long duration = 3500;
    private TimeCount time;

    public StepSensorAcceleration(Context context, StepCallBack stepCallBack) {
        super(context, stepCallBack);
    }

    @Override
    protected void registerStepListener() {
        // 注冊加速度傳感器
        isAvailable = sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                SensorManager.SENSOR_DELAY_GAME);
        if (isAvailable) {
            Log.i(TAG, "加速度傳感器可用!");
        } else {
            Log.i(TAG, "加速度傳感器不可用!");
        }
    }

    @Override
    public void unregisterStep() {
        sensorManager.unregisterListener(this);
    }

    public void onAccuracyChanged(Sensor arg0, int arg1) {
    }

    public void onSensorChanged(SensorEvent event) {
        Sensor sensor = event.sensor;
        synchronized (this) {
            if (sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
                calc_step(event);
            }
        }
    }

    synchronized private void calc_step(SensorEvent event) {
        average = (float) Math.sqrt(Math.pow(event.values[0], 2)
                + Math.pow(event.values[1], 2) + Math.pow(event.values[2], 2));
        detectorNewStep(average);
    }

    /*
     * 檢測步子,并開始計步
     * 1.傳入sersor中的數(shù)據(jù)
     * 2.如果檢測到了波峰,并且符合時間差以及閾值的條件,則判定為1步
     * 3.符合時間差條件,波峰波谷差值大于initialValue,則將該差值納入閾值的計算中
     * */
    public void detectorNewStep(float values) {
        if (gravityOld == 0) {
            gravityOld = values;
        } else {
            if (DetectorPeak(values, gravityOld)) {
                timeOfLastPeak = timeOfThisPeak;
                timeOfNow = System.currentTimeMillis();

                if (timeOfNow - timeOfLastPeak >= 200
                        && (peakOfWave - valleyOfWave >= ThreadValue) && (timeOfNow - timeOfLastPeak) <= 2000) {
                    timeOfThisPeak = timeOfNow;
                    //更新界面的處理,不涉及到算法
                    preStep();
                }
                if (timeOfNow - timeOfLastPeak >= 200
                        && (peakOfWave - valleyOfWave >= initialValue)) {
                    timeOfThisPeak = timeOfNow;
                    ThreadValue = Peak_Valley_Thread(peakOfWave - valleyOfWave);
                }
            }
        }
        gravityOld = values;
    }

    private void preStep() {
//        if (CountTimeState == 0) {
//            // 開啟計時器
//            time = new TimeCount(duration, 700);
//            time.start();
//            CountTimeState = 1;
//            Log.v(TAG, "開啟計時器");
//        } else if (CountTimeState == 1) {
//            TEMP_STEP++;
//            Log.v(TAG, "計步中 TEMP_STEP:" + TEMP_STEP);
//        } else if (CountTimeState == 2) {
        StepSensorBase.CURRENT_SETP++;
//            if (stepCallBack != null) {
        stepCallBack.Step(StepSensorBase.CURRENT_SETP);
//            }
//        }

    }


    /*
     * 檢測波峰
     * 以下四個條件判斷為波峰:
     * 1.目前點為下降的趨勢:isDirectionUp為false
     * 2.之前的點為上升的趨勢:lastStatus為true
     * 3.到波峰為止,持續(xù)上升大于等于2次
     * 4.波峰值大于1.2g,小于2g
     * 記錄波谷值
     * 1.觀察波形圖,可以發(fā)現(xiàn)在出現(xiàn)步子的地方,波谷的下一個就是波峰,有比較明顯的特征以及差值
     * 2.所以要記錄每次的波谷值,為了和下次的波峰做對比
     * */
    public boolean DetectorPeak(float newValue, float oldValue) {
        lastStatus = isDirectionUp;
        if (newValue >= oldValue) {
            isDirectionUp = true;
            continueUpCount++;
        } else {
            continueUpFormerCount = continueUpCount;
            continueUpCount = 0;
            isDirectionUp = false;
        }

//        Log.v(TAG, "oldValue:" + oldValue);
        if (!isDirectionUp && lastStatus
                && (continueUpFormerCount >= 2 && (oldValue >= minValue && oldValue < maxValue))) {
            peakOfWave = oldValue;
            return true;
        } else if (!lastStatus && isDirectionUp) {
            valleyOfWave = oldValue;
            return false;
        } else {
            return false;
        }
    }

    /*
     * 閾值的計算
     * 1.通過波峰波谷的差值計算閾值
     * 2.記錄4個值,存入tempValue[]數(shù)組中
     * 3.在將數(shù)組傳入函數(shù)averageValue中計算閾值
     * */
    public float Peak_Valley_Thread(float value) {
        float tempThread = ThreadValue;
        if (tempCount < valueNum) {
            tempValue[tempCount] = value;
            tempCount++;
        } else {
            tempThread = averageValue(tempValue, valueNum);
            for (int i = 1; i < valueNum; i++) {
                tempValue[i - 1] = tempValue[i];
            }
            tempValue[valueNum - 1] = value;
        }
        return tempThread;

    }

    /*
     * 梯度化閾值
     * 1.計算數(shù)組的均值
     * 2.通過均值將閾值梯度化在一個范圍里
     * */
    public float averageValue(float value[], int n) {
        float ave = 0;
        for (int i = 0; i < n; i++) {
            ave += value[i];
        }
        ave = ave / valueNum;
        if (ave >= 8) {
//            Log.v(TAG, "超過8");
            ave = (float) 4.3;
        } else if (ave >= 7 && ave < 8) {
//            Log.v(TAG, "7-8");
            ave = (float) 3.3;
        } else if (ave >= 4 && ave < 7) {
//            Log.v(TAG, "4-7");
            ave = (float) 2.3;
        } else if (ave >= 3 && ave < 4) {
//            Log.v(TAG, "3-4");
            ave = (float) 2.0;
        } else {
//            Log.v(TAG, "else");
            ave = (float) 1.7;
        }
        return ave;
    }

    class TimeCount extends CountDownTimer {
        public TimeCount(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
        }

        @Override
        public void onFinish() {
            // 如果計時器正常結(jié)束,則開始計步
            time.cancel();
            StepSensorBase.CURRENT_SETP += TEMP_STEP;
            lastStep = -1;
            Log.v(TAG, "計時正常結(jié)束");

            timer = new Timer(true);
            TimerTask task = new TimerTask() {
                public void run() {
                    if (lastStep == StepSensorBase.CURRENT_SETP) {
                        timer.cancel();
                        CountTimeState = 0;
                        lastStep = -1;
                        TEMP_STEP = 0;
                        Log.v(TAG, "停止計步:" + StepSensorBase.CURRENT_SETP);
                    } else {
                        lastStep = StepSensorBase.CURRENT_SETP;
                    }
                }
            };
            timer.schedule(task, 0, 2000);
            CountTimeState = 2;
        }

        @Override
        public void onTick(long millisUntilFinished) {
            if (lastStep == TEMP_STEP) {
                Log.v(TAG, "onTick 計時停止:" + TEMP_STEP);
                time.cancel();
                CountTimeState = 0;
                lastStep = -1;
                TEMP_STEP = 0;
            } else {
                lastStep = TEMP_STEP;
            }
        }
    }
}

簡書: http://www.lxweimin.com/p/6b05b1b8603e
CSDN博客: http://blog.csdn.net/qq_32115439/article/details/61619644
GitHub博客:http://lioil.win/2017/03/12/Android-Step.html
Coding博客:http://c.lioil.win/2017/03/12/Android-Step.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,807評論 25 708
  • 最近在項目中研究計步模塊,主要功能記錄當(dāng)天步數(shù),類似微信運動,支付寶計步,咕咚今日步數(shù)。開發(fā)之前的調(diào)研工作,搜遍b...
    碼農(nóng)一顆顆閱讀 32,389評論 37 111
  • 本篇文章是計步模塊第一版V1.0.0,在運行一段時間發(fā)現(xiàn)用戶投訴很多,計步有時不準(zhǔn)確。 對計步模塊進(jìn)行優(yōu)化V2.0...
    碼農(nóng)一顆顆閱讀 22,694評論 11 52
  • Android傳感器定義 Android 傳感器相關(guān)術(shù)語微機電傳感器(MEMS)MEMS 通常制作在規(guī)格很小的硅芯...
    Jannonx閱讀 4,407評論 0 1
  • 1,在這個世界上,除了家人,沒有任何人能包容你所有的壞脾氣和無理取鬧。沒有任何人能無條件對你好。為什么要把最壞的脾...
    x小熊熊x閱讀 320評論 0 3