一、介绍

基于计步器的室内定位系统,计步准确是非常关键的一项指标,有必要提高其准确性和可靠性。现有的检测技术,不论是硬件还是软件,都不能满足高精度的定位系统,尤其是在缓慢步行的情况下。特别是在一个陌生的环境,或者使用者是视力受损的人群,室内导航表现的更糟糕(译者注:就是说室内导航软件,用常规方法根本检测不到用户移动,更不提导航作用了)。

现有的大多数的计步器使用加速度数据,基于阈值来检测步伐。

本文讨论的计步器算法是基于使用陀螺仪,在室内定位中识别出人类步行状态。本算法是由Abhayasinghe和Murray提出。本研究作为一个室内导航系统的一部分,针对的人群是视力受损的人群(译者注:文章的3位作者可能是针对视力受损的人做室内导航软件的)。

现有的一些计步器的性能会在“背景”一节所讨论,而基于陀螺仪的计步器算法及其性能将在“步检测算法”部分和“实验结果”部分体现。

二、背景


Jerome 和Albright 测试了5款商业计步器,测试对象是13名视力受损的成年人和10位老人,结果发现这些商业计步器检测精度非常差,在平地行走精度只有41%−67%,而上下楼梯的情况更糟,上楼梯为9%−28%,下楼梯为11−41%。Crouter  等人测试了10款商用电子计步器,发现这些计步器在缓慢行走时,检测的步数都是偏少的。Garcia 等人对比了软件计步器和硬件的计步器,观察这两种类型计步器在所有速度下表现都差不多,在缓慢步行速度下(58 ~ 98步/分钟),硬件计步器精度只有20.5%±30%,软件计步器精度更是只有10%±30%。


Waqar等人曾在一个室内定位系统,使用了基于重力加速度计和固定阈值算法的计步器作为测试。他们的统计数据显示,在6次40步的测试中,平均准确率为86.67%,其中最低的一次准确率为82.5%,最高一次的准确率为95%,取中位数精度为85%。


Oner 等人讨论也提出了基于加速度计的智能手机计步器算法 :当步行的速度超过90bps(beats per second 每秒90拍,乐曲的节拍,音乐节奏速度单位)的时候,其算法显示出足够的精度。但当速度低于90bps时,其精度就会下降。在步行速度为80bps时,其算法误差大约是20%;步行速度在70bps时,误差为60%,步行速度在60bps时误差达到90%。


lim等人提出了在脚上安装陀螺仪计步器,但是lim等人没有提到他们系统的准确性。那不如让他们使用力敏电阻器(
force sensitive resisters ,FSR )接触脚趾和脚后跟,这样精度肯定会更高,因为使用力敏电阻接触身体更容易地检测到结果(译者注:论文作者开始吐槽了)。
Ayabe等人的统计了一些商用计步器,在爬楼梯和踏凳练习(译者注:应该是站立,然后单脚放在凳上,之后左右换脚踏凳)中,速度80到120步/分钟的时候,误差为±5%。然而,速度越低,精度也越低(> 40±40%步/分钟)。

 

这里讨论的大多数例子都是使用重力加速度去检测步数。但是,他们都有一个相同的问题,在缓慢的行走速度中表现不佳。低速表现不佳的主要原因是:低速行走时,重力加速度几乎为固定值,而且加速度计反应迟缓,再加上这些算法不能采用分级的阈值(译者注:这里应该是想引出动态阈值概念)以适应走路的步伐,这就引出了一个要求在缓慢行走时也能计算准确的计步检测算法。

三、计步算法

A、介绍

论文[6]提到,陀螺仪数据可以用在室内定位软件里的计步算法中。该文作者提出,把手机放在裤袋里,通过手机陀螺仪的单值数据(陀螺仪数据有3个值,分别为x,y,z轴数据)就可以追踪大腿的运动,从而进行计步检测。

B、陀螺仪数据与大腿运动之间的关系

