
鸿蒙跨端心率异常预警系统开发指南 原创
鸿蒙跨端心率异常预警系统开发指南
一、项目概述
本文基于HarmonyOS的生物传感器能力和分布式技术,开发一套心率异常预警程序。该系统整合PPG传感器降噪技术、实时心率变异性(HRV)分析和低电量简化算法,并借鉴《鸿蒙跨端U同步》中的多设备同步技术,实现心率数据的精准采集、实时分析和多终端预警。
二、系统架构
±--------------------+ ±--------------------+ ±--------------------+
穿戴设备 <-----> 分布式数据总线 <-----> 监控终端
(智能手表/手环) (Distributed Bus) (手机/平板/医护端)
±---------±---------+ ±---------±---------+ ±---------±---------+
±---------v----------+ ±---------v----------+ ±---------v----------+
PPG信号处理模块 心率分析模块 预警同步模块
(降噪/特征提取) (HRV/异常检测) (多设备预警/通知)
±--------------------+ ±--------------------+ ±--------------------+
三、核心代码实现
心率监测服务实现
// src/main/ets/service/HeartRateService.ts
import { distributedData } from ‘@ohos.data.distributedData’;
import { BusinessError } from ‘@ohos.base’;
import { sensor } from ‘@ohos.sensor’;
import { power } from ‘@ohos.power’;
import { notification } from ‘@ohos.notification’;
import { taskpool } from ‘@ohos.taskpool’;
import { fileIo } from ‘@ohos.fileio’;
import { zlib } from ‘@ohos.zlib’;
interface HeartRateData {
timestamp: number;
heartRate: number; // 心率值 (bpm)
hrv: number; // 心率变异性 (ms)
confidence: number; // 数据置信度 0-1
deviceId: string;
isSynced: boolean;
interface HealthAlert {
timestamp: number;
type: ‘high_hr’ ‘low_hr’ ‘abnormal_hrv’
‘arrhythmia’;
heartRate?: number;
hrv?: number;
message: string;
isSynced: boolean;
interface DeviceStatus {
batteryLevel: number;
isLowPowerMode: boolean;
lastSyncTime: number;
syncInterval: number; // 同步间隔(秒)
export class HeartRateService {
private static instance: HeartRateService;
private kvStore: distributedData.KVStore | null = null;
private readonly STORE_ID = ‘heart_rate_store’;
private heartRateSensorId: number = -1;
private ppgSensorId: number = -1;
private hrData: HeartRateData[] = [];
private alerts: HealthAlert[] = [];
private deviceStatus: DeviceStatus = {
batteryLevel: 100,
isLowPowerMode: false,
lastSyncTime: 0,
syncInterval: 300 // 默认5分钟同步一次
};
private rawPPGData: number[] = [];
private readonly MAX_STORAGE_HOURS = 24; // 本地最多存储24小时数据
private constructor() {
this.initKVStore();
this.loadLocalData();
this.checkBatteryStatus();
public static getInstance(): HeartRateService {
if (!HeartRateService.instance) {
HeartRateService.instance = new HeartRateService();
return HeartRateService.instance;
private async initKVStore(): Promise<void> {
try {
const options: distributedData.KVManagerConfig = {
bundleName: 'com.example.heartrate',
userInfo: {
userId: '0',
userType: distributedData.UserType.SAME_USER_ID
};
const kvManager = distributedData.createKVManager(options);
this.kvStore = await kvManager.getKVStore({
storeId: this.STORE_ID,
options: {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: true,
kvStoreType: distributedData.KVStoreType.SINGLE_VERSION
});
this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_REMOTE, (data) => {
this.handleRemoteDataChange(data);
});
catch (e) {
console.error(Failed to initialize KVStore. Code: {e.code}, message: {e.message});
}
private async loadLocalData(): Promise<void> {
try {
// 加载心率数据
const hrFile = await fileIo.open(‘data/heart_rate_data.bin’, 0o666);
const hrData = await fileIo.read(hrFile.fd, new ArrayBuffer(0));
await fileIo.close(hrFile.fd);
if (hrData) {
const decompressed = await zlib.deflateSync(hrData);
this.hrData = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(decompressed)));
// 加载报警记录
const alertFile = await fileIo.open('data/health_alerts.bin', 0o666);
const alertData = await fileIo.read(alertFile.fd, new ArrayBuffer(0));
await fileIo.close(alertFile.fd);
if (alertData) {
const decompressed = await zlib.deflateSync(alertData);
this.alerts = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(decompressed)));
// 加载设备状态
const statusFile = await fileIo.open('data/device_status.bin', 0o666);
const statusData = await fileIo.read(statusFile.fd, new ArrayBuffer(0));
await fileIo.close(statusFile.fd);
if (statusData) {
const decompressed = await zlib.deflateSync(statusData);
this.deviceStatus = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(decompressed)));
} catch (e) {
console.log('No local data found or error reading file');
// 清理过期数据
this.cleanupOldData();
private cleanupOldData(): void {
const now = Date.now();
const cutoff = now - this.MAX_STORAGE_HOURS 60 60 * 1000;
this.hrData = this.hrData.filter(d => d.timestamp >= cutoff);
this.alerts = this.alerts.filter(a => a.timestamp >= cutoff);
private async saveLocalData(): Promise<void> {
try {
// 确保目录存在
await fileIo.mkdir('data');
// 保存心率数据
const hrStr = JSON.stringify(this.hrData);
const hrCompressed = await zlib.inflateSync(new Uint8Array(hrStr.split('').map(c => c.charCodeAt(0))));
const hrFile = await fileIo.open('data/heart_rate_data.bin', 0o666 | fileIo.OpenMode.CREATE);
await fileIo.write(hrFile.fd, hrCompressed.buffer);
await fileIo.close(hrFile.fd);
// 保存报警记录
const alertStr = JSON.stringify(this.alerts);
const alertCompressed = await zlib.inflateSync(new Uint8Array(alertStr.split('').map(c => c.charCodeAt(0))));
const alertFile = await fileIo.open('data/health_alerts.bin', 0o666 | fileIo.OpenMode.CREATE);
await fileIo.write(alertFile.fd, alertCompressed.buffer);
await fileIo.close(alertFile.fd);
// 保存设备状态
const statusStr = JSON.stringify(this.deviceStatus);
const statusCompressed = await zlib.inflateSync(new Uint8Array(statusStr.split('').map(c => c.charCodeAt(0))));
const statusFile = await fileIo.open('data/device_status.bin', 0o666 | fileIo.OpenMode.CREATE);
await fileIo.write(statusFile.fd, statusCompressed.buffer);
await fileIo.close(statusFile.fd);
catch (e) {
console.error(Failed to save local data. Code: {e.code}, message: {e.message});
}
private async checkBatteryStatus(): Promise<void> {
try {
const batteryInfo = await power.getBatteryInfo();
this.deviceStatus.batteryLevel = batteryInfo.batterySoc;
// 低电量模式切换
const shouldLowPower = batteryInfo.batterySoc < 20;
if (shouldLowPower !== this.deviceStatus.isLowPowerMode) {
this.deviceStatus.isLowPowerMode = shouldLowPower;
this.adjustMonitoringStrategy();
// 低电量时延长同步间隔
this.deviceStatus.syncInterval = shouldLowPower ? 600 : 300;
catch (e) {
console.error(Failed to get battery info. Code: {e.code}, message: {e.message});
}
private adjustMonitoringStrategy(): void {
if (this.deviceStatus.isLowPowerMode) {
// 低电量模式: 降低采样频率,简化算法
if (this.heartRateSensorId !== -1) {
sensor.off(this.heartRateSensorId);
this.heartRateSensorId = -1;
if (this.ppgSensorId !== -1) {
sensor.off(this.ppgSensorId);
this.ppgSensorId = -1;
this.startLowPowerMonitoring();
else {
// 正常模式: 高精度监测
if (this.heartRateSensorId !== -1) {
sensor.off(this.heartRateSensorId);
this.heartRateSensorId = -1;
if (this.ppgSensorId !== -1) {
sensor.off(this.ppgSensorId);
this.ppgSensorId = -1;
this.startHighAccuracyMonitoring();
}
public async startHighAccuracyMonitoring(): Promise<boolean> {
try {
// 启用高精度心率传感器
this.heartRateSensorId = await sensor.on(sensor.SensorId.HEART_RATE, {
interval: sensor.SensorFrequency.SENSOR_DELAY_FASTEST,
callback: (data) => {
this.handleHeartRateData(data);
});
// 启用PPG传感器进行原始信号采集
this.ppgSensorId = await sensor.on(sensor.SensorId.PPG, {
interval: sensor.SensorFrequency.SENSOR_DELAY_GAME,
callback: (data) => {
this.handlePPGData(data);
});
// 启用高性能模式
await power.enablePowerMode(power.PowerMode.HIGH_PERFORMANCE, 'Heart rate monitoring');
return true;
catch (e) {
console.error(Failed to start high accuracy monitoring. Code: {e.code}, message: {e.message});
return false;
}
public async startLowPowerMonitoring(): Promise<boolean> {
try {
// 低电量模式仅使用心率传感器,降低采样频率
this.heartRateSensorId = await sensor.on(sensor.SensorId.HEART_RATE, {
interval: sensor.SensorFrequency.SENSOR_DELAY_NORMAL,
callback: (data) => {
this.handleHeartRateData(data);
});
// 启用低功耗模式
await power.enablePowerMode(power.PowerMode.LOW_POWER, 'Low power heart rate monitoring');
return true;
catch (e) {
console.error(Failed to start low power monitoring. Code: {e.code}, message: {e.message});
return false;
}
private async handleHeartRateData(data: sensor.HeartRateResponse): Promise<void> {
const now = Date.now();
// 简单滤波 - 去除异常值
if (data.heartRate < 30 || data.heartRate > 220) {
return; // 忽略明显不合理的心率值
// 计算HRV (简化版)
let hrv = 0;
if (this.hrData.length > 0) {
const lastHR = this.hrData[this.hrData.length - 1];
const interval = now - lastHR.timestamp;
hrv = Math.abs(interval - 60000 / data.heartRate); // 计算与预期间隔的差值
const newData: HeartRateData = {
timestamp: now,
heartRate: data.heartRate,
hrv,
confidence: data.confidence,
deviceId: this.getDeviceId(),
isSynced: false
};
this.hrData.push(newData);
// 心率异常检测
this.checkForAbnormalities(newData);
// 保存并同步数据
await this.saveLocalData();
if (now - this.deviceStatus.lastSyncTime > this.deviceStatus.syncInterval * 1000) {
await this.syncData();
this.deviceStatus.lastSyncTime = now;
}
private async handlePPGData(data: sensor.PPGResponse): Promise<void> {
// 保存原始PPG数据用于降噪处理
this.rawPPGData.push(…data.samples);
// 当积累足够数据时进行处理
if (this.rawPPGData.length >= 500) { // 约5秒数据(假设100Hz采样率)
// 使用任务池并行处理PPG信号
const task = new taskpool.Task(this.processPPGDataTask, this.rawPPGData.slice(-500));
const processedData = await taskpool.execute(task) as { heartRate: number, hrv: number };
// 如果高精度模式且PPG分析结果可信,更新心率数据
if (!this.deviceStatus.isLowPowerMode && processedData.heartRate > 0) {
const now = Date.now();
const newData: HeartRateData = {
timestamp: now,
heartRate: processedData.heartRate,
hrv: processedData.hrv,
confidence: 0.9, // PPG分析通常有较高置信度
deviceId: this.getDeviceId(),
isSynced: false
};
this.hrData.push(newData);
this.checkForAbnormalities(newData);
// 保留最近100个样本作为重叠
this.rawPPGData = this.rawPPGData.slice(-100);
}
private processPPGDataTask(rawData: number[]): { heartRate: number, hrv: number } {
// PPG信号处理算法 (简化版)
// 实际应用中应实现完整的降噪和特征提取算法
// 1. 降噪处理 - 移动平均滤波
const windowSize = 5;
const smoothedData = [];
for (let i = 0; i < rawData.length - windowSize; i++) {
let sum = 0;
for (let j = 0; j < windowSize; j++) {
sum += rawData[i + j];
smoothedData.push(sum / windowSize);
// 2. 峰值检测
const peaks = [];
for (let i = 1; i < smoothedData.length - 1; i++) {
if (smoothedData[i] > smoothedData[i - 1] && smoothedData[i] > smoothedData[i + 1]) {
peaks.push(i);
}
// 3. 计算心率和HRV
if (peaks.length >= 2) {
const intervals = [];
for (let i = 1; i < peaks.length; i++) {
intervals.push(peaks[i] - peaks[i - 1]);
const avgInterval = intervals.reduce((sum, val) => sum + val, 0) / intervals.length;
const heartRate = 60000 / (avgInterval * 10); // 假设100Hz采样率
// 计算HRV (RR间期标准差)
const squaredDiffs = intervals.map(i => Math.pow(i - avgInterval, 2));
const variance = squaredDiffs.reduce((sum, val) => sum + val, 0) / intervals.length;
const hrv = Math.sqrt(variance);
return { heartRate, hrv };
return { heartRate: 0, hrv: 0 };
private checkForAbnormalities(data: HeartRateData): void {
// 心率异常检测规则
const now = new Date(data.timestamp);
const timeStr = now.toLocaleTimeString();
// 1. 高心率检测 (>100 bpm)
if (data.heartRate > 100) {
const alert: HealthAlert = {
timestamp: data.timestamp,
type: 'high_hr',
heartRate: data.heartRate,
message: 高心率预警: {data.heartRate} bpm ({timeStr}),
isSynced: false
};
this.alerts.push(alert);
this.triggerAlert(alert);
// 2. 低心率检测 (<50 bpm)
else if (data.heartRate < 50) {
const alert: HealthAlert = {
timestamp: data.timestamp,
type: 'low_hr',
heartRate: data.heartRate,
message: 低心率预警: {data.heartRate} bpm ({timeStr}),
isSynced: false
};
this.alerts.push(alert);
this.triggerAlert(alert);
// 3. 异常HRV检测 (>50ms 或 <10ms)
if (data.hrv > 50 || (data.hrv > 0 && data.hrv < 10)) {
const alert: HealthAlert = {
timestamp: data.timestamp,
type: 'abnormal_hrv',
hrv: data.hrv,
message: 心率变异性异常: {data.hrv.toFixed(1)} ms ({timeStr}),
isSynced: false
};
this.alerts.push(alert);
this.triggerAlert(alert);
// 4. 心律失常检测 (简化版)
if (this.hrData.length >= 3) {
const last3 = this.hrData.slice(-3);
const diffs = [];
for (let i = 1; i < last3.length; i++) {
diffs.push(Math.abs(last3[i].heartRate - last3[i - 1].heartRate));
const avgDiff = diffs.reduce((sum, val) => sum + val, 0) / diffs.length;
if (avgDiff > 15) { // 相邻心率差值平均超过15 bpm
const alert: HealthAlert = {
timestamp: data.timestamp,
type: 'arrhythmia',
message: 疑似心律失常 (${timeStr}),
isSynced: false
};
this.alerts.push(alert);
this.triggerAlert(alert);
}
private async triggerAlert(alert: HealthAlert): Promise<void> {
// 发送本地通知
try {
await notification.publish({
id: 1,
contentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: '心率异常预警',
text: alert.message,
additionalText: new Date(alert.timestamp).toLocaleTimeString()
});
catch (e) {
console.error(Failed to publish notification. Code: {e.code}, message: {e.message});
// 同步报警信息
await this.syncData();
private getDeviceId(): string {
// 实际应用中应获取真实设备ID
return 'hr_monitor_' + Math.random().toString(36).substr(2, 9);
private async syncData(): Promise<void> {
if (!this.kvStore) return;
try {
// 同步心率数据
const unsyncedHR = this.hrData.filter(d => !d.isSynced);
if (unsyncedHR.length > 0) {
await this.kvStore.put('heart_rate_data', { value: unsyncedHR });
this.hrData.forEach(d => {
if (!d.isSynced) d.isSynced = true;
});
// 同步报警记录
const unsyncedAlerts = this.alerts.filter(a => !a.isSynced);
if (unsyncedAlerts.length > 0) {
await this.kvStore.put('health_alerts', { value: unsyncedAlerts });
this.alerts.forEach(a => {
if (!a.isSynced) a.isSynced = true;
});
// 同步设备状态
await this.kvStore.put('device_status', { value: this.deviceStatus });
catch (e) {
console.error(Failed to sync data. Code: {e.code}, message: {e.message});
}
private handleRemoteDataChange(data: distributedData.ChangeData): void {
data.insertEntries.forEach((entry: distributedData.Entry) => {
if (entry.key === ‘heart_rate_data’) {
const remoteHR = entry.value.value as HeartRateData[];
this.mergeHeartRateData(remoteHR);
else if (entry.key === ‘health_alerts’) {
const remoteAlerts = entry.value.value as HealthAlert[];
this.mergeHealthAlerts(remoteAlerts);
else if (entry.key === ‘device_status’) {
const remoteStatus = entry.value.value as DeviceStatus;
this.mergeDeviceStatus(remoteStatus);
});
private mergeHeartRateData(remoteData: HeartRateData[]): void {
remoteData.forEach(remote => {
const existing = this.hrData.find(local =>
local.timestamp === remote.timestamp &&
local.deviceId === remote.deviceId
);
if (!existing) {
this.hrData.push(remote);
else {
// 合并策略:保留更高置信度的数据
if (remote.confidence > existing.confidence) {
existing.heartRate = remote.heartRate;
existing.hrv = remote.hrv;
existing.confidence = remote.confidence;
}
});
// 按时间排序
this.hrData.sort((a, b) => a.timestamp - b.timestamp);
private mergeHealthAlerts(remoteAlerts: HealthAlert[]): void {
remoteAlerts.forEach(remote => {
const existing = this.alerts.find(local =>
local.timestamp === remote.timestamp &&
local.type === remote.type
);
if (!existing) {
this.alerts.push(remote);
});
// 按时间排序
this.alerts.sort((a, b) => a.timestamp - b.timestamp);
private mergeDeviceStatus(remoteStatus: DeviceStatus): void {
// 采用更保守的低电量模式
if (remoteStatus.isLowPowerMode && !this.deviceStatus.isLowPowerMode) {
this.deviceStatus.isLowPowerMode = true;
this.adjustMonitoringStrategy();
// 采用更短的同步间隔
this.deviceStatus.syncInterval = Math.min(
this.deviceStatus.syncInterval,
remoteStatus.syncInterval
);
public getRecentHeartRate(count: number = 10): HeartRateData[] {
return this.hrData.slice(-count).reverse();
public getRecentAlerts(count: number = 5): HealthAlert[] {
return this.alerts.slice(-count).reverse();
public getCurrentStatus(): DeviceStatus {
return this.deviceStatus;
public async stopMonitoring(): Promise<void> {
if (this.heartRateSensorId !== -1) {
await sensor.off(this.heartRateSensorId);
this.heartRateSensorId = -1;
if (this.ppgSensorId !== -1) {
await sensor.off(this.ppgSensorId);
this.ppgSensorId = -1;
await power.disablePowerMode(power.PowerMode.HIGH_PERFORMANCE);
await power.disablePowerMode(power.PowerMode.LOW_POWER);
await this.saveLocalData();
await this.syncData();
public async destroy(): Promise<void> {
await this.stopMonitoring();
if (this.kvStore) {
this.kvStore.off('dataChange');
}
心率监控组件实现
// src/main/ets/components/HeartRateMonitor.ets
@Component
export struct HeartRateMonitor {
private hrService = HeartRateService.getInstance();
@State heartRateData: HeartRateData[] = [];
@State healthAlerts: HealthAlert[] = [];
@State deviceStatus: DeviceStatus = {
batteryLevel: 100,
isLowPowerMode: false,
lastSyncTime: 0,
syncInterval: 300
};
@State isMonitoring: boolean = false;
private timer: number = 0;
aboutToAppear(): void {
this.loadData();
this.startMonitoring();
this.startAutoRefresh();
aboutToDisappear(): void {
this.stopAutoRefresh();
private loadData(): void {
this.heartRateData = this.hrService.getRecentHeartRate();
this.healthAlerts = this.hrService.getRecentAlerts();
this.deviceStatus = this.hrService.getCurrentStatus();
private async startMonitoring(): Promise<void> {
this.isMonitoring = await this.hrService.startHighAccuracyMonitoring();
private startAutoRefresh(): void {
this.timer = setInterval(() => {
this.loadData();
}, 10000); // 每10秒刷新一次
private stopAutoRefresh(): void {
if (this.timer) {
clearInterval(this.timer);
this.timer = 0;
}
build() {
Column() {
// 标题
Text(‘心率异常预警系统’)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 });
// 状态卡片
this.buildStatusCard();
// 心率图表
Text('实时心率趋势')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 10 });
this.buildHeartRateChart();
// 报警记录
Text('健康预警记录')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 10 });
if (this.healthAlerts.length > 0) {
this.buildAlertList();
else {
Text('暂无预警记录')
.fontSize(14)
.fontColor('#666666');
}
.width('100%')
.height('100%')
.padding(20);
@Builder
private buildStatusCard() {
Column() {
// 当前心率
Row() {
Image($r(‘app.media.ic_heart’))
.width(24)
.height(24)
.margin({ right: 10 });
Column() {
Text('当前心率')
.fontSize(14)
.fontColor('#666666');
if (this.heartRateData.length > 0) {
Text(${this.heartRateData[0].heartRate} bpm)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor(this.getHeartRateColor(this.heartRateData[0].heartRate));
Text(HRV: ${this.heartRateData[0].hrv.toFixed(1)} ms)
.fontSize(14)
.fontColor('#666666');
else {
Text('-- bpm')
.fontSize(24)
.fontWeight(FontWeight.Bold);
}
.layoutWeight(1);
.margin({ bottom: 15 });
// 设备状态
Row() {
Image($r('app.media.ic_battery'))
.width(24)
.height(24)
.margin({ right: 10 });
Column() {
Text('设备状态')
.fontSize(14)
.fontColor('#666666');
Row() {
Text(电量: ${this.deviceStatus.batteryLevel}%)
.fontSize(16);
Text(this.deviceStatus.isLowPowerMode ? ' (低电量模式)' : '')
.fontSize(16)
.fontColor('#FF9800');
}
.layoutWeight(1);
}
.width('100%')
.padding(15)
.backgroundColor('#FFFFFF')
.borderRadius(10)
.shadow({ radius: 5, color: '#E0E0E0', offsetX: 0, offsetY: 2 });
private getHeartRateColor(hr: number): string {
if (hr < 50) return '#2196F3'; // 低心率 - 蓝色
if (hr <= 100) return '#4CAF50'; // 正常 - 绿色
return '#F44336'; // 高心率 - 红色
@Builder
private buildHeartRateChart() {
// 获取最近5分钟数据
const now = Date.now();
const fiveMinutesAgo = now - 5 60 1000;
const recentData = this.heartRateData.filter(d => d.timestamp >= fiveMinutesAgo);
if (recentData.length === 0) {
return Text('暂无心率数据')
.fontSize(14)
.fontColor('#666666');
// 计算图表参数
const minHR = Math.max(40, Math.min(...recentData.map(d => d.heartRate)) - 10);
const maxHR = Math.min(120, Math.max(...recentData.map(d => d.heartRate)) + 10);
const hrRange = maxHR - minHR;
Column() {
// 图表区域
Stack() {
// 网格线
ForEach(Array.from({ length: 5 }), (_, i) => {
Line()
.width('100%')
.height(1)
.backgroundColor('#E0E0E0')
.position({ x: 0, y: i * 40 });
});
// 心率曲线
Polyline()
.width('100%')
.height(160)
.fillOpacity(0)
.stroke('#E91E63')
.strokeWidth(2)
.points(this.getChartPoints(recentData, minHR, hrRange));
// 数据点
ForEach(recentData, (data, i) => {
if (i % 5 === 0) { // 每5个数据显示一个点
Circle()
.width(8)
.height(8)
.fill('#E91E63')
.position({
x: i * (100 / (recentData.length - 1)) + '%',
y: (maxHR - data.heartRate) * (160 / hrRange) + '%'
});
});
.width(‘100%’)
.height(160);
// X轴标签
Row() {
Text(new Date(recentData[0].timestamp).toLocaleTimeString())
.fontSize(10);
Text(new Date(recentData[recentData.length - 1].timestamp).toLocaleTimeString())
.fontSize(10)
.margin({ left: '80%' });
.width(‘100%’)
.margin({ top: 5 });
// Y轴标签
Row() {
Text(${maxHR} bpm)
.fontSize(10);
Text(${minHR} bpm)
.fontSize(10)
.margin({ left: '85%' });
.width(‘100%’);
}
private getChartPoints(data: HeartRateData[], minHR: number, hrRange: number): Point[] {
return data.map((d, i) => ({
x: i * (100 / (data.length - 1)),
y: (minHR + hrRange - d.heartRate) * (160 / hrRange)
}));
@Builder
private buildAlertList() {
List({ space: 10 }) {
ForEach(this.healthAlerts, (alert) => {
ListItem() {
Column() {
Row() {
Text(this.getAlertTypeText(alert.type))
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(this.getAlertColor(alert.type))
.layoutWeight(1);
Text(new Date(alert.timestamp).toLocaleTimeString())
.fontSize(14)
.fontColor('#666666');
Text(alert.message)
.fontSize(14)
.fontColor('#666666')
.margin({ top: 5 });
.width(‘100%’)
.padding(10)
.borderRadius(10)
.backgroundColor('#FFFFFF')
.shadow({ radius: 3, color: '#E0E0E0', offsetX: 0, offsetY: 1 });
})
.width(‘100%’)
.height('30%')
private getAlertTypeText(type: string): string {
switch (type) {
case 'high_hr': return '⚠️ 高心率';
case 'low_hr': return '⚠️ 低心率';
case 'abnormal_hrv': return '⚠️ HRV异常';
case 'arrhythmia': return '⚠️ 心律失常';
default: return '⚠️ 健康预警';
}
private getAlertColor(type: string): string {
switch (type) {
case ‘high_hr’: return ‘#F44336’;
case ‘low_hr’: return ‘#2196F3’;
case ‘abnormal_hrv’: return ‘#FF9800’;
case ‘arrhythmia’: return ‘#9C27B0’;
default: return ‘#607D8B’;
}
private async toggleMonitoring(): Promise<void> {
if (this.isMonitoring) {
await this.hrService.stopMonitoring();
this.isMonitoring = false;
else {
this.isMonitoring = await this.hrService.startHighAccuracyMonitoring();
}
主界面实现
// src/main/ets/pages/HealthPage.ets
import { HeartRateService } from ‘…/service/HeartRateService’;
import { HeartRateMonitor } from ‘…/components/HeartRateMonitor’;
@Entry
@Component
struct HealthPage {
@State activeTab: number = 0;
private hrService = HeartRateService.getInstance();
build() {
Column() {
// 标题
Text(‘健康监测系统’)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 });
// 标签页
Tabs({ barPosition: BarPosition.Start }) {
TabContent() {
// 心率监测标签页
HeartRateMonitor()
.tabBar(‘心率监测’);
TabContent() {
// 健康报告标签页
this.buildReportTab()
.tabBar(‘健康报告’);
.barWidth(‘100%’)
.barHeight(50)
.width('100%')
.height('80%')
.width(‘100%’)
.height('100%')
.padding(20);
@Builder
private buildReportTab() {
Column() {
Text(‘心率健康报告’)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 });
// 今日统计数据
this.buildDailyStats();
// 心率变异性分析
Text('心率变异性(HRV)分析')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ top: 30, bottom: 10 });
this.buildHRVChart();
.width(‘100%’)
.height('100%')
.padding(20);
@Builder
private buildDailyStats() {
const now = Date.now();
const todayStart = new Date();
todayStart.setHours(0, 0, 0, 0);
const todayData = this.hrService.getRecentHeartRate(1000).filter(d =>
d.timestamp >= todayStart.getTime()
);
if (todayData.length === 0) {
return Text('今日暂无心率数据')
.fontSize(14)
.fontColor('#666666');
const avgHR = todayData.reduce((sum, d) => sum + d.heartRate, 0) / todayData.length;
const minHR = Math.min(...todayData.map(d => d.heartRate));
const maxHR = Math.max(...todayData.map(d => d.heartRate));
const avgHRV = todayData.reduce((sum, d) => sum + d.hrv, 0) / todayData.length;
Column() {
Row() {
Column() {
Text('平均心率')
.fontSize(14)
.fontColor('#666666');
Text(${avgHR.toFixed(0)} bpm)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor(this.getHeartRateColor(avgHR));
.width(‘33%’)
Column() {
Text('最低心率')
.fontSize(14)
.fontColor('#666666');
Text(${minHR} bpm)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#2196F3');
.width(‘33%’)
Column() {
Text('最高心率')
.fontSize(14)
.fontColor('#666666');
Text(${maxHR} bpm)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#F44336');
.width(‘33%’)
.margin({ bottom: 20 });
Row() {
Column() {
Text('平均HRV')
.fontSize(14)
.fontColor('#666666');
Text(${avgHRV.toFixed(1)} ms)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor(avgHRV > 50 || avgHRV < 10 ? '#FF9800' : '#4CAF50');
Column() {
Text('预警次数')
.fontSize(14)
.fontColor('#666666');
Text(${this.hrService.getRecentAlerts(1000).filter(a =>
a.timestamp >= todayStart.getTime()
).length})
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#F44336');
.margin({ left: 20 });
}
.width('100%')
.padding(15)
.backgroundColor('#FFFFFF')
.borderRadius(10)
.shadow({ radius: 5, color: '#E0E0E0', offsetX: 0, offsetY: 2 });
private getHeartRateColor(hr: number): string {
if (hr < 50) return '#2196F3'; // 低心率 - 蓝色
if (hr <= 100) return '#4CAF50'; // 正常 - 绿色
return '#F44336'; // 高心率 - 红色
@Builder
private buildHRVChart() {
// 获取最近24小时数据 (每小时一个点)
const now = Date.now();
const twentyFourHoursAgo = now - 24 60 60 * 1000;
const allData = this.hrService.getRecentHeartRate(1000).filter(d =>
d.timestamp >= twentyFourHoursAgo
);
if (allData.length === 0) {
return Text('暂无HRV数据')
.fontSize(14)
.fontColor('#666666');
// 按小时分组计算平均HRV
const hourlyData: Record<string, number[]> = {};
allData.forEach(data => {
const hour = new Date(data.timestamp).getHours();
const key = ${hour}:00;
if (!hourlyData[key]) {
hourlyData[key] = [];
hourlyData[key].push(data.hrv);
});
const chartData = Object.entries(hourlyData).map(([hour, hrvValues]) => ({
hour,
avgHRV: hrvValues.reduce((sum, val) => sum + val, 0) / hrvValues.length
}));
// 构建柱状图
Row({ space: 15 }) {
ForEach(chartData, (data) => {
Column() {
// HRV柱状图
Column() {
Blank()
.height(data.avgHRV * 2) // 放大显示
.width(20)
.backgroundColor(data.avgHRV > 50 || data.avgHRV < 10 ? '#FF9800' : '#4CAF50')
.height(100)
.justifyContent(FlexAlign.End);
// 时间标签
Text(data.hour)
.fontSize(10)
.margin({ top: 5 });
})
.width(‘100%’)
.height(150)
.margin({ top: 10 });
}
四、与游戏同步技术的结合点
实时状态同步:借鉴游戏中玩家状态实时同步机制,优化心率数据的跨设备同步
事件广播机制:类似游戏中的事件广播,实现心率异常预警的快速扩散
数据压缩传输:使用类似游戏中的网络优化技术,对PPG数据进行高效压缩传输
设备角色分配:参考游戏中的主机/客户端模式,确定主监测设备和从属设备
状态一致性保障:借鉴游戏中的状态同步机制,确保多设备间预警状态一致
五、关键特性实现
PPG传感器降噪处理:
private processPPGDataTask(rawData: number[]): { heartRate: number, hrv: number } {
// 1. 降噪处理 - 移动平均滤波
const windowSize = 5;
const smoothedData = [];
for (let i = 0; i < rawData.length - windowSize; i++) {
let sum = 0;
for (let j = 0; j < windowSize; j++) {
sum += rawData[i + j];
smoothedData.push(sum / windowSize);
// 2. 峰值检测
const peaks = [];
for (let i = 1; i < smoothedData.length - 1; i++) {
if (smoothedData[i] > smoothedData[i - 1] && smoothedData[i] > smoothedData[i + 1]) {
peaks.push(i);
}
// 3. 计算心率和HRV
if (peaks.length >= 2) {
const intervals = [];
for (let i = 1; i < peaks.length; i++) {
intervals.push(peaks[i] - peaks[i - 1]);
const avgInterval = intervals.reduce((sum, val) => sum + val, 0) / intervals.length;
const heartRate = 60000 / (avgInterval * 10); // 假设100Hz采样率
// 计算HRV (RR间期标准差)
const squaredDiffs = intervals.map(i => Math.pow(i - avgInterval, 2));
const variance = squaredDiffs.reduce((sum, val) => sum + val, 0) / intervals.length;
const hrv = Math.sqrt(variance);
return { heartRate, hrv };
return { heartRate: 0, hrv: 0 };
实时心率变异性分析:
private async handleHeartRateData(data: sensor.HeartRateResponse): Promise<void> {
const now = Date.now();
// 计算HRV (简化版)
let hrv = 0;
if (this.hrData.length > 0) {
const lastHR = this.hrData[this.hrData.length - 1];
const interval = now - lastHR.timestamp;
hrv = Math.abs(interval - 60000 / data.heartRate); // 计算与预期间隔的差值
const newData: HeartRateData = {
timestamp: now,
heartRate: data.heartRate,
hrv,
confidence: data.confidence,
deviceId: this.getDeviceId(),
isSynced: false
};
this.hrData.push(newData);
// 心率异常检测
this.checkForAbnormalities(newData);
低电量简化算法:
public async startLowPowerMonitoring(): Promise<boolean> {
try {
// 低电量模式仅使用心率传感器,降低采样频率
this.heartRateSensorId = await sensor.on(sensor.SensorId.HEART_RATE, {
interval: sensor.SensorFrequency.SENSOR_DELAY_NORMAL,
callback: (data) => {
this.handleHeartRateData(data);
});
// 启用低功耗模式
await power.enablePowerMode(power.PowerMode.LOW_POWER, 'Low power heart rate monitoring');
return true;
catch (e) {
console.error(Failed to start low power monitoring. Code: {e.code}, message: {e.message});
return false;
}
异常心率检测:
private checkForAbnormalities(data: HeartRateData): void {
// 心率异常检测规则
const now = new Date(data.timestamp);
const timeStr = now.toLocaleTimeString();
// 1. 高心率检测 (>100 bpm)
if (data.heartRate > 100) {
const alert: HealthAlert = {
timestamp: data.timestamp,
type: 'high_hr',
heartRate: data.heartRate,
message: 高心率预警: {data.heartRate} bpm ({timeStr}),
isSynced: false
};
this.alerts.push(alert);
this.triggerAlert(alert);
// 2. 低心率检测 (<50 bpm)
else if (data.heartRate < 50) {
const alert: HealthAlert = {
timestamp: data.timestamp,
type: 'low_hr',
heartRate: data.heartRate,
message: 低心率预警: {data.heartRate} bpm ({timeStr}),
isSynced: false
};
this.alerts.push(alert);
this.triggerAlert(alert);
// 3. 异常HRV检测 (>50ms 或 <10ms)
if (data.hrv > 50 || (data.hrv > 0 && data.hrv < 10)) {
const alert: HealthAlert = {
timestamp: data.timestamp,
type: 'abnormal_hrv',
hrv: data.hrv,
message: 心率变异性异常: {data.hrv.toFixed(1)} ms ({timeStr}),
isSynced: false
};
this.alerts.push(alert);
this.triggerAlert(alert);
}
六、性能优化策略
智能数据同步:
// 只有新数据时才触发同步
if (now - this.deviceStatus.lastSyncTime > this.deviceStatus.syncInterval * 1000) {
await this.syncData();
this.deviceStatus.lastSyncTime = now;
本地缓存优先:
public getRecentHeartRate(count: number = 10): HeartRateData[] {
// 先从内存缓存读取
return this.hrData.slice(-count).reverse();
并行计算优化:
// 使用任务池并行处理PPG信号
const task = new taskpool.Task(this.processPPGDataTask, this.rawPPGData.slice(-500));
const processedData = await taskpool.execute(task) as { heartRate: number, hrv: number };
资源释放管理:
public async destroy(): Promise<void> {
await this.stopMonitoring();
if (this.kvStore) {
this.kvStore.off('dataChange');
}
七、项目扩展方向
ECG心电图集成:增加心电图监测功能,提供更全面的心脏健康分析
血压估算:基于PPG信号实现无创血压估算
睡眠分析:结合心率变异性进行睡眠质量评估
云端健康档案:建立个人健康档案,长期跟踪心率变化趋势
紧急联系人通知:在检测到严重心律失常时自动通知紧急联系人
八、总结
本文实现的心率异常预警系统具有以下特点:
采用先进的PPG信号降噪技术,提高心率监测精度
实时分析心率变异性,早期发现心脏健康问题
智能低电量模式切换,平衡性能与续航
基于分布式数据同步,实现多终端实时预警
提供直观的数据可视化和完整的健康报告功能
该应用展示了HarmonyOS在健康监测领域的强大能力,特别是在生物信号处理、低功耗控制和多设备协同方面的优势。通过借鉴游戏同步技术,实现了高效可靠的健康预警机制,为个人健康监护提供了完整的解决方案。
