HealthKit 运行 以公里为单位的拆分代码不准确 – 为什么?
HealthKit Running Splits In Kilometres Code Inaccurate – Why?
下面是我到目前为止得到的代码,无法弄清楚为什么我得到的数据不准确。
尚未考虑不应影响前两公里误差的暂停事件...
因此输出将是 1 公里的距离和 1 公里所用的持续时间。
有什么改进的想法,请帮忙?
func getHealthKitWorkouts(){
print("HealthKit Workout:")
/* Boris here: Looks like we need some sort of Health Kit manager */
let healthStore:HKHealthStore = HKHealthStore()
let durationFormatter = NSDateComponentsFormatter()
var workouts = [HKWorkout]()
// Predicate to read only running workouts
let predicate = HKQuery.predicateForWorkoutsWithWorkoutActivityType(HKWorkoutActivityType.Running)
// Order the workouts by date
let sortDescriptor = NSSortDescriptor(key:HKSampleSortIdentifierStartDate, ascending: false)
// Create the query
let sampleQuery = HKSampleQuery(sampleType: HKWorkoutType.workoutType(), predicate: predicate, limit: 0, sortDescriptors: [sortDescriptor])
{ (sampleQuery, results, error ) -> Void in
if let queryError = error {
print( "There was an error while reading the samples: \(queryError.localizedDescription)")
}
workouts = results as! [HKWorkout]
let target:Int = 0
print(workouts[target].workoutEvents)
print("Energy ", workouts[target].totalEnergyBurned)
print(durationFormatter.stringFromTimeInterval(workouts[target].duration))
print((workouts[target].totalDistance!.doubleValueForUnit(HKUnit.meterUnit())))
self.coolMan(workouts[target])
self.coolManStat(workouts[target])
}
// Execute the query
healthStore.executeQuery(sampleQuery)
}
func coolMan(let workout: HKWorkout){
let expectedOutput = [
NSTimeInterval(293),
NSTimeInterval(359),
NSTimeInterval(359),
NSTimeInterval(411),
NSTimeInterval(810)
]
let healthStore:HKHealthStore = HKHealthStore()
let distanceType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning)
let workoutPredicate = HKQuery.predicateForObjectsFromWorkout(workout)
let startDateSort = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: true)
let query = HKSampleQuery(sampleType: distanceType!, predicate: workoutPredicate,
limit: 0, sortDescriptors: [startDateSort]) {
(sampleQuery, results, error) -> Void in
// Process the detailed samples...
if let distanceSamples = results as? [HKQuantitySample] {
var count = 0.00, countPace = 0.00, countDistance = 0.0, countPacePerMeterSum = 0.0
var countSplits = 0
var firstStart = distanceSamples[0].startDate
let durationFormatter = NSDateComponentsFormatter()
print(" Time Splits: ")
for (index, element) in distanceSamples.enumerate() {
count += element.quantity.doubleValueForUnit(HKUnit.meterUnit())
/* Calculate Pace */
let duration = ((element.endDate.timeIntervalSinceDate(element.startDate)))
let distance = distanceSamples[index].quantity
let pacePerMeter = distance.doubleValueForUnit(HKUnit.meterUnit()) / duration
countPace += duration
countPacePerMeterSum += pacePerMeter
if count > 1000 {
/* Account for extra bits */
let percentageUnder = (1000 / count)
//countPace = countPace * percentageUnder
// 6.83299013038 * 2.5
print(" Reached Kilometer \(count) ")
// MARK: Testing
let testOutput = durationFormatter.stringFromTimeInterval(NSTimeInterval.init(floatLiteral: test)),
testOutputExpected = durationFormatter.stringFromTimeInterval(expectedOutput[countSplits])
print(" Output Accuracy (", round(test - expectedOutput[countSplits]) , "): expected \(testOutputExpected) versus \(testOutput)")
print(" ", firstStart, " until ", element.endDate)
/* Print The Split Time Taken */
firstStart = distanceSamples[index].endDate;
count = (count % 1000) //0.00
countPace = (count % 1000) * pacePerMeter
countSplits++
/* Noise
\(countSplits) – \(count) – Pace \(countPace) – Pace Per Meter \(pacePerMeter) – Summed Pace Per Meter \(countPacePerMeterSum) – \(countPacePerMeterSum / Double.init(index))"
*/
}
/* Account for the last entry */
if (distanceSamples.count - 1 ) == index {
print("We started a kilometer \(countSplits+1) – \(count)")
let pacePerKM = (count / countPace) * 1000
print(durationFormatter.stringFromTimeInterval(NSTimeInterval.init(floatLiteral: (pacePerKM ))))
}
}
}else {
// Perform proper error handling here...
print("*** An error occurred while adding a sample to " + "the workout: \(error!.localizedDescription)")
abort()
}
}
healthStore.executeQuery(query)
}
func coolManStat(let workout: HKWorkout){
let healthStore:HKHealthStore = HKHealthStore()
let stepsCount = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning)
let sumOption = HKStatisticsOptions.CumulativeSum
let statisticsSumQuery = HKStatisticsQuery(quantityType: stepsCount!, quantitySamplePredicate: HKQuery.predicateForObjectsFromWorkout(workout),
options: sumOption)
{ (query, result, error) in
if let sumQuantity = result?.sumQuantity() {
let numberOfSteps = Int(sumQuantity.doubleValueForUnit(HKUnit.meterUnit()))/1000
print(" Right -O: ",numberOfSteps)
}
}
healthStore.executeQuery(statisticsSumQuery)
}
两年多过去了,我相信你现在已经解决了这个问题!但我相信以后会有其他人遇到这个话题,所以我想我会分享答案。
我从您的代码版本开始(非常感谢!!)并遇到了同样的问题。我不得不做一些改变。并非所有这些更改都与您遇到的问题有关,但无论如何,以下是我到目前为止想到的所有注意事项:
漂移
您没有处理 'drift',尽管这不是导致您的输出出现重大错误的原因。我的意思是你的代码在说:
如果计数 > 1000
但是您不对超过 1000 的余数进行任何操作,因此您的公里时间不是 1000 米,而是 1001 米。所以你的时间对于当前公里来说都是不准确的,而且它包括下一公里的一些 运行ning,所以那个时间也会是错误的。长时间 运行,这可能会开始引起明显的问题。但这对短 运行s 来说没什么大不了的,因为我认为这种差异在小距离内还不够显着。但这绝对值得修复。在我的代码中,我假设 运行ner 在当前样本中以恒定的速度移动(这显然不完美,但我认为没有更好的方法),然后我只是找到当前样本距离的分数,使分割距离超过 1000 米,并获得当前样本持续时间的相同分数,并将其从当前公里的时间中删除,并将其(和距离)添加到下一个分割。
GPS 下降
你的结果的真正问题是你没有处理 GPS 掉落。我目前处理这个问题的方法是将当前样本的开始日期与前一个样本的结束日期进行比较。如果它们不相同,则说明 GPS 掉线。您需要将之前的 endDate 和当前的 startDate 之间的差异添加到当前拆分中。 编辑: 您还需要使用 activity 的开始日期和第一个示例的开始日期来执行此操作。 GPS 连接时,这两个日期之间会有间隔。
暂停
上述 GPS 丢失问题有点复杂。如果用户暂停了锻炼,那么当前样本的开始日期和前一个样本的结束日期之间也会有差异。因此,您需要能够检测到这种情况,而不是在这种情况下调整拆分。但是,如果用户的 GPS 掉线并且他们在那段时间也暂停了,那么您需要从丢失的时间中减去暂停时间,然后再将其添加到拆分。
不幸的是,我的分段仍未与 Apple Workouts 应用程序 100% 同步。但他们已经从可能的几分钟内关闭变成了大部分时间在 1 秒内。我见过的最差的是 3 秒。我只研究了几个小时,所以我计划继续尝试获得 100% 的准确率。如果我得到那个,我会更新这个答案。但我相信我已经解决了这里的主要问题。
下面是我到目前为止得到的代码,无法弄清楚为什么我得到的数据不准确。
尚未考虑不应影响前两公里误差的暂停事件...
因此输出将是 1 公里的距离和 1 公里所用的持续时间。 有什么改进的想法,请帮忙?
func getHealthKitWorkouts(){
print("HealthKit Workout:")
/* Boris here: Looks like we need some sort of Health Kit manager */
let healthStore:HKHealthStore = HKHealthStore()
let durationFormatter = NSDateComponentsFormatter()
var workouts = [HKWorkout]()
// Predicate to read only running workouts
let predicate = HKQuery.predicateForWorkoutsWithWorkoutActivityType(HKWorkoutActivityType.Running)
// Order the workouts by date
let sortDescriptor = NSSortDescriptor(key:HKSampleSortIdentifierStartDate, ascending: false)
// Create the query
let sampleQuery = HKSampleQuery(sampleType: HKWorkoutType.workoutType(), predicate: predicate, limit: 0, sortDescriptors: [sortDescriptor])
{ (sampleQuery, results, error ) -> Void in
if let queryError = error {
print( "There was an error while reading the samples: \(queryError.localizedDescription)")
}
workouts = results as! [HKWorkout]
let target:Int = 0
print(workouts[target].workoutEvents)
print("Energy ", workouts[target].totalEnergyBurned)
print(durationFormatter.stringFromTimeInterval(workouts[target].duration))
print((workouts[target].totalDistance!.doubleValueForUnit(HKUnit.meterUnit())))
self.coolMan(workouts[target])
self.coolManStat(workouts[target])
}
// Execute the query
healthStore.executeQuery(sampleQuery)
}
func coolMan(let workout: HKWorkout){
let expectedOutput = [
NSTimeInterval(293),
NSTimeInterval(359),
NSTimeInterval(359),
NSTimeInterval(411),
NSTimeInterval(810)
]
let healthStore:HKHealthStore = HKHealthStore()
let distanceType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning)
let workoutPredicate = HKQuery.predicateForObjectsFromWorkout(workout)
let startDateSort = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: true)
let query = HKSampleQuery(sampleType: distanceType!, predicate: workoutPredicate,
limit: 0, sortDescriptors: [startDateSort]) {
(sampleQuery, results, error) -> Void in
// Process the detailed samples...
if let distanceSamples = results as? [HKQuantitySample] {
var count = 0.00, countPace = 0.00, countDistance = 0.0, countPacePerMeterSum = 0.0
var countSplits = 0
var firstStart = distanceSamples[0].startDate
let durationFormatter = NSDateComponentsFormatter()
print(" Time Splits: ")
for (index, element) in distanceSamples.enumerate() {
count += element.quantity.doubleValueForUnit(HKUnit.meterUnit())
/* Calculate Pace */
let duration = ((element.endDate.timeIntervalSinceDate(element.startDate)))
let distance = distanceSamples[index].quantity
let pacePerMeter = distance.doubleValueForUnit(HKUnit.meterUnit()) / duration
countPace += duration
countPacePerMeterSum += pacePerMeter
if count > 1000 {
/* Account for extra bits */
let percentageUnder = (1000 / count)
//countPace = countPace * percentageUnder
// 6.83299013038 * 2.5
print(" Reached Kilometer \(count) ")
// MARK: Testing
let testOutput = durationFormatter.stringFromTimeInterval(NSTimeInterval.init(floatLiteral: test)),
testOutputExpected = durationFormatter.stringFromTimeInterval(expectedOutput[countSplits])
print(" Output Accuracy (", round(test - expectedOutput[countSplits]) , "): expected \(testOutputExpected) versus \(testOutput)")
print(" ", firstStart, " until ", element.endDate)
/* Print The Split Time Taken */
firstStart = distanceSamples[index].endDate;
count = (count % 1000) //0.00
countPace = (count % 1000) * pacePerMeter
countSplits++
/* Noise
\(countSplits) – \(count) – Pace \(countPace) – Pace Per Meter \(pacePerMeter) – Summed Pace Per Meter \(countPacePerMeterSum) – \(countPacePerMeterSum / Double.init(index))"
*/
}
/* Account for the last entry */
if (distanceSamples.count - 1 ) == index {
print("We started a kilometer \(countSplits+1) – \(count)")
let pacePerKM = (count / countPace) * 1000
print(durationFormatter.stringFromTimeInterval(NSTimeInterval.init(floatLiteral: (pacePerKM ))))
}
}
}else {
// Perform proper error handling here...
print("*** An error occurred while adding a sample to " + "the workout: \(error!.localizedDescription)")
abort()
}
}
healthStore.executeQuery(query)
}
func coolManStat(let workout: HKWorkout){
let healthStore:HKHealthStore = HKHealthStore()
let stepsCount = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning)
let sumOption = HKStatisticsOptions.CumulativeSum
let statisticsSumQuery = HKStatisticsQuery(quantityType: stepsCount!, quantitySamplePredicate: HKQuery.predicateForObjectsFromWorkout(workout),
options: sumOption)
{ (query, result, error) in
if let sumQuantity = result?.sumQuantity() {
let numberOfSteps = Int(sumQuantity.doubleValueForUnit(HKUnit.meterUnit()))/1000
print(" Right -O: ",numberOfSteps)
}
}
healthStore.executeQuery(statisticsSumQuery)
}
两年多过去了,我相信你现在已经解决了这个问题!但我相信以后会有其他人遇到这个话题,所以我想我会分享答案。
我从您的代码版本开始(非常感谢!!)并遇到了同样的问题。我不得不做一些改变。并非所有这些更改都与您遇到的问题有关,但无论如何,以下是我到目前为止想到的所有注意事项:
漂移
您没有处理 'drift',尽管这不是导致您的输出出现重大错误的原因。我的意思是你的代码在说:
如果计数 > 1000
但是您不对超过 1000 的余数进行任何操作,因此您的公里时间不是 1000 米,而是 1001 米。所以你的时间对于当前公里来说都是不准确的,而且它包括下一公里的一些 运行ning,所以那个时间也会是错误的。长时间 运行,这可能会开始引起明显的问题。但这对短 运行s 来说没什么大不了的,因为我认为这种差异在小距离内还不够显着。但这绝对值得修复。在我的代码中,我假设 运行ner 在当前样本中以恒定的速度移动(这显然不完美,但我认为没有更好的方法),然后我只是找到当前样本距离的分数,使分割距离超过 1000 米,并获得当前样本持续时间的相同分数,并将其从当前公里的时间中删除,并将其(和距离)添加到下一个分割。
GPS 下降
你的结果的真正问题是你没有处理 GPS 掉落。我目前处理这个问题的方法是将当前样本的开始日期与前一个样本的结束日期进行比较。如果它们不相同,则说明 GPS 掉线。您需要将之前的 endDate 和当前的 startDate 之间的差异添加到当前拆分中。 编辑: 您还需要使用 activity 的开始日期和第一个示例的开始日期来执行此操作。 GPS 连接时,这两个日期之间会有间隔。
暂停
上述 GPS 丢失问题有点复杂。如果用户暂停了锻炼,那么当前样本的开始日期和前一个样本的结束日期之间也会有差异。因此,您需要能够检测到这种情况,而不是在这种情况下调整拆分。但是,如果用户的 GPS 掉线并且他们在那段时间也暂停了,那么您需要从丢失的时间中减去暂停时间,然后再将其添加到拆分。
不幸的是,我的分段仍未与 Apple Workouts 应用程序 100% 同步。但他们已经从可能的几分钟内关闭变成了大部分时间在 1 秒内。我见过的最差的是 3 秒。我只研究了几个小时,所以我计划继续尝试获得 100% 的准确率。如果我得到那个,我会更新这个答案。但我相信我已经解决了这里的主要问题。