一个步态周期的测量方法是从迈出一步脚后跟触地,到相同的脚后跟下一步的接触。在迈出一步触地时,大腿向前迈出的距离是最大的,比其他方向的距离都要大。图1显示了使用陀螺仪x轴数据去记录大腿的运动波形,该图形已经使用了低通滤波降噪(使用的是6阶巴特沃斯低通滤波,截止频率微5Hz)。触地点和步态周期都在图1进行了标记。当大腿静止时,通过融合加速度计和罗盘数据,可以计算出初始位置。对于这种状态的计算,陀螺仪的静态数值将会被计算平均值所去除。

可以清楚的看出,装有手机的那一条腿在触地时,在经过滤波后的陀螺仪读数接近于0的,而且斜率是负。

因此,滤波后的陀螺仪读数从一个负斜率通过零点再上穿零点为一个步态周期。

android 开源计步demo android计步器原理_陀螺仪

图1 走在平坦地面,大腿运动方向图形(实线),经过滤波的陀螺仪x轴图形(虚线)

同时也观察到当走楼梯和走斜坡时候,脚触地的图形和陀螺仪滤波后负斜率过零点的图形相符。显而易见,经过滤波后的陀螺仪数据结合过零点检测法,就可以检测出步态周期,即使是走楼梯或走斜面。以上所有的结论,都是基于手机垂直地放着大腿裤袋里所得出的,包括脚的运动波形和陀螺仪x轴波形。因此,实际上只要处理陀螺仪x轴即可。

C、数据预处理

陀螺仪x轴数据使用截止频率为3Hz的六阶巴特沃斯低通滤波器来滤波降噪,再使用过零点检测法去检测步数。之所以选择3Hz截止频率,是因为人类快走时平均范围是每秒2.5步[10]。截止频率越低,对平滑波形效果越好,把在零点附近的毛刺过滤到最小,又能清晰地显示步态周期的波形。

D、过零点检测

使用简单的波形经过2个零点的检测法,可以简化算法。一只脚向前走或者并拢,形成的波形都能通过正、负2个零点。因此,过零点的总数,就是人走的步数。

E、规避误差

如图1的红圈所示,从一段脚触地到离开,过滤后的陀螺仪信号以一个负斜率过零,但仍可能在短时间内以正斜率过零。这就会造成误差。然而,因为这一小段只是在一个步态周期占到0-10%时间,完全可以用一个超时机制来避免这种不必要的过零点。一旦一个过零点被成功确认,那么,过零检测器就设认为100毫秒内,不允许有下一个过零点,这样就避免了多余的零点。为什么选用100毫秒?首先,选用前15%的步态周期时间,足够避免前10%的过零点引入误差。而一个缓慢的步态周期是每秒1.5步,因此15%时间约为100毫秒。100ms这个时间比在每秒3步这种快速的步态周期延迟了30%,因此它是不会干扰到快速行走情况下的过零检测。

F、验证过零检测

在步伐检测中,我们在每一次的过零检测之后,还要使用一种阈值去验证是否符合步伐判断条件。如图1所示,陀螺仪读数在每一次过零点之后都会达到峰值。然而,在红圈里有一个波峰,该波峰相对其他波峰的读数来说,明显要小很多,而且它并不对应大腿的摆动,因此这个波峰是要消除的。
 

本算法包含一个校准模式,用户以最慢的速度去行走,算法就能够调校出最小偏差的陀螺仪信号。当检测到一个过零点,本算法检测该过零点接下来的一个波峰,还检测该波峰是否大于阈值。当波峰大于阈值,步数就会加1。

G、计步算法

检测算法的流程步骤,见图2。要指出的是,本算法对正负斜率过零点都会检测,而且在每次检测后对应切换波峰波谷阈值。然而,切换波峰波谷并不是表示会减少波形的复杂性。

android 开源计步demo android计步器原理_android 开源计步demo_02

图2 计步算法流程图

H、本算法的实现

本算法是由iPhone 4S来进行数据采集,并通过matlab仿真器进行验证。需要指出的是,本算法会把手机放进、取出裤袋等动作计算为步数。因为苹果软件许可里,不允许使用一些手机特性,例如使用环境光传感器去检测手机是否进入了裤袋,当确认手机进入了口袋,计步器才开始计步;当手机离开裤袋,计步器停止计步。当按下开始按钮,程序可以设置一个时限,让用户把手机放进裤袋。当时限一到,计步算法自动启动。而当用户拿出手机,算法可以从总步数里减少点步数,以修正为真实的步数。

