用运动测量健康
Measure health with motion
2021年6月9日
一句话判断
Apple Watch 不仅是计步器——CMDeviceMotion 和 CMPedometer 组合起来,你的 App 可以做到从步态分析到跌倒检测的一切运动健康功能。
这场 Session 讲了什么
这个 Session 深入讲解了如何利用 Apple Watch 和 iPhone 的运动传感器数据来测量健康指标。苹果的运动框架栈分为几层:底层的 Core Motion(CMMotionManager)提供原始的加速度计和陀螺仪数据;中间层的 CMDeviceMotion 融合了传感器数据并校正了偏差;上层的 CMPedometer 和 CMAltimeter 提供了高级的步数和楼层攀升数据。
Session 重点展示了两个场景:步态分析(Gait Analysis)和运动类型识别。步态分析通过 CMDeviceMotion 的 userAcceleration 和 attitude 数据,结合信号处理算法,可以提取步长、步频、对称性等指标。这些数据在康复医学和老年人健康监测中有直接应用。运动类型识别则通过 CMMotionActivityManager 判断用户是在走路、跑步、骑车还是开车——虽然系统已经提供了这个能力,但 Session 讲了如何通过机器学习模型提高识别精度。
另外,Session 提到了 watchOS 8 中新增的运动健康 API:HealthKit 的 HKWorkoutBuilder 支持实时数据流,你可以在运动进行中持续获取心率、步频等数据并即时展示。
值得深挖的点
从原始加速度数据提取步态特征。 CMDeviceMotion.userAcceleration 给你的是设备坐标系下的三轴加速度(已去除重力分量)。要从中提取步态信息,你需要先做坐标变换——把设备坐标系的加速度转换到世界坐标系(使用 CMDeviceMotion.attitude 的旋转矩阵),然后对垂直方向的加速度做峰值检测。相邻两个峰值之间的时间间隔就是一步的时间,倒数就是步频。水平方向的位移可以通过对加速度做双重积分来估算(虽然积分漂移是个大问题,但短时间内的估算还是可用的)。Session 强调:不要从头写这些信号处理算法,苹果的 CMPedometer 已经内置了比你自己实现更精确的步数检测。
HKWorkoutBuilder 的实时数据流。 watchOS 8 的 HKWorkoutBuilder 支持通过 beginCollection(withStartDate:) 和 endCollection(withEndDate:) 来定义一次运动的时间窗口。在这期间,你可以通过 dataSource 订阅特定类型的数据更新(心率、距离、卡路里等),每次有新数据时自动触发回调。这比以前轮询 HealthKit 的方式高效得多,也是实现实时运动仪表盘的基础。
代码片段
import CoreMotion
// 使用 CMPedometer 获取步数数据
let pedometer = CMPedometer()
func startStepCounting() {
guard CMPedometer.isStepCountingAvailable() else { return }
// 实时步数更新
pedometer.startUpdates(from: Date()) { data, error in
guard let data = data else { return }
let steps = data.numberOfSteps
let distance = data.distance // 可选,估计距离
let pace = data.currentPace // 可选,当前步速
let cadence = data.currentCadence // 可选,当前步频
print("步数: \(steps), 距离: \(distance?.intValue ?? 0) 米")
}
}
import HealthKit
// 使用 HKWorkoutBuilder 构建实时运动数据流
let healthStore = HKHealthStore()
let workoutBuilder = HKWorkoutBuilder(
healthStore: healthStore,
configuration: HKWorkoutConfiguration(),
device: nil
)
func startWorkout() async throws {
try await workoutBuilder.beginCollection(at: Date())
// 订阅心率数据更新
let heartRateType = HKQuantityType.quantityType(forIdentifier: .heartRate)!
let dataSource = HKLiveWorkoutDataSource(
healthStore: healthStore,
workoutConfiguration: HKWorkoutConfiguration()
)
dataSource.enableAutoHeartRateDetection(true)
// 实时处理数据
workoutBuilder.delegate = self
}
// HKWorkoutBuilderDelegate
func workoutBuilderDidCollectEvent(_ workoutBuilder: HKWorkoutBuilder) {
// 新的运动数据已收集
let heartRate = workoutBuilder.latestStatistics(
for: HKQuantityType.quantityType(forIdentifier: .heartRate)!
)
}
// 检测运动类型变化
let activityManager = CMMotionActivityManager()
func startActivityMonitoring() {
activityManager.startActivityUpdates(to: .main) { activity in
guard let activity = activity else { return }
if activity.walking {
print("用户在走路")
} else if activity.running {
print("用户在跑步")
} else if activity.cycling {
print("用户在骑车")
} else if activity.automotive {
print("用户在开车")
}
// confidence 表示检测的可信度
print("可信度: \(activity.confidence.rawValue)")
}
}
最佳实践
- 在请求 Core Motion 数据前先检查
isStepCountingAvailable()/isActivityAvailable()——不是所有设备都有运动传感器(比如 iPod touch) - HealthKit 和 Core Motion 的权限要分开请求,给用户一个清晰的理由说明为什么需要每个权限
- 步态分析类 App 优先使用
CMPedometer的高级 API 而不是原始传感器数据,苹果的算法比你手写的信号处理更可靠 - watchOS 上长时间的运动数据采集要在 Workout Session 内进行,否则系统可能会限制后台传感器访问
还有什么值得关注
CMAltimeter可以检测楼层攀升数据,对于健身 App 的爬楼梯追踪很有用- iOS 15 的
CMFallDetector(如果可用)可以辅助实现跌倒检测功能 - HealthKit 的运动数据可以写入
HKWorkoutRoute来记录 GPS 轨迹,配合运动类型数据一起展示