HarmonyOS运动开发:精准估算室内运动的距离、速度与步幅
前言
在室内运动场景中,由于缺乏 GPS 信号,传统的基于卫星定位的运动数据追踪方法无法使用。因此,如何准确估算室内运动的距离、速度和步幅,成为了运动应用开发中的一个重要挑战。本文将结合鸿蒙(HarmonyOS)开发实战经验,深入解析如何利用加速度传感器等设备功能,实现室内运动数据的精准估算。
一、加速度传感器:室内运动数据的核心
加速度传感器是实现室内运动数据估算的关键硬件。它能够实时监测设备在三个轴向上的加速度变化,从而为运动状态分析提供基础数据。以下是加速度传感器服务类的核心代码:
import common from '@ohos.app.ability.common';
import sensor from '@ohos.sensor';
import { BusinessError } from '@kit.BasicServicesKit';
import { abilityAccessCtrl } from '@kit.AbilityKit';
import { UserProfile } from '../user/UserProfile';
interface Accelerometer {
x: number;
y: number;
z: number;
}
export class AccelerationSensorService {
private static instance: AccelerationSensorService | null = null;
private context: common.UIAbilityContext;
private isMonitoring: boolean = false; // 是否正在监听
private constructor(context: common.UIAbilityContext) {
this.context = context;
}
static getInstance(context: common.UIAbilityContext): AccelerationSensorService {
if (!AccelerationSensorService.instance) {
AccelerationSensorService.instance = new AccelerationSensorService(context);
}
return AccelerationSensorService.instance;
}
private accelerometerCallback = (data: sensor.AccelerometerResponse) => {
this.accelerationData = {
x: data.x,
y: data.y,
z: data.z
};
};
private async requestAccelerationPermission(): Promise<boolean> {
const atManager = abilityAccessCtrl.createAtManager();
try {
const result = await atManager.requestPermissionsFromUser(
this.context,
['ohos.permission.ACCELEROMETER']
);
return result.permissions[0] === 'ohos.permission.ACCELEROMETER' &&
result.authResults[0] === 0;
} catch (err) {
console.error('申请权限失败:', err);
return false;
}
}
public async startDetection(): Promise<void> {
if (this.isMonitoring) return;
const hasPermission = await this.requestAccelerationPermission();
if (!hasPermission) {
throw new Error('未授予加速度传感器权限');
}
this.isMonitoring = true;
this.setupAccelerometer();
}
private setupAccelerometer(): void {
try {
sensor.on(sensor.SensorId.ACCELEROMETER, this.accelerometerCallback);
console.log('加速度传感器启动成功');
} catch (error) {
console.error('加速度传感器初始化失败:', (error as BusinessError).message);
}
}
public stopDetection(): void {
if (!this.isMonitoring) return;
this.isMonitoring = false;
sensor.off(sensor.SensorId.ACCELEROMETER, this.accelerometerCallback);
}
private accelerationData: Accelerometer = { x: 0, y: 0, z: 0 };
getCurrentAcceleration(): Accelerometer {
return this.accelerationData;
}
calculateStride(timeDiff: number): number {
const accel = this.accelerationData;
const magnitude = Math.sqrt(accel.x ** 2 + accel.y ** 2 + accel.z ** 2);
const userProfile = UserProfile.getInstance();
if (Math.abs(magnitude - 9.8) < 0.5) { // 接近重力加速度时视为静止
return 0;
}
const baseStride = userProfile.getHeight() * 0.0045; // 转换为米
const dynamicFactor = Math.min(1.5, Math.max(0.8, (magnitude / 9.8) * (70 / userProfile.getWeight())));
return baseStride * dynamicFactor * timeDiff;
}
}
核心点解析
• 权限申请:在使用加速度传感器之前,必须申请ohos.permission.ACCELEROMETER权限。通过abilityAccessCtrl.createAtManager方法申请权限,并检查用户是否授权。
• 数据监听:通过sensor.on方法监听加速度传感器数据,实时更新accelerationData。
• 步幅计算:结合用户身高和加速度数据动态计算步幅。静止状态下返回 0 步幅,避免误判。
二、室内运动数据的估算
在室内运动场景中,我们无法依赖 GPS 定位,因此需要通过步数和步幅来估算运动距离和速度。以下是核心计算逻辑:
addPointBySteps(): number {
const currentSteps = this.stepCounterService?.getCurrentSteps() ?? 0;
const userProfile = UserProfile.getInstance();
const accelerationService = AccelerationSensorService.getInstance(this.context);
const point = new RunPoint(0, 0);
const currentTime = Date.now();
point.netDuration = Math.floor((currentTime - this.startTime) / 1000);
point.totalDuration = point.netDuration + Math.floor(this.totalDuration);
const pressureService = PressureDetectionService.getInstance();
point.altitude = pressureService.getCurrentAltitude();
point.totalAscent = pressureService.getTotalAscent();
point.totalDescent = pressureService.getTotalDescent();
point.steps = currentSteps;
if (this.runState === RunState.Running) {
const stepDiff = currentSteps - (this.previousPoint?.steps ?? 0);
const timeDiff = (currentTime - (this.previousPoint?.timestamp ?? currentTime)) / 1000;
const accelData = accelerationService.getCurrentAcceleration();
const magnitude = Math.sqrt(accelData.x ** 2 + accelData.y ** 2 + accelData.z ** 2);
let stride = accelerationService.calculateStride(timeDiff);
if (stepDiff > 0 && stride > 0) {
const distanceBySteps = stepDiff * stride;
this.totalDistance += distanceBySteps / 1000;
point.netDistance = this.totalDistance * 1000;
point.totalDistance = point.netDistance;
console.log(`步数变化: ${stepDiff}, 步幅: ${stride.toFixed(2)}m, 距离增量: ${distanceBySteps.toFixed(2)}m`);
}
if (this.previousPoint && timeDiff > 0) {
const instantCadence = stepDiff > 0 ? (stepDiff / timeDiff) * 60 : 0;
point.cadence = this.currentPoint ?
(this.currentPoint.cadence * 0.7 + instantCadence * 0.3) :
instantCadence;
const instantSpeed = distanceBySteps / timeDiff;
point.speed = this.currentPoint ?
(this.currentPoint.speed * 0.7 + instantSpeed * 0.3) :
instantSpeed;
point.stride = stride;
} else {
point.cadence = this.currentPoint?.cadence ?? 0;
point.speed = this.currentPoint?.speed ?? 0;
point.stride = stride;
}
if (this.exerciseType && userProfile && this.previousPoint) {
const distance = point.netDuration;
const ascent = point.totalAscent - this.previousPoint.totalAscent;
const descent = point.totalDescent - this.previousPoint.totalDescent;
const newCalories = CalorieCalculator.calculateCalories(
this.exerciseType,
userProfile.getWeight(),
userProfile.getAge(),
userProfile.getGender(),
0, // 暂不使用心率数据
ascent,
descent,
distance
);
point.calories = this.previousPoint.calories + newCalories;
}
}
this.previousPoint = this.currentPoint;
this.currentPoint = point;
if (this.currentSport && this.runState === RunState.Running) {
this.currentSport.distance = this.totalDistance * 1000;
this.currentSport.calories = point.calories;
this.sportDataService.saveCurrentSport(this.currentSport);
}
return this.totalDistance;
}
核心点解析
• 步数差与时间差:通过当前步数与上一次记录的步数差值,结合时间差,计算出步频和步幅。
• 动态步幅调整:根据加速度数据动态调整步幅,确保在不同运动强度下的准确性。
• 速度与卡路里计算:结合步幅和步数差值,计算出运动速度和消耗的卡路里。
• 数据平滑处理:使用移动平均法对步频和速度进行平滑处理,减少数据波动。
三、每秒更新数据
为了实时展示运动数据,我们需要每秒更新一次数据。以下是定时器的实现逻辑:
private startTimer(): void {
if (this.timerInterval === null) {
this.timerInterval = setInterval(() => {
if (this.runState === RunState.Running) {
this.netDuration = Math.floor((Date.now() - this.startTime) / 1000);
// 室内跑:使用步数添加轨迹点
if (this.exerciseType?.sportType === SportType.INDOOR) {
this.addPointBySteps(); // 新增调用
}
// 计算当前配速(秒/公里)
let currentPace = 0;
if (this.totalDistance > 0) {
currentPace = Math.floor(this.netDuration / this.totalDistance);
}
if (this.currentPoint) {
this.currentPoint.pace = currentPace;
}
// 通知所有监听器
this.timeListeners.forEach(listener => {
listener.onTimeUpdate(this.netDuration, this.currentPoint);
});
}
}, 1000); // 每1秒更新一次
}
}
核心点解析
- 定时器设置:使用
setInterval方法每秒触发一次数据更新逻辑。 - 运动状态判断:只有在运动状态为
Running时,才进行数据更新。 - 配速计算:通过总时间与总距离的比值计算当前配速。
- 通知监听器:将更新后的数据通过监听器传递给其他组件,确保数据的实时展示。
四、优化与改进
1. 数据平滑处理
在实际运动过程中,加速度数据可能会受到多种因素的干扰,导致数据波动较大。为了提高数据的准确性和稳定性,我们采用了移动平均法对步频和速度进行平滑处理:
point.cadence = this.currentPoint ?
(this.currentPoint.cadence * 0.7 + instantCadence * 0.3) :
instantCadence;
point.speed = this.currentPoint ?
(this.currentPoint.speed * 0.7 + instantSpeed * 0.3) :
instantSpeed;
通过这种方式,可以有效减少数据的短期波动,使运动数据更加平滑和稳定。
2.动态步幅调整
步幅会因用户的运动强度和身体状态而变化。为了更准确地估算步幅,我们引入了动态调整机制:
let stride = accelerationService.calculateStride(timeDiff);
在calculateStride方法中,结合用户的身高、体重和加速度数据,动态计算步幅。这种方法可以更好地适应不同用户的运动状态。
五、总结与展望
通过加速度传感器和定时器,我们成功实现了室内运动的距离、速度和步幅估算。这些功能不仅能够帮助用户更好地了解自己的运动状态,还能为运动健康管理提供重要数据支持。
HarmonyOS运动开发:精准估算室内运动的距离、速度与步幅的更多相关文章
- 青蛙的约会 扩展欧几里得 方程ax+by=c的整数解 一个跑道长为周长为L米,两只青蛙初始位置为x,y;(x!=y,同时逆时针运动,每一次运动分别为m,n米;问第几次运动后相遇,即在同一位置。
/** 题目:青蛙的约会 链接:https://vjudge.net/contest/154246#problem/R 题意:一个跑道长为周长为L米,两只青蛙初始位置为x,y:(x!=y,同时逆时针运 ...
- 鸿蒙HarmonyOS应用开发落地实践,Harmony Go 技术沙龙落地北京
12月26日,华为消费者BG软件部开源中心与51CTO Harmony OS技术社区携手,共同主办了主题为"Harmony OS 应用开发落地实践"的 Harmony Go 技术沙 ...
- 它来了,它来了,HarmonyOS应用开发在线体验来了
接下来是我们的两分钟科普,一分钟玩转HarmonyOS应用开发在线体验,一分钟简单了解"一次开发.多设备部署"的原理.萌新的开发者也能第一时间掌握,往下看吧~ 一分钟玩转Harmo ...
- Google Tango Java SDK开发:Motion Tracking 运动追踪
Java API Motion Tracking Tutorial运动追踪教程 This page describes how the Java API handles motion tracking ...
- 前言「HarmonyOS应用开发基础篇」
场景一.随着智能设备种类的不断增多,我们基本上每人都有好几台智能设备,比如智能手机,平板,耳机,音响,穿戴设备等等.这些设备都具有独立性,偶尔的组合也是我们通过手动去搭配,并且不一定能够完全组合在一起 ...
- javascript运动系列第九篇——碰撞运动
× 目录 [1]碰撞检测 [2]无损碰撞 [3]有损碰撞 前面的话 碰撞可以分为碰壁和互碰两种形式,上篇介绍了碰壁运动,本文将从浅入深地介绍碰撞运动的互碰形式 碰撞检测 对于互碰形式的碰撞运动来说,首 ...
- JS运动学习笔记 -- 任意值的运动框架(高/宽度,背景颜色,文本内容,透明度等)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- JavaScript 运动(缓冲运动,多物体运动 ,多物体多值运动+回调机制)
匀速运动 (当需要物体做匀速运动直接调用statMove函数) function startMove(dom,targetPosetion){ //dom : 运动对象,targetPositio ...
- HarmonyOS应用开发-Component体系介绍(一)
目录: 1. Component的内部类/接口 2. Component方法简介 3.总结 在HarmonyOS的UI框架中,Component为用户界面提供基本组件,Component类位于ohos ...
- 鸿蒙真的是套壳吗?HarmonyOS应用开发初体验,Java原生和JavaScript的mvvm开发
初探寻鸿蒙os的应用开发 本期视频已发布到bilibili 注意是应用开发,没错码农(应用开发)一般关注这个就行了 IDE 点击应用开发以后,点击链接下载IDE,DevEco Studio是基于IDE ...
随机推荐
- Selenium KPI接口 警告弹 -alert/confirm/promp
应用场景: 有些页面进入后自带弹窗提醒功能需要确认,这时候就需要将焦点定位到alert弹窗上. 使用格式: alert=driver.switchtoalert() alert.accept() 实现 ...
- C系统级编程-复习
数组对象类型 Array of Type,它是多个相同对象类型的一维派生类型,包含两要素:元素个数,元素的对象类型 所谓多维数组,不过是元素的迭代衍生,本质还是一维的 声明 对象标识的名称 对象类型 ...
- 如何编写正确高效的Dockerfile
Dockerfile是什么 Dockerfile 非常普通,它就是一个纯文本,里面记录了一系列的构建指令,比如选择基础镜像.拷贝文件.运行脚本等等,RUN, COPY, ADD指令都会生成一个 Lay ...
- Go语言计算字符串长度——len()和RuneCountInString()
Go 语言的内建函数 len(),可以用来获取切片.字符串.通道(channel)等的长度.下面的代码可以用 len() 来获取字符串的长度. tip1 := "genji is a nin ...
- markdown设置目录、锚点
目录 在编辑时正确使用标题,在段首输入[toc]即可 锚点 创建到命名锚记的链接的过程分为两步: 首先是建立一个跳转的连接: [说明文字](#jump) 然后标记要跳转到什么位置,注意id要与之前(# ...
- Django实战项目-学习任务系统-配置定时调度任务
接着上期代码内容,继续完善优化系统功能. 本次增加配置定时调度任务功能,学习任务系统定时任务管理添加的定时学习任务,需要通过配置调度任务,定时发布周期性的学习任务. 以及每天定时发送学生用户属性值,积 ...
- 云服务器下如何部署Django项目详细操作步骤
前期本人完成了"编写你的第一个 Django 应用程序",有了一个简单的项目代码,在本地window系统自测没问题了,接下来就想办法部署到服务器上,可以通过公网访问我们的Djang ...
- 什么是swagger,一篇带你入门
一.前言 在前后端分离开发的过程中,前端和后端需要进行api对接进行交互,就需要一个api规范文档,方便前后端的交互,但api文档不能根据代码的变化发生实时动态的改变,这样后端修改了接口,前端不能及时 ...
- API开放平台网关需要做什么?
首发于公众号:BiggerBoy 欢迎关注,查看更多技术文章 怎么搭建API开放平台网关? API的全称是应用编程接口(Application Programming Interface),这并不是一 ...
- CSS那些事读书笔记-2
背景 作为一个后端开发,曾经尝试过学习前端,但是总觉不得要领,照猫画虎,而公司里又有专业的前端开发,工作中几乎接触不到实际的前端任务,所以前端的技能田野一直是一片荒芜.但是笔者深知前端的技能对找工作和 ...