鸿蒙跨端老人跌倒检测系统开发指南 原创

进修的泡芙
发布于 2025-6-23 13:28
浏览
0收藏

鸿蒙跨端老人跌倒检测系统开发指南

一、项目概述

本文基于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在健康监护领域的强大能力,特别是在传感器数据处理、低功耗控制和多设备协同方面的优势。通过借鉴游戏同步技术,实现了高效可靠的预警机制,为老人健康监护提供了完整的解决方案。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
收藏
回复
举报
回复
    相关推荐