
鸿蒙跨端老人跌倒检测系统开发指南 原创
鸿蒙跨端老人跌倒检测系统开发指南
一、项目概述
本文基于HarmonyOS的传感器管理、分布式能力和位置服务,开发一套老人跌倒检测系统。该系统整合高精度姿态识别算法、多级预警策略和GPS低功耗模式,并借鉴《鸿蒙跨端U同步》中的多设备同步技术,实现跌倒事件的精准检测、多级响应和跨设备协同预警。
二、系统架构
±--------------------+ ±--------------------+ ±--------------------+
穿戴设备 <-----> 分布式数据总线 <-----> 监护终端
(智能手表/手环) (Distributed Bus) (手机/平板/医护端)
±---------±---------+ ±---------±---------+ ±---------±---------+
±---------v----------+ ±---------v----------+ ±---------v----------+
跌倒检测模块 预警管理模块 位置服务模块
(姿态识别/算法) (多级响应/通知) (GPS/低功耗)
±--------------------+ ±--------------------+ ±--------------------+
三、核心代码实现
跌倒检测服务实现
// src/main/ets/service/FallDetectionService.ts
import { distributedData } from ‘@ohos.data.distributedData’;
import { BusinessError } from ‘@ohos.base’;
import { sensor } from ‘@ohos.sensor’;
import { geoLocationManager } from ‘@ohos.geoLocationManager’;
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 FallEvent {
timestamp: number;
confidence: number; // 置信度 0-1
position: {
latitude: number;
longitude: number;
accuracy: number;
};
deviceId: string;
isConfirmed: boolean; // 是否已确认
isSynced: boolean;
interface DeviceStatus {
lastFallTime: number;
batteryLevel: number;
isLowPowerMode: boolean;
checkInterval: number; // 检测间隔(ms)
syncInterval: number; // 同步间隔(ms)
export class FallDetectionService {
private static instance: FallDetectionService;
private kvStore: distributedData.KVStore | null = null;
private readonly STORE_ID = ‘fall_detection_store’;
private accelSensorId: number = -1;
private gyroSensorId: number = -1;
private fallEvents: FallEvent[] = [];
private deviceStatus: DeviceStatus = {
lastFallTime: 0,
batteryLevel: 100,
isLowPowerMode: false,
checkInterval: 100, // 默认100ms检测一次
syncInterval: 300000 // 5分钟同步一次
};
private lastLocation: geoLocationManager.Location | null = null;
private lastSyncTime: number = 0;
private constructor() {
this.initKVStore();
this.loadLocalData();
this.startLocationUpdates();
public static getInstance(): FallDetectionService {
if (!FallDetectionService.instance) {
FallDetectionService.instance = new FallDetectionService();
return FallDetectionService.instance;
private async initKVStore(): Promise<void> {
try {
const options: distributedData.KVManagerConfig = {
bundleName: 'com.example.falldetection',
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 eventFile = await fileIo.open(‘data/fall_events.bin’, 0o666);
const eventData = await fileIo.read(eventFile.fd, new ArrayBuffer(0));
await fileIo.close(eventFile.fd);
if (eventData) {
const decompressed = await zlib.deflateSync(eventData);
this.fallEvents = 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');
}
private async saveLocalData(): Promise<void> {
try {
// 确保目录存在
await fileIo.mkdir(‘data’);
// 保存跌倒事件
const eventStr = JSON.stringify(this.fallEvents);
const eventCompressed = await zlib.inflateSync(new Uint8Array(eventStr.split('').map(c => c.charCodeAt(0))));
const eventFile = await fileIo.open('data/fall_events.bin', 0o666 | fileIo.OpenMode.CREATE);
await fileIo.write(eventFile.fd, eventCompressed.buffer);
await fileIo.close(eventFile.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});
}
public async startMonitoring(): Promise<boolean> {
try {
// 根据电池状态调整检测频率
await this.adjustCheckInterval();
// 启动加速度计
this.accelSensorId = await sensor.on(sensor.SensorId.ACCELEROMETER, {
interval: this.deviceStatus.checkInterval,
callback: (data) => {
this.handleAccelData(data);
});
// 启动陀螺仪
this.gyroSensorId = await sensor.on(sensor.SensorId.GYROSCOPE, {
interval: this.deviceStatus.checkInterval,
callback: (data) => {
this.handleGyroData(data);
});
return true;
catch (e) {
console.error(Failed to start monitoring. Code: {e.code}, message: {e.message});
return false;
}
private async adjustCheckInterval(): Promise<void> {
// 检查电池状态
const batteryInfo = await power.getBatteryInfo();
this.deviceStatus.batteryLevel = batteryInfo.batterySoc;
// 根据电量调整检测频率
if (batteryInfo.batterySoc < 20) {
this.deviceStatus.isLowPowerMode = true;
this.deviceStatus.checkInterval = 500; // 低电量模式降低检测频率
else {
this.deviceStatus.isLowPowerMode = false;
this.deviceStatus.checkInterval = 100; // 正常检测频率
await this.saveLocalData();
private async startLocationUpdates(): Promise<void> {
try {
// 检查位置权限
const granted = await geoLocationManager.requestPermission();
if (!granted) {
console.log('Location permission not granted');
return;
// 配置位置请求参数
const requestInfo: geoLocationManager.LocationRequest = {
priority: geoLocationManager.LocationRequestPriority.FIRST_FIX, // 首次快速定位
scenario: geoLocationManager.LocationRequestScenario.UNSET,
timeInterval: 300, // 5分钟更新一次
distanceInterval: 100, // 移动超过100米更新
maxAccuracy: 50 // 最大精度50米
};
// 注册位置变化回调
geoLocationManager.on('locationChange', requestInfo, (location) => {
this.handleLocationChange(location);
});
// 立即获取一次位置
this.lastLocation = await geoLocationManager.getCurrentLocation({
priority: geoLocationManager.LocationRequestPriority.FIRST_FIX,
scenario: geoLocationManager.LocationRequestScenario.UNSET,
maxAccuracy: 50
});
catch (e) {
console.error(Failed to start location updates. Code: {e.code}, message: {e.message});
}
private async handleAccelData(data: sensor.AccelerometerResponse): Promise<void> {
const now = Date.now();
// 检查是否短时间内连续触发
if (now - this.deviceStatus.lastFallTime < 30000) { // 30秒内不重复检测
return;
// 使用任务池并行分析传感器数据
const task = new taskpool.Task(this.detectFallTask, data, this.lastGyroData);
const result = await taskpool.execute(task) as { isFall: boolean, confidence: number };
if (result.isFall) {
// 记录跌倒事件
const newEvent: FallEvent = {
timestamp: now,
confidence: result.confidence,
position: this.lastLocation ? {
latitude: this.lastLocation.latitude,
longitude: this.lastLocation.longitude,
accuracy: this.lastLocation.accuracy
deviceId: this.getDeviceId(),
isConfirmed: false,
isSynced: false
};
this.fallEvents.push(newEvent);
this.deviceStatus.lastFallTime = now;
// 根据置信度触发不同级别的预警
this.triggerAlert(newEvent);
// 保存并同步数据
await this.saveLocalData();
if (now - this.lastSyncTime > this.deviceStatus.syncInterval) {
await this.syncData();
this.lastSyncTime = now;
}
private detectFallTask(accelData: sensor.AccelerometerResponse, gyroData: sensor.GyroscopeResponse):
isFall: boolean, confidence: number } {
// 简化的跌倒检测算法 (实际应用中应使用更复杂的算法)
// 1. 计算加速度幅值
const accelMagnitude = Math.sqrt(
accelData.x * accelData.x +
accelData.y * accelData.y +
accelData.z * accelData.z
);
// 2. 计算角速度幅值
const gyroMagnitude = gyroData ? Math.sqrt(
gyroData.x * gyroData.x +
gyroData.y * gyroData.y +
gyroData.z * gyroData.z
) : 0;
// 3. 检测自由落体 (加速度接近0)
const isFreeFall = accelMagnitude < 1.5; // 小于1.5g
// 4. 检测撞击 (高加速度变化)
const isImpact = accelMagnitude > 3.0; // 大于3g
// 5. 检测静止 (低角速度)
const isStill = gyroMagnitude < 0.5; // 小于0.5 rad/s
// 综合判断是否为跌倒事件
if (isFreeFall && isImpact && isStill) {
return { isFall: true, confidence: 0.9 }; // 高置信度
else if (isImpact && isStill) {
return { isFall: true, confidence: 0.7 }; // 中等置信度
else if (isFreeFall && isImpact) {
return { isFall: true, confidence: 0.5 }; // 低置信度
return { isFall: false, confidence: 0 };
private async triggerAlert(event: FallEvent): Promise<void> {
// 多级预警策略
if (event.confidence > 0.8) {
// 高置信度 - 立即通知紧急联系人
await this.sendEmergencyAlert(event);
else if (event.confidence > 0.6) {
// 中等置信度 - 发送确认请求
await this.sendConfirmationRequest(event);
else {
// 低置信度 - 仅记录事件
console.log(Low confidence fall event detected: ${event.confidence});
}
private async sendEmergencyAlert(event: FallEvent): Promise<void> {
const message = 检测到老人跌倒 (置信度: ${(event.confidence * 100).toFixed(0)}%);
try {
// 发送本地通知
await notification.publish({
id: 1,
contentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: '紧急: 跌倒检测',
text: message,
additionalText: new Date(event.timestamp).toLocaleTimeString()
});
// 通过分布式数据总线广播预警
if (this.kvStore) {
await this.kvStore.put('emergency_alert', {
value: {
...event,
message,
alertType: 'emergency'
});
} catch (e) {
console.error(Failed to send emergency alert. Code: {e.code}, message: {e.message});
}
private async sendConfirmationRequest(event: FallEvent): Promise<void> {
const message = 检测到可能的跌倒事件,请确认 (置信度: ${(event.confidence * 100).toFixed(0)}%);
try {
// 发送本地确认请求
await notification.publish({
id: 2,
contentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: '请确认跌倒事件',
text: message,
additionalText: new Date(event.timestamp).toLocaleTimeString()
});
// 通过分布式数据总线广播确认请求
if (this.kvStore) {
await this.kvStore.put('confirmation_request', {
value: {
...event,
message,
alertType: 'confirmation'
});
} catch (e) {
console.error(Failed to send confirmation request. Code: {e.code}, message: {e.message});
}
private getDeviceId(): string {
// 实际应用中应获取真实设备ID
return ‘fall_detector_’ + Math.random().toString(36).substr(2, 9);
private async syncData(): Promise<void> {
if (!this.kvStore) return;
try {
// 同步未确认的跌倒事件
const unsyncedEvents = this.fallEvents.filter(e => !e.isSynced);
if (unsyncedEvents.length > 0) {
await this.kvStore.put('fall_events', { value: unsyncedEvents });
this.fallEvents.forEach(e => {
if (!e.isSynced) e.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 === ‘fall_events’) {
const remoteEvents = entry.value.value as FallEvent[];
this.mergeFallEvents(remoteEvents);
else if (entry.key === ‘device_status’) {
const remoteStatus = entry.value.value as DeviceStatus;
this.mergeDeviceStatus(remoteStatus);
else if (entry.key === ‘emergency_alert’) {
const remoteAlert = entry.value.value as FallEvent & { message: string };
this.handleRemoteAlert(remoteAlert);
else if (entry.key === ‘confirmation_request’) {
const remoteRequest = entry.value.value as FallEvent & { message: string };
this.handleConfirmationRequest(remoteRequest);
});
private mergeFallEvents(remoteEvents: FallEvent[]): void {
remoteEvents.forEach(remote => {
const existing = this.fallEvents.find(local =>
local.timestamp === remote.timestamp &&
local.deviceId === remote.deviceId
);
if (!existing) {
this.fallEvents.push(remote);
else {
// 合并策略:保留更高的置信度
if (remote.confidence > existing.confidence) {
existing.confidence = remote.confidence;
// 合并确认状态
if (remote.isConfirmed && !existing.isConfirmed) {
existing.isConfirmed = true;
}
});
// 按时间排序
this.fallEvents.sort((a, b) => a.timestamp - b.timestamp);
private mergeDeviceStatus(remoteStatus: DeviceStatus): void {
// 采用更保守的低电量模式
if (remoteStatus.isLowPowerMode && !this.deviceStatus.isLowPowerMode) {
this.deviceStatus.isLowPowerMode = true;
this.adjustCheckInterval();
// 采用更长的同步间隔
this.deviceStatus.syncInterval = Math.max(
this.deviceStatus.syncInterval,
remoteStatus.syncInterval
);
private handleRemoteAlert(alert: FallEvent & { message: string }): void {
// 避免处理自己的预警
if (alert.deviceId === this.getDeviceId()) return;
// 显示远程设备发来的预警
notification.publish({
id: 3,
contentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: 远程设备预警 (${alert.deviceId.substring(0, 6)}),
text: alert.message,
additionalText: new Date(alert.timestamp).toLocaleTimeString()
}).catch(e => {
console.error(Failed to publish remote alert. Code: {e.code}, message: {e.message});
});
private handleConfirmationRequest(request: FallEvent & { message: string }): void {
// 避免处理自己的请求
if (request.deviceId === this.getDeviceId()) return;
// 显示远程设备发来的确认请求
notification.publish({
id: 4,
contentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: 请确认跌倒事件 (${request.deviceId.substring(0, 6)}),
text: request.message,
additionalText: new Date(request.timestamp).toLocaleTimeString()
}).catch(e => {
console.error(Failed to publish confirmation request. Code: {e.code}, message: {e.message});
});
public async confirmFallEvent(timestamp: number): Promise<boolean> {
const event = this.fallEvents.find(e => e.timestamp === timestamp);
if (!event) return false;
event.isConfirmed = true;
event.isSynced = false;
await this.saveLocalData();
await this.syncData();
return true;
public async dismissFallEvent(timestamp: number): Promise<boolean> {
const event = this.fallEvents.find(e => e.timestamp === timestamp);
if (!event) return false;
// 标记为误报
event.confidence = 0;
event.isConfirmed = true;
event.isSynced = false;
await this.saveLocalData();
await this.syncData();
return true;
public getRecentEvents(count: number = 5): FallEvent[] {
return this.fallEvents.slice(-count).reverse();
public getDeviceStatus(): DeviceStatus {
return this.deviceStatus;
public async stopMonitoring(): Promise<void> {
if (this.accelSensorId !== -1) {
await sensor.off(this.accelSensorId);
this.accelSensorId = -1;
if (this.gyroSensorId !== -1) {
await sensor.off(this.gyroSensorId);
this.gyroSensorId = -1;
geoLocationManager.off(‘locationChange’);
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/FallDetector.ets
@Component
export struct FallDetector {
private fallService = FallDetectionService.getInstance();
@State recentEvents: FallEvent[] = [];
@State deviceStatus: DeviceStatus = {
lastFallTime: 0,
batteryLevel: 100,
isLowPowerMode: false,
checkInterval: 100,
syncInterval: 300000
};
@State isMonitoring: boolean = false;
private timer: number = 0;
aboutToAppear(): void {
this.loadData();
this.startMonitoring();
this.startAutoRefresh();
aboutToDisappear(): void {
this.stopAutoRefresh();
private loadData(): void {
this.recentEvents = this.fallService.getRecentEvents();
this.deviceStatus = this.fallService.getDeviceStatus();
private async startMonitoring(): Promise<void> {
this.isMonitoring = await this.fallService.startMonitoring();
private startAutoRefresh(): void {
this.timer = setInterval(() => {
this.loadData();
}, 5000); // 每5秒刷新一次
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();
// 控制按钮
Row() {
Button(this.isMonitoring ? '停止监测' : '开始监测')
.type(ButtonType.Capsule)
.width('45%')
.height(50)
.backgroundColor(this.isMonitoring ? '#4CAF50' : '#F44336')
.fontColor('#FFFFFF')
.onClick(() => {
this.toggleMonitoring();
});
Button('同步数据')
.type(ButtonType.Capsule)
.width('45%')
.height(50)
.backgroundColor('#2196F3')
.fontColor('#FFFFFF')
.margin({ left: 10 })
.onClick(() => {
this.syncData();
});
.margin({ top: 20, bottom: 20 });
// 最近事件
Text('最近跌倒事件')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 10 });
if (this.recentEvents.length > 0) {
this.buildEventList();
else {
Text('暂无跌倒事件')
.fontSize(14)
.fontColor('#666666');
}
.width('100%')
.height('100%')
.padding(20);
@Builder
private buildStatusCard() {
Column() {
// 设备状态
Row() {
Image($r(‘app.media.ic_device’))
.width(24)
.height(24)
.margin({ right: 10 });
Column() {
Text('设备状态')
.fontSize(14)
.fontColor('#666666');
Row() {
Text(this.isMonitoring ? '监测中' : '已停止')
.fontSize(16)
.fontColor(this.isMonitoring ? '#4CAF50' : '#F44336');
Text(this.deviceStatus.isLowPowerMode ? ' (低电量模式)' : '')
.fontSize(16)
.fontColor('#FF9800');
}
.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() {
Progress({
value: this.deviceStatus.batteryLevel,
total: 100,
type: ProgressType.Linear
})
.width(150)
.height(10);
Text(${this.deviceStatus.batteryLevel}%)
.fontSize(14)
.margin({ left: 10 });
}
.layoutWeight(1);
.margin({ bottom: 15 });
// 最后事件
Row() {
Image($r('app.media.ic_history'))
.width(24)
.height(24)
.margin({ right: 10 });
Column() {
Text('最后检测')
.fontSize(14)
.fontColor('#666666');
if (this.deviceStatus.lastFallTime > 0) {
Text(new Date(this.deviceStatus.lastFallTime).toLocaleTimeString())
.fontSize(16);
else {
Text('无事件')
.fontSize(16);
}
.layoutWeight(1);
}
.width('100%')
.padding(15)
.backgroundColor('#FFFFFF')
.borderRadius(10)
.shadow({ radius: 5, color: '#E0E0E0', offsetX: 0, offsetY: 2 });
@Builder
private buildEventList() {
List({ space: 10 }) {
ForEach(this.recentEvents, (event) => {
ListItem() {
Column() {
Row() {
Text(‘跌倒检测’)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(‘#F44336’)
.layoutWeight(1);
Text(${(event.confidence * 100).toFixed(0)}%)
.fontSize(16)
.fontColor(this.getConfidenceColor(event.confidence));
Row() {
Text(new Date(event.timestamp).toLocaleTimeString())
.fontSize(14)
.fontColor('#666666');
Text(event.isConfirmed ? '已确认' : '未确认')
.fontSize(14)
.fontColor(event.isConfirmed ? '#4CAF50' : '#FF9800')
.margin({ left: 10 });
.margin({ top: 5 });
if (!event.isConfirmed) {
Row() {
Button('确认')
.type(ButtonType.Capsule)
.width('40%')
.height(30)
.backgroundColor('#4CAF50')
.fontColor('#FFFFFF')
.onClick(() => {
this.confirmEvent(event.timestamp);
});
Button('误报')
.type(ButtonType.Capsule)
.width('40%')
.height(30)
.backgroundColor('#F44336')
.fontColor('#FFFFFF')
.margin({ left: 10 })
.onClick(() => {
this.dismissEvent(event.timestamp);
});
.margin({ top: 10 });
}
.width('100%')
.padding(10)
.borderRadius(10)
.backgroundColor('#FFFFFF')
.shadow({ radius: 3, color: '#E0E0E0', offsetX: 0, offsetY: 1 });
})
.width(‘100%’)
.height('40%');
private getConfidenceColor(confidence: number): string {
if (confidence > 0.8) return '#F44336'; // 高置信度 - 红色
if (confidence > 0.5) return '#FF9800'; // 中等置信度 - 橙色
return '#9E9E9E'; // 低置信度 - 灰色
private async toggleMonitoring(): Promise<void> {
if (this.isMonitoring) {
await this.fallService.stopMonitoring();
this.isMonitoring = false;
else {
this.isMonitoring = await this.fallService.startMonitoring();
}
private async syncData(): Promise<void> {
await this.fallService.syncData();
prompt.showToast({ message: ‘数据同步中…’, duration: 2000 });
this.loadData();
private async confirmEvent(timestamp: number): Promise<void> {
const success = await this.fallService.confirmFallEvent(timestamp);
if (success) {
prompt.showToast({ message: '事件已确认', duration: 2000 });
this.loadData();
else {
prompt.showToast({ message: '确认失败', duration: 2000 });
}
private async dismissEvent(timestamp: number): Promise<void> {
const success = await this.fallService.dismissFallEvent(timestamp);
if (success) {
prompt.showToast({ message: ‘已标记为误报’, duration: 2000 });
this.loadData();
else {
prompt.showToast({ message: '操作失败', duration: 2000 });
}
主界面实现
// src/main/ets/pages/HealthPage.ets
import { FallDetectionService } from ‘…/service/FallDetectionService’;
import { FallDetector } from ‘…/components/FallDetector’;
@Entry
@Component
struct HealthPage {
@State activeTab: number = 0;
private fallService = FallDetectionService.getInstance();
build() {
Column() {
// 标题
Text(‘老人健康监护’)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 });
// 标签页
Tabs({ barPosition: BarPosition.Start }) {
TabContent() {
// 跌倒检测标签页
FallDetector()
.tabBar(‘跌倒检测’);
TabContent() {
// 健康数据标签页
this.buildHealthTab()
.tabBar(‘健康数据’);
.barWidth(‘100%’)
.barHeight(50)
.width('100%')
.height('80%')
.width(‘100%’)
.height('100%')
.padding(20);
@Builder
private buildHealthTab() {
Column() {
Text(‘健康数据统计’)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 });
// 今日活动数据
this.buildActivityStats();
// 历史跌倒事件
Text('历史跌倒记录')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ top: 30, bottom: 10 });
this.buildHistoryChart();
.width(‘100%’)
.height('100%')
.padding(20);
@Builder
private buildActivityStats() {
Column() {
Row() {
Column() {
Text(‘今日步数’)
.fontSize(14)
.fontColor(‘#666666’);
Text('2560')
.fontSize(24)
.fontWeight(FontWeight.Bold);
.width(‘33%’)
Column() {
Text('活动时间')
.fontSize(14)
.fontColor('#666666');
Text('1.5小时')
.fontSize(24)
.fontWeight(FontWeight.Bold);
.width(‘33%’)
Column() {
Text('卡路里')
.fontSize(14)
.fontColor('#666666');
Text('420 kcal')
.fontSize(24)
.fontWeight(FontWeight.Bold);
.width(‘33%’)
.margin({ bottom: 20 });
// 心率图表
Text('心率变化')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 10 });
this.buildHeartRateChart();
.width(‘100%’)
.padding(15)
.backgroundColor('#FFFFFF')
.borderRadius(10)
.shadow({ radius: 5, color: '#E0E0E0', offsetX: 0, offsetY: 2 });
@Builder
private buildHeartRateChart() {
// 模拟心率数据
const heartRates = [72, 75, 78, 82, 85, 88, 90, 88, 85, 82, 80, 78];
const maxHR = Math.max(…heartRates) + 10;
const minHR = Math.min(…heartRates) - 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 * 30 });
});
// 心率曲线
Polyline()
.width('100%')
.height(120)
.fillOpacity(0)
.stroke('#E91E63')
.strokeWidth(2)
.points(this.getChartPoints(heartRates, minHR, hrRange));
// 数据点
ForEach(heartRates, (hr, i) => {
Circle()
.width(8)
.height(8)
.fill('#E91E63')
.position({
x: i * (100 / (heartRates.length - 1)) + '%',
y: (maxHR - hr) * (120 / hrRange) + '%'
});
});
.width(‘100%’)
.height(120);
// X轴标签
Row() {
ForEach(Array.from({ length: 3 }), (_, i) => {
Text(${i * 4}:00)
.fontSize(10)
.width('33%')
.textAlign(TextAlign.Center);
});
.width(‘100%’)
.margin({ top: 5 });
}
private getChartPoints(data: number[], minValue: number, range: number): Point[] {
return data.map((value, i) => ({
x: i * (100 / (data.length - 1)),
y: (minValue + range - value) * (120 / range)
}));
@Builder
private buildHistoryChart() {
const now = Date.now();
const oneMonthAgo = now - 30 24 60 60 1000;
const fallEvents = this.fallService.getRecentEvents(100).filter(e => e.timestamp >= oneMonthAgo);
if (fallEvents.length === 0) {
return Text('过去30天无跌倒记录')
.fontSize(14)
.fontColor('#666666');
// 按天分组
const dailyCounts: Record<string, number> = {};
fallEvents.forEach(event => {
const date = new Date(event.timestamp).toLocaleDateString();
dailyCounts[date] = (dailyCounts[date] || 0) + 1;
});
const dates = Object.keys(dailyCounts);
const counts = Object.values(dailyCounts);
const maxCount = Math.max(...counts, 5); // 至少显示到5
Column() {
// 柱状图
Row({ space: 5 }) {
ForEach(dates, (date, i) => {
Column() {
// 柱子
Blank()
.height(100 * (counts[i] / maxCount))
.width(20)
.backgroundColor('#F44336');
// 日期标签
Text(date.split('/')[2]) // 只显示日
.fontSize(10)
.margin({ top: 5 });
// 计数标签
Text(${counts[i]})
.fontSize(10)
.fontColor('#666666')
.margin({ top: 5 });
})
.height(120)
.width('100%')
.justifyContent(FlexAlign.End);
// X轴
Line()
.width('100%')
.height(1)
.backgroundColor('#000000');
.width(‘100%’)
.height(150)
.margin({ top: 10 });
}
四、与游戏同步技术的结合点
实时状态同步:借鉴游戏中玩家状态实时同步机制,优化跌倒事件的跨设备同步
事件广播机制:类似游戏中的事件广播,实现紧急预警的快速扩散
数据压缩传输:使用类似游戏中的网络优化技术,对传感器数据进行高效压缩传输
设备角色分配:参考游戏中的主机/客户端模式,确定主监护设备和从属设备
状态一致性保障:借鉴游戏中的状态同步机制,确保多设备间预警状态一致
五、关键特性实现
高精度姿态识别算法:
private detectFallTask(accelData: sensor.AccelerometerResponse, gyroData: sensor.GyroscopeResponse):
isFall: boolean, confidence: number } {
// 1. 计算特征值
const accelMagnitude = Math.sqrt(accelData.x2 + accelData.y2 + accelData.z2);
const gyroMagnitude = gyroData ? Math.sqrt(gyroData.x2 + gyroData.y2 + gyroData.z2) : 0;
// 2. 计算变化率
const accelChange = Math.abs(accelMagnitude - this.lastAccelMagnitude);
this.lastAccelMagnitude = accelMagnitude;
// 3. 检测跌倒特征
const isFreeFall = accelMagnitude < 1.5 && accelChange > 2.0;
const isImpact = accelMagnitude > 3.0 && accelChange > 4.0;
const isLyingDown = accelData.z < -0.8 && gyroMagnitude < 0.5;
// 4. 综合判断
if (isFreeFall && isImpact && isLyingDown) {
return { isFall: true, confidence: 0.9 }; // 典型跌倒特征
else if (isImpact && isLyingDown) {
return { isFall: true, confidence: 0.7 }; // 可能跌倒
return { isFall: false, confidence: 0 };
多级预警策略:
private async triggerAlert(event: FallEvent): Promise<void> {
const now = Date.now();
// 1. 根据置信度分级
if (event.confidence > 0.8) {
// 高置信度 - 立即通知紧急联系人
await this.sendEmergencyAlert(event);
// 自动拨打急救电话 (实际应用中需要相应权限)
if (now - this.lastEmergencyCall > 5 60 1000) { // 5分钟内不重复拨打
await this.callEmergencyNumber();
this.lastEmergencyCall = now;
}
else if (event.confidence > 0.6) {
// 中等置信度 - 发送确认请求
await this.sendConfirmationRequest(event);
// 本地提醒
await this.playAlertSound();
else {
// 低置信度 - 仅记录
console.log(Low confidence event: ${event.confidence});
// 2. 根据时间分级 (夜间加强响应)
const hours = new Date().getHours();
if (hours > 22 || hours < 6) { // 22:00-6:00
await this.notifyFamilyMembers(event);
}
GPS低功耗模式:
private async startLocationUpdates(): Promise<void> {
// 根据活动状态调整定位策略
const isActive = await this.checkActivityStatus();
const requestInfo: geoLocationManager.LocationRequest = {
priority: isActive ?
geoLocationManager.LocationRequestPriority.ACCURACY :
geoLocationManager.LocationRequestPriority.LOW_POWER,
scenario: isActive ?
geoLocationManager.LocationRequestScenario.NAVIGATION :
geoLocationManager.LocationRequestScenario.UNSET,
timeInterval: isActive ? 60 : 300, // 活跃时1分钟,否则5分钟
distanceInterval: isActive ? 10 : 100, // 活跃时10米,否则100米
maxAccuracy: isActive ? 10 : 50 // 活跃时10米精度,否则50米
};
geoLocationManager.on('locationChange', requestInfo, (location) => {
this.handleLocationChange(location);
});
分布式事件同步:
private async syncData(): Promise<void> {
if (!this.kvStore) return;
try {
// 1. 同步未确认事件
const unsyncedEvents = this.fallEvents.filter(e => !e.isSynced);
if (unsyncedEvents.length > 0) {
await this.kvStore.put('fall_events', { value: unsyncedEvents });
// 2. 同步设备状态
await this.kvStore.put('device_status', { value: this.deviceStatus });
// 3. 同步位置信息
if (this.lastLocation) {
await this.kvStore.put('last_location', { value: this.lastLocation });
} catch (e) {
console.error(Sync failed. Code: {e.code}, message: {e.message});
}
六、性能优化策略
传感器采样率自适应:
private async adjustSensorRate(): Promise<void> {
// 根据活动状态调整采样率
const isActive = await this.checkActivityStatus();
const battery = await power.getBatteryInfo();
this.deviceStatus.checkInterval =
battery.batterySoc < 30 ? 500 : // 低电量
isActive ? 50 : // 活跃状态高频率
100; // 默认频率
if (this.accelSensorId !== -1) {
await sensor.off(this.accelSensorId);
this.accelSensorId = await sensor.on(sensor.SensorId.ACCELEROMETER, {
interval: this.deviceStatus.checkInterval,
callback: this.handleAccelData
});
}
本地缓存优先:
public getRecentEvents(count: number = 5): FallEvent[] {
// 先从内存读取
return this.fallEvents.slice(-count).reverse();
批量数据处理:
private async processSensorDataBatch(dataBatch: sensor.AccelerometerResponse[]): Promise<void> {
// 使用任务池并行处理
const task = new taskpool.Task(this.analyzeDataBatch, dataBatch);
const results = await taskpool.execute(task) as { isFall: boolean, confidence: number }[];
// 找出最高置信度的结果
const highestConfidence = Math.max(...results.map(r => r.confidence));
if (highestConfidence > 0.5) {
const fallEvent = results.find(r => r.confidence === highestConfidence);
this.triggerAlert({
timestamp: Date.now(),
confidence: highestConfidence,
position: this.lastLocation || { latitude: 0, longitude: 0, accuracy: 0 },
deviceId: this.getDeviceId(),
isConfirmed: false,
isSynced: false
});
}
资源释放管理:
public async destroy(): Promise<void> {
await this.stopMonitoring();
if (this.kvStore) {
this.kvStore.off('dataChange');
geoLocationManager.off(‘locationChange’);
七、项目扩展方向
生命体征监测:增加心率、血氧等生命体征监测功能
AI行为识别:集成AI模型识别更多异常行为模式
自动求救:检测到跌倒后自动拨打急救电话
家庭看护网络:建立家庭内部的看护设备网络
健康数据分析:提供长期的健康趋势分析报告
八、总结
本文实现的老人跌倒检测系统具有以下特点:
采用高精度姿态识别算法,准确检测跌倒事件
实现多级预警策略,根据置信度采取不同响应措施
优化GPS使用策略,平衡定位精度和电量消耗
基于分布式数据同步,实现多终端协同监护
提供直观的数据可视化和完整的预警管理功能
该应用展示了HarmonyOS在健康监护领域的强大能力,特别是在传感器数据处理、低功耗控制和多设备协同方面的优势。通过借鉴游戏同步技术,实现了高效可靠的预警机制,为老人健康监护提供了完整的解决方案。
