Highlight
Core Motion 在 iOS 17 和 watchOS 10 中新增三项能力:AirPods 头部追踪数据支持 macOS、Apple Watch Ultra 水下深度与温度监测、以及 HealthKit 运动期间的高频传感器数据流(800Hz 加速度计 / 200Hz 设备运动)。
核心内容
运动数据是 Apple 生态的隐形基础设施。计步、碰撞检测、空间音频——这些体验都建立在加速度计、陀螺仪、气压计等传感器之上。Core Motion 作为统一入口,让开发者也能使用这些数据。
AirPods 头部追踪扩展到 macOS
(02:22)空间音频的动态头部追踪依赖与 iPhone 和 Apple Watch 相同的运动算法。CMHeadphoneMotionManager 从 iOS 14 起开放这些数据,让 App 能追踪用户头部的姿态、加速度和旋转。
今年这个能力扩展到 macOS 14。
import CoreMotion
let headphoneManager = CMHeadphoneMotionManager()
headphoneManager.delegate = self
// 检查设备是否支持
if headphoneManager.isDeviceMotionAvailable {
headphoneManager.startDeviceMotionUpdates(to: .main) { motion, error in
guard let motion = motion else { return }
// 头部姿态(俯仰、偏航、翻滚)
let attitude = motion.attitude
// 用户加速度
let acceleration = motion.userAcceleration
// 旋转速率
let rotation = motion.rotationRate
// 数据来源:左耳还是右耳
let location = motion.sensorLocation // .left or .right
}
}
关键点:
- 支持 AirPods Pro 等支持空间音频动态头部追踪的耳机
sensorLocation区分数据来自左耳还是右耳- 开启自动入耳检测后,取出单耳会自动切换到另一耳继续传输
- 需要 Info.plist 中配置
Motion Usage Description权限描述
连接状态监听:
extension ViewController: CMHeadphoneMotionManagerDelegate {
func headphoneMotionManagerDidConnect(_ manager: CMHeadphoneMotionManager) {
// 耳机已连接,可以开始接收数据
}
func headphoneMotionManagerDidDisconnect(_ manager: CMHeadphoneMotionManager) {
// 耳机断开,停止相关功能
}
}
关键点:
- delegate 方法在连接状态变化时触发
- 自动入耳检测开启时,取出/放入耳机也会触发 disconnect/connect
- 头戴式耳机的佩戴检测同样支持
Apple Watch Ultra 水下数据监测
(07:22)CMWaterSubmersionManager 利用 Apple Watch Ultra 的气压计,在浮潜、游泳等水上活动中追踪水深和水温。
import CoreMotion
let submersionManager = CMWaterSubmersionManager()
submersionManager.delegate = self
Delegate 实现:
extension WorkoutManager: CMWaterSubmersionManagerDelegate {
// 浸没状态变化(入水/出水)
func waterSubmersionManager(
_ manager: CMWaterSubmersionManager,
didUpdate event: CMWaterSubmersionEvent
) {
switch event.state {
case .notSubmerged:
// 在水面上
case .submergedShallow:
// 水下 1 米以内
case .submergedDeep:
// 水下超过 1 米
case .approachingMaxDepth:
// 接近 6 米最大深度
case .pastMaxDepth:
// 超过 6 米
case .sensorDepthError:
// 传感器超出量程
}
}
// 深度、压力、水温测量数据
func waterSubmersionManager(
_ manager: CMWaterSubmersionManager,
didUpdate measurement: CMWaterSubmersionMeasurement
) {
let depth = measurement.depth?.value // 水深(米)
let pressure = measurement.pressure?.value // 水压
let surfacePressure = measurement.surfacePressure?.value // 水面气压
}
// 水温更新
func waterSubmersionManager(
_ manager: CMWaterSubmersionManager,
didUpdate temperature: CMWaterTemperature
) {
let waterTemp = temperature.temperature.value // 水温
let uncertainty = temperature.uncertainty // 不确定度
}
}
关键点:
- 仅 Apple Watch Ultra 支持,需要 watchOS 9+
- 需要添加 “Shallow Depth and Pressure” capability
- 水温数据仅在浸没状态下可用,刚入水时不确定度较高,随时间收敛
- 最大监测深度 6 米,超出后进入
pastMaxDepth状态 - 深度分区:
notSubmerged→submergedShallow(小于 1m)→submergedDeep(大于 1m)
高频传感器数据流 CMBatchedSensorManager
(11:35)Apple Watch Series 8 和 Ultra 支持新的高频传感器数据流。与现有的 CMMotionManager(最高 100Hz,逐样本实时推送)不同,CMBatchedSensorManager 每秒批量传输一次数据,频率提升到 800Hz 加速度计和 200Hz 设备运动。
两种数据获取方式的对比:
| 特性 | CMMotionManager | CMBatchedSensorManager |
|---|---|---|
| 加速度计频率 | 最高 100Hz | 800Hz |
| 设备运动频率 | 最高 100Hz | 200Hz |
| 传输方式 | 逐样本实时推送 | 每秒批量传输 |
| 延迟 | 低(亚秒级) | 较高(秒级) |
| 使用场景 | UI 反馈、实时控制 | 运动分析、事后计算 |
| 前提条件 | 无 | 需要活跃的 HealthKit Workout |
(13:18)演讲者以棒球挥棒为例演示了高频数据的价值。挥棒全程约 0.3 秒,100Hz 采样只能获得 30 个数据点,而 800Hz 能获得 240 个——足够分辨击球瞬间的细微振动差异。
详细内容
高频数据流的使用流程
(16:17)使用 CMBatchedSensorManager 需要活跃的 HealthKit Workout Session:
import CoreMotion
import HealthKit
let batchedManager = CMBatchedSensorManager()
// 1. 确认设备支持
guard batchedManager.isAccelerometerSupported else { return }
// 2. 启动 HealthKit Workout
let workoutConfiguration = HKWorkoutConfiguration()
workoutConfiguration.activityType = .baseball
// ... 配置并启动 workout session
// 3. 使用 Swift async 接收批量数据
Task {
do {
for try await batch in batchedManager.accelerometerUpdates() {
processAccelerometerBatch(batch)
}
} catch {
// 处理授权错误或平台不支持
}
}
关键点:
isAccelerometerSupported和isDeviceMotionSupported检查设备支持- 仅 Apple Watch Series 8 和 Ultra 支持
- 必须在活跃的 HealthKit Workout 中才能获取数据
- 使用 Swift async/await 接口逐批处理数据
- 循环中需要检查 workout 是否结束,避免无限循环
挥棒分析的算法实现
(17:08)演讲者展示了如何用高频数据计算”击球接触时间”(time to contact):
步骤 1:检测击球瞬间
func detectImpact(from batch: [CMAccelerometerData]) -> TimeInterval? {
// z 轴垂直于表冠方向,击球时会产生明显振动
let filteredZ = batch.map { highPassFilter($0.acceleration.z) }
// 找到滤波后信号的峰值
guard let maxIndex = filteredZ.enumerated().max(by: { $0.element < $1.element })?.offset else {
return nil
}
// 从原始数据获取对应时间戳
return batch[maxIndex].timestamp
}
关键点:
- z 轴方向垂直于 Apple Watch 表冠
- 使用高通滤波器隔离高频冲击信号
- 峰值对应击球瞬间
步骤 2:检测挥棒起始
func detectSwingStart(impactTime: TimeInterval, motionBuffer: [CMDeviceMotion]) -> TimeInterval? {
// 从 impactTime 向前遍历,寻找旋转速率沿重力方向的分量
// 从超过阈值变为低于阈值的转折点
for sample in motionBuffer.reversed() {
// 只检查 impactTime 之前的样本
guard sample.timestamp < impactTime else { continue }
// 挥棒持续时间不应超过合理范围(如 500ms)
guard impactTime - sample.timestamp < 0.5 else { break }
// 计算旋转速率沿重力方向的分量
let rotationAlongGravity =
sample.rotationRate.x * sample.gravity.x +
sample.rotationRate.y * sample.gravity.y +
sample.rotationRate.z * sample.gravity.z
// 寻找从超过阈值到低于阈值的转变点
if abs(rotationAlongGravity) < swingThreshold {
return sample.timestamp
}
}
return nil
}
关键点:
- 从 impactTime 向前遍历设备运动数据
- 旋转速率沿重力方向的分量反映手腕绕身体的旋转
- 挥棒期间该分量显著非零,静止时接近零
- 设置合理的挥棒持续时间上限过滤噪声
步骤 3:计算接触时间
func computeTimeToContact(impactTime: TimeInterval, swingStartTime: TimeInterval?) -> TimeInterval? {
guard let startTime = swingStartTime else { return nil }
// 验证挥棒期间累计旋转角度在合理范围
let accumulatedRotation = computeAccumulatedRotation(from: startTime, to: impactTime)
guard accumulatedRotation > minRotationThreshold && accumulatedRotation < maxRotationThreshold else {
return nil
}
return impactTime - startTime
}
关键点:
- 接触时间 = 击球瞬间时间戳 - 挥棒起始时间戳
- 通过累计旋转角度验证检测到的挥棒是否有效
- 过滤误检(如挥手、调整姿势等非挥棒动作)
击中和未击中的信号差异
(20:45)演讲者对比了击中球和未击中球的加速度计信号。两种情况下挥棒动作本身相似,但击球瞬间的振动模式明显不同——击中时产生尖锐的冲击峰值,未击中时振动更平缓。800Hz 数据流能捕捉这种差异,100Hz 则难以区分。
核心启发
1. 头部追踪驱动的空间音频游戏
- 做什么:用 AirPods 的头部姿态数据控制游戏视角或菜单选择
- 为什么值得做:用户无需手持设备,转头即可操作,适合健身和 VR 类体验
- 怎么开始:
CMHeadphoneMotionManager获取attitude数据,映射到游戏相机的欧拉角
2. 水上运动记录 App
- 做什么:为浮潜、自由潜水爱好者记录深度曲线、水温变化、水下时间
- 为什么值得做:Apple Watch Ultra 的
CMWaterSubmersionManager提供原生水深和水温 API,无需外接传感器 - 怎么开始:配置 “Shallow Depth and Pressure” capability,实现 delegate 接收深度和温度更新,叠加到 HealthKit Workout 中
3. 运动姿势分析工具
- 做什么:用 800Hz 加速度计和 200Hz 设备运动数据分析高尔夫挥杆、网球发球等动作
- 为什么值得做:高频数据能捕捉 100Hz 无法分辨的细微动作差异,如击球瞬间的杆头速度、手腕翻转角度
- 怎么开始:启动 HealthKit Workout,使用
CMBatchedSensorManager的 async 接口批量获取数据,设计针对特定运动的检测算法
4. 健身动作计数与质量评估
- 做什么:自动识别俯卧撑、深蹲等动作的完成次数,并评估动作质量
- 为什么值得做:头部追踪数据可以判断身体姿态,高频加速度计能检测动作的节奏和幅度
- 怎么开始:AirPods 提供头部姿态参考,
CMBatchedSensorManager提供手腕运动细节,两者结合构建全身运动模型
关联 Session
- Build a multi-device workout app — Apple Watch 与 iPhone 协同构建运动 App 的完整流程
- Building custom workouts with WorkoutKit — WorkoutKit 自定义训练结构,与 CMBatchedSensorManager 配合使用
- What’s new in HealthKit — HealthKit 新功能,运动数据存储与共享
- Meet Core ML Tools — 将高频传感器数据用于机器学习模型训练
评论
GitHub Issues · utterances