四、实验结果

MATLAB仿真结果表明,针对原有的数据统计,本算法计算的步数准确率为100%。本算法是在五种不同的真实活动场景中进行测试:平地,上楼梯,下楼梯,上坡和下坡。有5男5女共10名志愿者参与。他们被要求把手机垂直在裤袋里,并进行的活动。试验分两个阶段进行:第一阶段用正常速度行走,第二阶段用五种不同的步行速度(50,75,100,125和150步/分钟)进行。每个阶段每个步骤都有真实的数据记录。

表一显示在普通速度下不同活动场景的样本结果。这组试验表明,本算法在每个活动场景都达到95%以上的精度。

android 开源计步demo android计步器原理_算法_03

表1 

表二显示了实际步数,计算步数,以及在所有试验的精度。可以看出,本算法在下楼梯时的平均准确度最低,但仍为94.55%。最低准确度为上下楼梯,但仍有90.91%。另外,在平地中行走的最低准确度为96%,最大为100%。本算法在斜坡上的准确度大于95%,下斜坡平均准确度为98.18%,上斜坡为平均准确度为97.17%。

android 开源计步demo android计步器原理_陀螺仪_04

表2

第二组实验是在平地和上楼梯中进行。在自愿者被要求用五种速度去走:50,75,100,125和150步/分钟。表3显示,在平地走的平均准确率最小75步/分钟的速度,但仍为94.59%,而平均准确率仍高达97.89%。值得注意的时,速度最慢的50步/分钟测试中,最小准确率都有96%。而所有步行速度的平均准确率都大于96%。
 

android 开源计步demo android计步器原理_陀螺仪_05

表3

表2显示,每次测试步数在11步的情况下,上下楼梯的最小准确率为90.91%。虽然这是整组实验中最低的,但是在表3看出,上楼梯平均精度最低也有96.36%,速度在75和125步/分钟。下楼梯时,最低的平均准确率为95.45%,速度为50和125的步/分钟。

五、讨论与展望

为毛我们测试上下楼的只走了11步呢?因为我们的楼梯就这么一个(-_-!!!)。就是因为这个原因,搞到我们的实验数据在上下楼梯很难看啊,引入了差一步就差了10%的误差。楼梯级数多点准确率肯定会比现在高。虽然检测结果会比真实步数少,但是真正情况中,没人会在下到楼梯尽头时,将手机拿出裤袋,因此上述错误是不用发生的。(译者注:这是啥逻辑?!我看不懂这个意思)另外,一些软件条例也限制我们不能检测手机是否在裤袋,导致我们的准确率没办法达到100%。
下一步,本算法会应用到其他平台,以观察本算法的真实表现。在本文中,本算法是假设手机必须垂直地放在较紧的裤袋里。作者正在优化本算法,使得手机可以适应各个方向,考虑结合陀螺仪其他轴,编写朝向修正算法。但是,把手机放在比较紧的裤袋里固定起来,是保证本算法准确的关键。
 

 六、代码实现

//首先传入SensorEvent数据
    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中的数据
    * 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;
    }



    /*
     * 检测波峰
     * 以下四个条件判断为波峰:
     * 1.目前点为下降的趋势:isDirectionUp为false
     * 2.之前的点为上升的趋势:lastStatus为true
     * 3.到波峰为止,持续上升大于等于2次
     * 4.波峰值大于1.2g,小于2g
     * 记录波谷值
     * 1.观察波形图,可以发现在出现步子的地方,波谷的下一个就是波峰,有比较明显的特征以及差值
     * 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[]数组中
     * 3.在将数组传入函数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.计算数组的均值
     * 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 {
            ave = (float) 1.7;
        }
        return ave;
    }

六、结论

本文提出了一种智能手机基于单点陀螺计步器为视力障碍人群提供室内定位系统而发展出来的一个功能部分。通过测试不同的活动和不同的步行速度,该算法能得到满意的结果,即使在非常缓慢的步行速度,步数检测仍具有非常高的精度。不管是平地还是斜坡、楼梯,基于陀螺仪的计步器都能容易用作室内定位系统和导航系统的精准计步功能。