原有iOS计步器采用系统框架swift语言调用
/// 传感器
///
/// - Parameter callbcak: 步数steps+距离distance
func caculateSensor(callbcak:@escaping(_ steps:Int,_ distance:Float) ->()) {
if CMPedometer.isStepCountingAvailable() && CMPedometer.isDistanceAvailable() {
self.sensor.startUpdates(from: Date(), withHandler: { (pedometerData:CMPedometerData?, error:Error?) in
if pedometerData != nil {
if pedometerData?.numberOfSteps != nil && pedometerData?.distance != nil{
callbcak(pedometerData?.numberOfSteps as! Int,(pedometerData?.distance?.floatValue)!);
}
}
})
}
}
改为三轴传感器的计步器实现
开启陀螺仪采集三轴数据
/**
* 开启陀螺仪
*/
-(void)startUpdateAccelerometer{
/* 设置采样的频率,单位是秒 */
NSTimeInterval updateInterval = 1/30.0; // 每秒采样30次 ()
/* 判断是否加速度传感器可用,如果可用则继续 */
if ([self.motionManager isAccelerometerAvailable] == YES) {
/* 给采样频率赋值,单位是秒 */
[self.motionManager setAccelerometerUpdateInterval:updateInterval];
/* 加速度传感器开始采样,每次采样结果在block中处理 */
[self.motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error)
{
CGFloat sqrtValue =sqrt(accelerometerData.acceleration.x*accelerometerData.acceleration.x+accelerometerData.acceleration.y*accelerometerData.acceleration.y+accelerometerData.acceleration.z*accelerometerData.acceleration.z); //采集到的三轴加速度矢量长度
[self detectorNewStep:sqrtValue]; //输入算法计算
}];
}
}
计步核心
/*
* 检测步子,并开始计步
* 1.传入sersor中的数据
* 2.如果检测到了波峰,并且符合时间差以及阈值的条件,则判定为1步
* 3.符合时间差条件,波峰波谷差值大于initialValue,则将该差值纳入阈值的计算中
* */
-(void)detectorNewStep:(float) value{
if (gravityOld == 0) {
gravityOld = value;//第一次记录
} else {
//如果是波峰
if ([self detectorPeak:value OldValue:gravityOld]) {
timeOfNow = [self nowTimeInterverl];//当前时间
//是否修改初始阀值(ThreadValue=2)两个波峰的时间差(一步时间)大于0.22s
if (timeOfNow - timeOfLastPeak >= 220
&& (peakOfWave - valleyOfStepMinWave >= initialValue)) {
ThreadValue = [self peak_Valley_thread:(peakOfWave - valleyOfWave)];//调用阀值计算
}
//是否步点 波峰-波谷>=动态阀值ThreadValue
if (timeOfNow - timeOfLastPeak >= 220 && (peakOfWave - valleyOfWave >= ThreadValue)) {
timeOfLastPeak = timeOfNow;;//记录波峰时间
/*
* 更新界面的处理,不涉及到算法
* 一般在通知更新界面之前,增加下面处理,为了处理无效运动:
* 1.连续记录10才开始计步
* 2.例如记录的9步用户停住超过3秒,则前面的记录失效,下次从头开始
* 3.连续记录了9步用户还在运动,之前的数据才有效
*
*/
motion_step ++;//步数
is_motion_step_add = true;//is_motion_step_add 是否步数增加(修改波谷最小值)
self.stepCallback(motion_step);//刷新页面数据
}
}
}
gravityOld = value;
}
波峰检测
/*
* 检测波峰
* 以下四个条件判断为波峰:
* 1.目前点为下降的趋势:isDirectionUp为false
* 2.之前的点为上升的趋势:lastStatus为true
* 3.到波峰为止,持续上升大于等于2次
* 4.波峰值大于20
* 记录波谷值
* 1.观察波形图,可以发现在出现步子的地方,波谷的下一个就是波峰,有比较明显的特征以及差值
* 2.所以要记录每次的波谷值,为了和下次的波峰做对比
* */
-(BOOL) detectorPeak:(float)newValue OldValue:(float)oldValue{
lastStatus = isDirectionUp;
if (newValue >= oldValue) {// 正在上升
isDirectionUp = YES;
continueUpCount++;
} else {// 下降
continueUpFormerCount = continueUpCount;
continueUpCount = 0;
isDirectionUp = NO;
}
// 此时下降,上一个点状态是上升 且 (继续上升的个数 >= 2 或者oldValue > 20)
if (!isDirectionUp && lastStatus
&& (continueUpFormerCount >= 2 || oldValue >= 20)) {// 波峰
peakOfWave = oldValue;
return YES;
} else if (!lastStatus && isDirectionUp) {// 波谷
if (valleyOfWave > oldValue || is_motion_step_add) {//选出波峰到来前的最低波谷值
is_motion_step_add = false;
valleyOfWave = oldValue;
}
return NO;
} else {
return NO;
}
}
阀值的计算
/*
* 阈值的计算
* 1.通过波峰波谷的差值计算阈值
* 2.记录4个值,存入tempValue[]数组中
* 3.在将数组传入函数averageValue中计算阈值
*/
-(float)peak_Valley_thread:(float)value{
float tempThread = ThreadValue;
if (tempValue.count < 4) {
tempValue[tempCount] = @(value);
tempCount++;
} else {
tempThread = [self averageValue:tempValue Num:4];
for (int i = 1; i < 4; i++) {
tempValue[i-1] = tempValue[i];
}
tempValue[3] = @(value);
}
return tempThread;
}
/* 梯度化阈值
* 1.计算数组的均值
* 2.通过均值将阈值梯度化在一个范围里
* */
-(float)averageValue:(NSMutableArray *)value Num:(int)n{
float ave = 0;
for (int i = 0; i < n; i++) {
ave += [value[i] floatValue];
}
ave = ave / 4.0;
if (ave >= 8)
ave = (float) 4.3;
else if (ave >= 7 && ave < 8)
ave = (float) 3.3;
else if (ave >= 4 && ave < 7)
ave = (float) 2.28;
else if (ave >= 3 && ave < 4)
ave = (float) 1.98;
else {
ave = (float) initialValue;//initialValue = 1.1 最低波峰波谷差。
}
return ave;
}