HarmonyOS 6(API 23)实战:打造“光佑银龄“——AI老人安全守护与健康管理新范式

文章目录


每日一句正能量

本事是一个人最硬的底牌,最稳的靠山、最坚实的铠甲。

真正的强大从来不是压倒一切,而是能驾驭自己的锋芒,在该坚持时深耕,在该收敛时留白。


一、前言:当银发守护遇上光感与智能

中国正加速进入老龄化社会,截至2025年底,60岁以上人口已突破3亿,其中约90%选择居家养老。然而,"空巢老人"的安全与健康问题日益严峻:跌倒后无法及时求助、慢性病用药遗漏、昼夜节律紊乱导致睡眠障碍、认知退化未被及时发现......传统养老监护设备往往功能单一、交互复杂,老人学习成本高,子女远程看护也缺乏有效手段。

更深层的痛点在于光环境对老年人健康的影响:老年人视网膜感光细胞退化,对光线变化的适应能力下降。清晨缺乏足够光照会抑制褪黑素清除,导致白天嗜睡、夜间失眠;傍晚蓝光暴露过多会延迟睡眠 onset;夜间起夜时刺眼的灯光可能引发跌倒。这些问题长期被忽视,却直接影响老年人的生活质量和安全。

HarmonyOS 6(API 23)带来的**悬浮导航(Floating Navigation)沉浸光感(Immersive Light Sensing)**两大核心能力,为智能养老应用开辟了全新的技术路径:

  • 沉浸光感能够实时感知环境光照的色温、亮度与光谱成分,结合老年人视觉特征,自动生成个性化的昼夜节律光疗方案,同时优化界面显示以保护老人视力
  • 悬浮导航可在全屏守护模式下,以极简大字体、语音优先的方式提供用药提醒、健康问询、紧急呼叫、家属联动等智能体交互入口,成为老人身边的"数字守护精灵"

本文将实战演示如何构建**"光佑银龄"**------一个能"读懂"老人需求、用光线调节健康节律、通过悬浮AI助手提供全天候安全守护的智能养老系统。


二、核心能力与技术架构

2.1 沉浸光感的银发场景适配

HarmonyOS 6的AmbientLightFusion在API 23中针对老年人视觉特征进行了专项优化:

光感维度 老年人影响 自适应策略
照度需求 老年人需要2-3倍于年轻人的照度 自动提升界面亮度,增大对比度
色温敏感 晶状体黄化,对蓝光过滤能力增强 傍晚自动降低蓝光,保护褪黑素分泌
眩光敏感 瞳孔收缩能力下降,眩光耐受差 检测眩光源,自动调整显示区域
暗适应慢 暗适应时间从2分钟延长至10分钟 夜间起夜时渐进式亮灯,避免突变
昼夜节律 褪黑素分泌节律相位提前 晨间强化光照,傍晚提前调暗

2.2 悬浮导航的适老化交互设计

HarmonyOS 6的FloatingNavigation支持**"极简守护"**模式------针对老年人认知特点进行专项优化:

  • 大字体、高对比度、语音播报优先
  • 一键紧急呼叫(长按3秒)
  • 用药时间自动提醒(悬浮球变色跳动)
  • 跌倒检测自动报警(红色闪烁+自动拨号)
  • 语音陪伴模式(紫色脉动,随时聊天)

2.3 系统架构


三、核心代码实战

3.1 银发节律光感引擎(SeniorLightRhythmEngine.ets)

代码亮点: 本引擎是系统的"光疗处方师"。它基于老年人生物钟特征,结合环境光感数据,生成精准的昼夜节律光疗方案。核心创新包括:晨间光疗窗口 (7:00-9:00,10000lux冷白光,抑制褪黑素、提升警觉度)、傍晚褪黑素准备 (18:00后逐步降低蓝光,促进自然入睡)、夜间渐进式照明(起夜时从0.1lux缓慢升至30lux,避免眩目)。

typescript 复制代码
// services/SeniorLightRhythmEngine.ets
import { sensor } from '@kit.SensorServiceKit';
import { display } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';

// 老年人视觉特征
export interface SeniorVisionProfile {
  age: number;
  cataractLevel: number;       // 白内障程度 0-1
  macularDegeneration: boolean; // 黄斑变性
  glaucoma: boolean;           // 青光眼
  diabetesRetinopathy: boolean; // 糖尿病视网膜病变
  preferredColorTemp: number;  // 偏好的色温
  lightSensitivity: number;    // 光敏感度
}

// 节律光疗方案
export interface LightTherapyPlan {
  phase: 'dawn' | 'morning' | 'day' | 'evening' | 'night' | 'dawn_transition';
  targetLux: number;
  targetColorTemp: number;
  blueLightRatio: number;
  duration: number;            // 建议时长(分钟)
  purpose: string;
  warning?: string;
}

// 夜间照明配置
export interface NightLightingConfig {
  initialBrightness: number;   // 初始亮度 0.1-1.0 lux
  rampUpDuration: number;      // 渐升时长(秒)
  maxBrightness: number;       // 最大亮度
  colorTemp: number;           // 色温 2200-2700K
  motionTriggerRange: number;  // 触发距离(米)
}

@Observed
export class RhythmState {
  currentPhase: string = 'day';
  currentLux: number = 300;
  currentColorTemp: number = 4000;
  blueFilterActive: boolean = false;
  nightMode: boolean = false;
  therapyScore: number = 0;   // 今日光疗完成度 0-100
}

export class SeniorLightRhythmEngine {
  private static instance: SeniorLightRhythmEngine;
  private seniorProfile: SeniorVisionProfile;
  private rhythmState: RhythmState = new RhythmState();
  private stateListeners: Array<(state: RhythmState) => void> = [];
  private therapyPlan: LightTherapyPlan | null = null;
  private nightConfig: NightLightingConfig = {
    initialBrightness: 0.1,
    rampUpDuration: 10,
    maxBrightness: 30,
    colorTemp: 2500,
    motionTriggerRange: 2.0
  };

  // 标准节律光疗方案库
  private readonly STANDARD_PLANS: Record<string, LightTherapyPlan> = {
    'dawn': {
      phase: 'dawn',
      targetLux: 100,
      targetColorTemp: 3000,
      blueLightRatio: 0.2,
      duration: 30,
      purpose: '温和唤醒,避免强光刺激'
    },
    'morning': {
      phase: 'morning',
      targetLux: 8000,
      targetColorTemp: 6500,
      blueLightRatio: 0.45,
      duration: 120,
      purpose: '强光抑制褪黑素,提升日间警觉度'
    },
    'day': {
      phase: 'day',
      targetLux: 500,
      targetColorTemp: 5000,
      blueLightRatio: 0.3,
      duration: 0,
      purpose: '维持正常活动照明'
    },
    'evening': {
      phase: 'evening',
      targetLux: 200,
      targetColorTemp: 3000,
      blueLightRatio: 0.15,
      duration: 180,
      purpose: '降低蓝光,准备褪黑素分泌'
    },
    'night': {
      phase: 'night',
      targetLux: 0,
      targetColorTemp: 2200,
      blueLightRatio: 0.05,
      duration: 0,
      purpose: '最小化光照,保护睡眠'
    },
    'dawn_transition': {
      phase: 'dawn_transition',
      targetLux: 50,
      targetColorTemp: 2700,
      blueLightRatio: 0.1,
      duration: 15,
      purpose: '起夜渐进照明,避免眩目'
    }
  };

  static getInstance(): SeniorLightRhythmEngine {
    if (!SeniorLightRhythmEngine.instance) {
      SeniorLightRhythmEngine.instance = new SeniorLightRhythmEngine();
    }
    return SeniorLightRhythmEngine.instance;
  }

  async initialize(profile: SeniorVisionProfile): Promise<void> {
    this.seniorProfile = profile;
    
    // 根据眼疾调整方案
    this.adjustForEyeConditions();
    
    // 启动环境光监测
    sensor.on(sensor.SensorId.AMBIENT_LIGHT, (data) => {
      this.rhythmState.currentLux = data.intensity;
      this.evaluateRhythmProgress();
    });

    // 启动夜间运动检测(毫米波雷达或PIR)
    this.startNightMotionDetection();
    
    hilog.info(0x0000, 'SeniorLight', '银发节律光感引擎初始化完成');
  }

  private adjustForEyeConditions(): void {
    const profile = this.seniorProfile;
    
    // 白内障:需要更高照度,降低蓝光
    if (profile.cataractLevel > 0.3) {
      Object.values(this.STANDARD_PLANS).forEach(plan => {
        plan.targetLux *= (1 + profile.cataractLevel);
        plan.blueLightRatio *= (1 - profile.cataractLevel * 0.5);
      });
    }
    
    // 黄斑变性:避免蓝光,增强红光对比
    if (profile.macularDegeneration) {
      Object.values(this.STANDARD_PLANS).forEach(plan => {
        plan.blueLightRatio = Math.min(plan.blueLightRatio, 0.15);
        plan.targetColorTemp = Math.min(plan.targetColorTemp, 4000);
      });
    }
    
    // 青光眼:避免暗环境骤变
    if (profile.glaucoma) {
      this.nightConfig.rampUpDuration = 20; // 更慢渐升
      this.nightConfig.maxBrightness = 20;  // 更低最大亮度
    }
  }

  private evaluateRhythmProgress(): void {
    const hour = new Date().getHours();
    const minute = new Date().getMinutes();
    const timeDecimal = hour + minute / 60;
    
    // 确定当前节律阶段
    let targetPhase: string;
    if (timeDecimal >= 5 && timeDecimal < 7) targetPhase = 'dawn';
n    else if (timeDecimal >= 7 && timeDecimal < 10) targetPhase = 'morning';
    else if (timeDecimal >= 10 && timeDecimal < 17) targetPhase = 'day';
    else if (timeDecimal >= 17 && timeDecimal < 21) targetPhase = 'evening';
    else if (timeDecimal >= 21 || timeDecimal < 5) targetPhase = 'night';
    else targetPhase = 'day';
    
    this.therapyPlan = this.STANDARD_PLANS[targetPhase];
    this.rhythmState.currentPhase = targetPhase;
    
    // 计算光疗完成度
    this.calculateTherapyScore(timeDecimal);
    
    // 根据当前环境光与目标差距,给出建议
    this.generateLightAdvice();
    
    this.notifyStateUpdate();
  }

  private calculateTherapyScore(timeDecimal: number): void {
    // 简化的光疗完成度计算
    let score = 0;
    if (timeDecimal >= 7 && timeDecimal <= 9) {
      // 晨间光疗窗口
      const currentLux = this.rhythmState.currentLux;
      const targetLux = this.therapyPlan?.targetLux || 8000;
      score = Math.min(100, (currentLux / targetLux) * 100);
    } else if (timeDecimal >= 17 && timeDecimal <= 20) {
      // 傍晚光疗
      score = 50; // 基础分
      if (this.rhythmState.currentLux < 300) score += 30;
      if (this.rhythmState.currentColorTemp < 3500) score += 20;
    }
    this.rhythmState.therapyScore = Math.round(score);
  }

  private generateLightAdvice(): void {
    if (!this.therapyPlan) return;
    
    const currentLux = this.rhythmState.currentLux;
    const targetLux = this.therapyPlan.targetLux;
    
    if (this.therapyPlan.phase === 'morning' && currentLux < targetLux * 0.5) {
      this.therapyPlan.warning = '晨间光照不足,建议拉开窗帘或开启主灯';
    } else if (this.therapyPlan.phase === 'evening' && currentLux > targetLux * 2) {
      this.therapyPlan.warning = '傍晚光照过强,建议调暗灯光,准备入睡';
    }
  }

  private startNightMotionDetection(): void {
    // 通过毫米波雷达或PIR传感器检测夜间运动
    // 简化实现
    setInterval(() => {
      const hour = new Date().getHours();
      if (hour >= 22 || hour <= 5) {
        // 模拟检测到运动
        if (Math.random() > 0.7) {
          this.triggerNightLighting();
        }
      }
    }, 5000);
  }

  private triggerNightLighting(): void {
    // 启动渐进式夜间照明
    this.rhythmState.currentPhase = 'dawn_transition';
    this.rhythmState.nightMode = true;
    
    // 通过HarmonyOS控制智能灯具
    this.controlSmartLights(this.nightConfig);
    
    hilog.info(0x0000, 'SeniorLight', '夜间渐进照明启动');
  }

  private async controlSmartLights(config: NightLightingConfig): Promise<void> {
    // 通过分布式软总线控制智能灯具
    // 实现渐进式亮灯
    const steps = 20;
    for (let i = 0; i <= steps; i++) {
      const brightness = config.initialBrightness + 
        (config.maxBrightness - config.initialBrightness) * (i / steps);
      // 发送控制指令到灯具
      await this.sendLightCommand(brightness, config.colorTemp);
      await new Promise(r => setTimeout(r, config.rampUpDuration * 1000 / steps));
    }
  }

  private async sendLightCommand(brightness: number, colorTemp: number): Promise<void> {
    // 通过HarmonyOS分布式能力发送灯光控制指令
    // 实际实现需结合具体智能家居协议
  }

  getCurrentPlan(): LightTherapyPlan | null {
    return this.therapyPlan;
  }

  getNightConfig(): NightLightingConfig {
    return this.nightConfig;
  }

  getCurrentState(): RhythmState {
    return this.rhythmState;
  }

  onStateChanged(callback: (state: RhythmState) => void): void {
    this.stateListeners.push(callback);
  }

  private notifyStateUpdate(): void {
    this.stateListeners.forEach(cb => cb(this.rhythmState));
  }

  stopEngine(): void {
    sensor.off(sensor.SensorId.AMBIENT_LIGHT);
    this.stateListeners = [];
  }
}

3.2 安全守护悬浮球(GuardianNavBall.ets)

代码亮点: 这是系统的"数字守护精灵"。针对老年人认知特点,悬浮球采用极简设计:超大字体、高对比度配色、语音播报优先。核心创新是**"紧急呼叫一键直达"------长按3秒自动拨打预设紧急联系人并发送定位; "用药提醒智能联动"------到用药时间悬浮球变为橙色并语音播报;"跌倒检测自动响应"**------检测到跌倒后悬浮球红色闪烁,自动展开"呼叫家属/联系急救/我已没事"选项。

typescript 复制代码
// components/GuardianNavBall.ets
import { FloatingNavigation } from '@kit.ArkUI';
import { SeniorLightRhythmEngine, RhythmState } from '../services/SeniorLightRhythmEngine';
import { HealthGuardianAgent } from '../services/HealthGuardianAgent';
import { speechRecognizer, textToSpeech } from '@kit.SpeechKit';
import { call } from '@kit.TelephonyKit';

export enum GuardianStatus {
  NORMAL = 'NORMAL',           // 正常
  MEDICATION = 'MEDICATION', // 用药提醒
  FALL_ALERT = 'FALL_ALERT', // 跌倒警报
  EMERGENCY = 'EMERGENCY',    // 紧急状态
  COMPANION = 'COMPANION',    // 陪伴模式
  NIGHT = 'NIGHT'             // 夜间模式
}

@Component
export struct GuardianNavBall {
  @State ballScale: number = 1.0;
  @State ballColor: ResourceColor = '#4CAF50';
  @State isExpanded: boolean = false;
  @State currentStatus: GuardianStatus = GuardianStatus.NORMAL;
  @State emergencyCountdown: number = 0; // 紧急呼叫倒计时
  @State medicationName: string = '';
  @State isListening: boolean = false;
  
  private rhythmEngine: SeniorLightRhythmEngine = SeniorLightRhythmEngine.getInstance();
  private healthAgent: HealthGuardianAgent = new HealthGuardianAgent();
  private emergencyTimer?: number;
  
  // 紧急联系人
  private emergencyContacts: string[] = ['13800138000', '13900139000'];

  aboutToAppear() {
    // 监听节律状态
    this.rhythmEngine.onStateChanged((state) => {
      this.adaptToRhythm(state);
    });

    // 启动用药监测
    this.startMedicationMonitoring();
    
    // 启动跌倒检测
    this.startFallDetection();
    
    // 启动语音控制
    this.startVoiceControl();
    
    // 启动状态脉动
    this.startStatusPulse();
  }

  private adaptToRhythm(state: RhythmState): void {
    if (state.nightMode) {
      this.currentStatus = GuardianStatus.NIGHT;
      this.ballColor = '#3F51B5';
n    } else if (this.currentStatus === GuardianStatus.NORMAL) {
      // 根据节律阶段调整颜色
      switch (state.currentPhase) {
        case 'morning': this.ballColor = '#FFD700'; break;
        case 'evening': this.ballColor = '#9370DB'; break;
        default: this.ballColor = '#4CAF50';
      }
    }
  }

  private startMedicationMonitoring(): void {
    // 检查用药时间
    setInterval(() => {
      const medication = this.healthAgent.checkMedicationTime();
      if (medication) {
        this.currentStatus = GuardianStatus.MEDICATION;
        this.medicationName = medication.name;
        this.ballColor = '#FF9800';
        
        // 语音播报
        this.speak(`该吃${medication.name}了,每次${medication.dosage}`);
        
        // 自动展开菜单
        this.isExpanded = true;
      }
    }, 60000); // 每分钟检查
  }

  private startFallDetection(): void {
    // 通过加速度计+毫米波雷达检测跌倒
    // 简化实现
    setInterval(() => {
      // 模拟跌倒检测
      if (this.healthAgent.detectFall()) {
        this.triggerFallAlert();
      }
    }, 1000);
  }

  private triggerFallAlert(): void {
    this.currentStatus = GuardianStatus.FALL_ALERT;
    this.ballColor = '#F44336';
    this.isExpanded = true;
    
    // 启动紧急倒计时
    this.emergencyCountdown = 30;
    this.speak('检测到跌倒,30秒后将自动呼叫家属。如无需帮助,请点击\"我没事\"');
    
    this.emergencyTimer = setInterval(() => {
      this.emergencyCountdown--;
      if (this.emergencyCountdown <= 0) {
        this.autoCallEmergency();
        clearInterval(this.emergencyTimer);
      }
    }, 1000);
  }

  private autoCallEmergency(): void {
    // 自动拨打紧急联系人
    this.speak('正在呼叫家属');
    call.makeCall(this.emergencyContacts[0]);
    
    // 发送定位信息
    this.healthAgent.sendEmergencyLocation();
  }

  private startVoiceControl(): void {
    speechRecognizer.on('result', (result) => {
      const text = result.text;
      this.processVoiceCommand(text);
    });
    
    speechRecognizer.startListening({
      language: 'zh-CN',
      wakeWord: '小守护'
    });
  }

  private async processVoiceCommand(text: string): Promise<void> {
    this.isListening = true;
    
    if (text.includes('呼叫') || text.includes('救命')) {
      this.triggerEmergency();
    } else if (text.includes('吃药') || text.includes('用药')) {
      this.confirmMedication();
    } else if (text.includes('我没事') || text.includes('取消')) {
      this.cancelEmergency();
    } else if (text.includes('陪我说话') || text.includes('聊天')) {
      this.startCompanionMode();
    }
    
    this.isListening = false;
  }

  private async speak(text: string): Promise<void> {
    await textToSpeech.speak({
      text: text,
      voice: 'zh-CN-female-warm',
      speed: 0.85, // 稍慢,便于老人理解
      pitch: 1.0,
      volume: 1.0
    });
  }

  private triggerEmergency(): void {
    this.currentStatus = GuardianStatus.EMERGENCY;
    this.ballColor = '#B71C1C';
    this.autoCallEmergency();
  }

  private confirmMedication(): void {
    this.healthAgent.confirmMedicationTaken(this.medicationName);
    this.currentStatus = GuardianStatus.NORMAL;
    this.speak('已记录用药,请按时服药');
  }

  private cancelEmergency(): void {
    if (this.emergencyTimer) {
      clearInterval(this.emergencyTimer);
n      this.emergencyCountdown = 0;
    }
    this.currentStatus = GuardianStatus.NORMAL;
    this.ballColor = '#4CAF50';
    this.isExpanded = false;
    this.speak('已取消紧急呼叫');
  }

  private startCompanionMode(): void {
    this.currentStatus = GuardianStatus.COMPANION;
    this.ballColor = '#9C27B0';
    this.healthAgent.startCompanionChat();
  }

  // ===== 菜单动作 =====
  private callFamily(): void {
    call.makeCall(this.emergencyContacts[0]);
  }

  private callEmergency(): void {
    call.makeCall('120');
  }

  private imOkay(): void {
n    this.cancelEmergency();
  }

  private viewHealthReport(): void {
    // 显示健康报告
  }

  private startLightTherapy(): void {
    // 启动光疗
    this.speak('正在启动光疗模式');
  }

  build() {
    Stack() {
      // 紧急倒计时显示
      if (this.emergencyCountdown > 0) {
        Column() {
          Text(`${this.emergencyCountdown}`)
n            .fontSize(36)
            .fontColor('#F44336')
            .fontWeight(FontWeight.Bold)
          Text('秒后自动呼叫')
            .fontSize(14)
            .fontColor('#F44336')
        }
        .position({ x: -60, y: 10 })
        .width(100)
        .backgroundColor('rgba(255,255,255,0.9)')\n        
        .borderRadius(8)
        .padding(8)
      }

      // 用药提醒显示
      if (this.currentStatus === GuardianStatus.MEDICATION && !this.isExpanded) {
        Column() {
          Text(`该吃${this.medicationName}了`)
            .fontSize(16)
            .fontColor('#FF9800')           
            .fontWeight(FontWeight.Bold)
        }
        .position({ x: -80, y: 15 })
        .width(140)
        .backgroundColor('rgba(255,255,255,0.9)')        
        .borderRadius(8)
        .padding(8)
      }

      // 扇形菜单(大按钮,适老化设计)
      if (this.isExpanded) {
        if (this.currentStatus === GuardianStatus.FALL_ALERT) {
          // 跌倒警报专用菜单         
          this.BigMenuButton('呼叫家属', () => this.callFamily(), 0, '#F44336')          
          this.BigMenuButton('联系急救', () => this.callEmergency(), 1, '#B71C1C')          
          this.BigMenuButton('我没事', () => this.imOkay(), 2, '#4CAF50')        
          } 
          else {\n          
          this.BigMenuButton('健康报告', () => this.viewHealthReport(), 0, '#2196F3')          
          this.BigMenuButton('光疗模式', () => this.startLightTherapy(), 1, '#FFD700')          
          this.BigMenuButton('语音陪伴', () => this.startCompanionMode(), 2, '#9C27B0')          
          this.BigMenuButton('紧急呼叫', () => this.triggerEmergency(), 3, '#F44336')      }     
          }

      // 主悬浮球(超大尺寸,便于点击)
      Column() {
        if (this.isListening) {
          Text('听...')
            .fontSize(24)           
            .fontColor('#FFFFFF')            
            .fontWeight(FontWeight.Bold)        
            } else if (this.emergencyCountdown > 0) {          
            Text('!')           
            .fontSize(36)            
            .fontColor('#FFFFFF')           
            .fontWeight(FontWeight.Bold)       
            } else {          
            Image(this.getStatusIcon())            
            .width(40)            
            .height(40)            
            .fillColor('#FFFFFF')        
            }      
            }      
            .width(72)      
            .height(72)      
            .backgroundColor(this.ballColor)      
            .borderRadius(36)     
            .scale({ x: this.ballScale, y: this.ballScale })      
            .shadow({ \n        radius: 16, \n        color: this.ballColor,\n        offsetY: 4\n      })\n      .onClick(() => {\n        this.isExpanded = !this.isExpanded;\n      })\n      .onTouch((event) => {\n        if (event.type === TouchType.LongPress) {\n          this.triggerEmergency();\n        }\n      })\n      .animation({\n        duration: 200,\n        curve: Curve.EaseInOut\n      })\n    }\n    .width(260)\n    .height(260)\n    .position({ x: 280, y: 550 })\n  }

  @Builder\n  BigMenuButton(label: string, action: () => void, index: number, color: string) {\n    Column() {\n      Text(label)\n        .fontSize(18) // 大字体\n        .fontColor('#FFFFFF')\n        .fontWeight(FontWeight.Bold)\n    }\n    .width(80)\n    .height(80)\n    .backgroundColor(color)\n    .borderRadius(40)\n    .position({\n      x: this.getMenuX(index),\n      y: this.getMenuY(index)\n    })\n    .onClick(() => {\n      action();\n      this.isExpanded = false;\n    })\n    .animation({\n      duration: 300,\n      curve: Curve.Spring,\n      delay: index * 60\n    })\n  }

  private getMenuX(index: number): number {\n    const angles = [210, 240, 270, 300];\n    const radius = 110;\n    const angle = (angles[index] || 270) * Math.PI / 180;\n    return 130 + radius * Math.cos(angle) - 40;\n  }

  private getMenuY(index: number): number {\n    const angles = [210, 240, 270, 300];\n    const radius = 110;\n    const angle = (angles[index] || 270) * Math.PI / 180;\n    return 130 + radius * Math.sin(angle) - 40;\n  }

  private getStatusIcon(): Resource {\n    switch (this.currentStatus) {\n      case GuardianStatus.NORMAL: return $r('app.media.ic_guardian');\n      case GuardianStatus.MEDICATION: return $r('app.media.ic_pill');\n      case GuardianStatus.FALL_ALERT: return $r('app.media.ic_fall');\n      case GuardianStatus.EMERGENCY: return $r('app.media.ic_emergency');\n      case GuardianStatus.COMPANION: return $r('app.media.ic_chat');\n      case GuardianStatus.NIGHT: return $r('app.media.ic_moon');\n      default: return $r('app.media.ic_guardian');\n    }\n  }

  private startStatusPulse(): void {\n    setInterval(() => {\n      switch (this.currentStatus) {\n        case GuardianStatus.NORMAL:\n          this.ballScale = 1.0 + Math.sin(Date.now() / 1000) * 0.05;\n          break;\n        case GuardianStatus.MEDICATION:\n          this.ballScale = 1.0 + Math.sin(Date.now() / 300) * 0.15;\n          break;\n        case GuardianStatus.FALL_ALERT:\n          this.ballScale = 1.0 + Math.sin(Date.now() / 200) * 0.2;\n          break;\n        case GuardianStatus.EMERGENCY:\n          this.ballScale = 1.0 + Math.sin(Date.now() / 150) * 0.25;\n          break;\n      }\n    }, 50);\n  }
}

3.3 AI健康管家智能体(HealthGuardianAgent.ets)

代码亮点: 这是系统的"家庭医生"。它基于端侧大模型和老年医学知识图谱,提供用药管理、健康评估、紧急响应、情感陪伴等服务。核心创新是**"多模态健康融合"**------将智能手环数据(心率、血氧、步数)、环境光感数据(睡眠质量、昼夜节律)、语音交互数据(认知状态、情绪变化)融合,生成综合健康评分与风险预警。

typescript 复制代码
// services/HealthGuardianAgent.ets
import { SeniorLightRhythmEngine, RhythmState } from './SeniorLightRhythmEngine';
import { hilog } from '@kit.PerformanceAnalysisKit';

export interface Medication {
  name: string;\n  dosage: string;\n  times: string[]; // 用药时间 ['08:00', '12:00', '18:00']\n  taken: boolean[];\n  instructions: string;\n}

export interface HealthScore {
  overall: number;           // 综合评分 0-100\n  physical: number;          // 身体评分\n  mental: number;            // 精神评分\n  sleep: number;             // 睡眠评分\n  medication: number;        // 用药依从性\n  safety: number;            // 安全评分\n}

export interface RiskAssessment {\n  level: 'low' | 'medium' | 'high' | 'critical';\n  factors: string[];\n  recommendations: string[];\n  emergencyNeeded: boolean;\n}

export class HealthGuardianAgent {
n  private rhythmEngine: SeniorLightRhythmEngine;\n  private medications: Medication[] = [];\n  private healthHistory: HealthScore[] = [];\n  private conversationContext: string[] = [];

  constructor() {\n    this.rhythmEngine = SeniorLightRhythmEngine.getInstance();\n  }

  async loadMedications(meds: Medication[]): Promise<void> {\n    this.medications = meds;\n  }

  checkMedicationTime(): Medication | null {\n    const now = new Date();\n    const currentTime = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;\n    \n    for (const med of this.medications) {\n      for (let i = 0; i < med.times.length; i++) {\n        if (med.times[i] === currentTime && !med.taken[i]) {\n          return med;\n        }\n      }\n    }\n    return null;\n  }

  confirmMedicationTaken(medName: string): void {\n    const med = this.medications.find(m => m.name === medName);\n    if (med) {\n      const now = new Date();\n      const currentTime = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;\n      const index = med.times.indexOf(currentTime);\n      if (index >= 0) {\n        med.taken[index] = true;\n      }\n    }\n  }

  detectFall(): boolean {\n    // 通过加速度计+毫米波雷达检测跌倒\n    // 跌倒特征:突然的高加速度+静止\n    // 简化实现\n    return Math.random() < 0.001; // 极低概率模拟\n  }

  async assessHealth(): Promise<HealthScore> {\n    // 收集各项数据\n    const physical = await this.assessPhysicalHealth();\n    const mental = await this.assessMentalHealth();\n    const sleep = await this.assessSleepQuality();\n    const medication = this.assessMedicationAdherence();\n    const safety = this.assessSafetyStatus();\n\n    const overall = Math.round(\n      physical * 0.25 + mental * 0.2 + sleep * 0.25 + medication * 0.15 + safety * 0.15\n    );

    const score: HealthScore = {\n      overall,\n      physical,\n      mental,\n      sleep,\n      medication,\n      safety\n    };

    this.healthHistory.push(score);
n    if (this.healthHistory.length > 30) {\n      this.healthHistory.shift();\n    }

    return score;\n  }

  private async assessPhysicalHealth(): Promise<number> {\n    // 从智能手环获取数据\n    // 心率、血压、血氧、步数\n    return 85; // 模拟\n  }

  private async assessMentalHealth(): Promise<number> {\n    // 通过语音交互分析情绪状态\n    // 通过活动模式分析抑郁倾向\n    return 80;\n  }

  private async assessSleepQuality(): Promise<number> {\n    // 从光感数据推断睡眠质量\n    const rhythmState = this.rhythmEngine.getCurrentState();\n    // 光疗完成度与睡眠质量正相关\n    return Math.min(100, 60 + rhythmState.therapyScore * 0.4);\n  }

  private assessMedicationAdherence(): number {\n    let total = 0;\n    let taken = 0;\n    for (const med of this.medications) {\n      total += med.times.length;\n      taken += med.taken.filter(t => t).length;\n    }\n    return total > 0 ? Math.round((taken / total) * 100) : 100;\n  }

  private assessSafetyStatus(): number {\n    // 综合跌倒风险、活动范围、环境安全\n    return 90;\n  }

  async assessRisk(): Promise<RiskAssessment> {\n    const score = await this.assessHealth();\n    const risks: string[] = [];\n    const recommendations: string[] = [];\n    let emergencyNeeded = false;

    if (score.physical < 60) {\n      risks.push('身体状况需关注');\n      recommendations.push('建议安排体检');\n    }\n    if (score.sleep < 60) {\n      risks.push('睡眠质量差');\n      recommendations.push('调整光疗方案,改善睡眠环境');\n    }\n    if (score.medication < 80) {\n      risks.push('用药依从性低');\n      recommendations.push('设置更多用药提醒');\n    }
n    if (score.safety < 70) {\n      risks.push('安全风险较高');\n      recommendations.push('检查居家环境,安装防滑设施');\n      emergencyNeeded = true;\n    }

    let level: 'low' | 'medium' | 'high' | 'critical' = 'low';\n    if (score.overall < 40) level = 'critical';\n    else if (score.overall < 60) level = 'high';\n    else if (score.overall < 80) level = 'medium';

    return {\n      level,\n      factors: risks,\n      recommendations,\n      emergencyNeeded\n    };\n  }

  async startCompanionChat(): Promise<void> {\n    // 启动陪伴对话\n    // 基于大模型生成适合老年人的对话内容\n    const topics = [\n      '今天天气不错,要不要出去走走?',\n      '您昨晚睡得好吗?',\n      '记得按时吃药哦',\n      '给您讲个有趣的故事吧'\n    ];\n    const topic = topics[Math.floor(Math.random() * topics.length)];\n    \n    // 语音播报\n    await this.speak(topic);\n  }

  async sendEmergencyLocation(): Promise<void> {\n    // 获取定位并发送给家属\n    hilog.info(0x0000, 'Guardian', '发送紧急定位');\n  }

  private async speak(text: string): Promise<void> {\n    const { textToSpeech } = await import('@kit.SpeechKit');\n    await textToSpeech.speak({\n      text,\n      voice: 'zh-CN-female-warm',\n      speed: 0.85,\n      pitch: 1.0\n    });\n  }

  getHealthTrend(): string {\n    if (this.healthHistory.length < 2) return 'stable';\n    const recent = this.healthHistory.slice(-3);\n    const avg = recent.reduce((s, h) => s + h.overall, 0) / recent.length;\n    const previous = this.healthHistory.slice(-6, -3);\n    const prevAvg = previous.length > 0 ? \n      previous.reduce((s, h) => s + h.overall, 0) / previous.length : avg;\n    \n    if (avg > prevAvg + 5) return 'improving';\n    if (avg < prevAvg - 5) return 'declining';\n    return 'stable';\n  }
}

3.4 自适应守护面板(AdaptiveGuardianPanel.ets)

代码亮点: 这是系统的"守护仪表盘"。针对老年人视力特点,采用超大字体、高对比度、简洁布局。支持光感自适应 ------环境昏暗时自动增强对比度、增大字体;语音播报优先 ------所有关键信息均可语音朗读;一键操作------所有功能均可在3步内完成。

typescript 复制代码
// components/AdaptiveGuardianPanel.ets
import { SeniorLightRhythmEngine, RhythmState, LightTherapyPlan } from '../services/SeniorLightRhythmEngine';
import { HealthGuardianAgent, HealthScore, RiskAssessment } from '../services/HealthGuardianAgent';

@Component
export struct AdaptiveGuardianPanel {
  @State rhythmState: RhythmState = new RhythmState();\n  @State healthScore: HealthScore | null = null;\n  @State riskAssessment: RiskAssessment | null = null;\n  @State currentPlan: LightTherapyPlan | null = null;\n  @State fontSize: number = 20; // 大字体\n  \n  private rhythmEngine: SeniorLightRhythmEngine = SeniorLightRhythmEngine.getInstance();\n  private healthAgent: HealthGuardianAgent = new HealthGuardianAgent();

  aboutToAppear() {\n    this.rhythmEngine.onStateChanged((state) => {\n      this.rhythmState = state;\n      this.currentPlan = this.rhythmEngine.getCurrentPlan();\n    });

n    // 定期更新健康评估\n    setInterval(async () => {\n      this.healthScore = await this.healthAgent.assessHealth();\n      this.riskAssessment = await this.healthAgent.assessRisk();\n    }, 300000); // 每5分钟
  }

  private getScoreColor(score: number): ResourceColor {\n    if (score >= 80) return '#4CAF50';\n    if (score >= 60) return '#FF9800';\n    return '#F44336';\n  }

  private getRiskBgColor(level: string): ResourceColor {\n    switch (level) {\n      case 'low': return 'rgba(76,175,80,0.1)';\n      case 'medium': return 'rgba(255,152,0,0.1)';\n      case 'high': return 'rgba(244,67,54,0.1)';\n      case 'critical': return 'rgba(183,28,28,0.2)';\n      default: return 'transparent';\n    }\n  }

  build() {\n    Column() {\n      // 顶部:当前时间与节律状态\n      Row() {\n        Text(this.getCurrentTime())\n          .fontSize(this.fontSize + 4)\n          .fontColor('#FFFFFF')\n          .fontWeight(FontWeight.Bold)\n        \n        Blank()\n        \n        // 节律阶段指示\n        if (this.currentPlan) {\n          Text(this.getPhaseLabel(this.currentPlan.phase))\n            .fontSize(this.fontSize - 4)\n            .fontColor(this.getPhaseColor(this.currentPlan.phase))\n            .padding({ left: 12, right: 12, top: 6, bottom: 6 })\n            .backgroundColor('rgba(255,255,255,0.1)')\n            .borderRadius(16)\n        }\n      }\n      .width('100%')\n      .height(60)\n      .padding({ left: 20, right: 20 })

      // 健康评分大卡片\n      if (this.healthScore) {\n        Column() {\n          // 综合评分\n          Row() {\n            Column() {\n              Text(`${this.healthScore.overall}`)\n                .fontSize(56)\n                .fontColor(this.getScoreColor(this.healthScore.overall))\n                .fontWeight(FontWeight.Bold)\n              Text('健康分')\n                .fontSize(this.fontSize - 4)\n                .fontColor('rgba(255,255,255,0.6)')\n            }\n            .layoutWeight(1)\n            \n            // 趋势指示\n            Column() {\n              Text(this.healthAgent.getHealthTrend() === 'improving' ? '↗' : \n                   this.healthAgent.getHealthTrend() === 'declining' ? '↘' : '→')\n                .fontSize(36)\n                .fontColor(this.healthAgent.getHealthTrend() === 'improving' ? '#4CAF50' : \n                          this.healthAgent.getHealthTrend() === 'declining' ? '#F44336' : '#FF9800')\n              Text('趋势')\n                .fontSize(this.fontSize - 6)\n                .fontColor('rgba(255,255,255,0.6)')\n            }\n          }\n          .width('100%')\n          \n          // 分项评分\n          Row() {\n            this.ScoreItem('身体', this.healthScore.physical, '#4CAF50')\n            this.ScoreItem('精神', this.healthScore.mental, '#2196F3')\n            this.ScoreItem('睡眠', this.healthScore.sleep, '#9C27B0')\n            this.ScoreItem('用药', this.healthScore.medication, '#FF9800')\n            this.ScoreItem('安全', this.healthScore.safety, '#E91E63')\n          }\n          .width('100%')\n          .margin({ top: 16 })\n        }\n        .width('100%')\n        .padding(20)\n        .backgroundColor('rgba(255,255,255,0.05)')\n        .borderRadius(16)\n        .margin({ top: 16, left: 16, right: 16 })\n      }

      // 风险预警区\n      if (this.riskAssessment && this.riskAssessment.factors.length > 0) {\n        Column() {\n          Text('⚠ 关注提醒')\n            .fontSize(this.fontSize)\n            .fontColor('#FF9800')\n            .fontWeight(FontWeight.Bold)\n            .margin({ bottom: 8 })\n          \n          ForEach(this.riskAssessment.factors, (factor: string) => {\n            Text(`• ${factor}`)\n              .fontSize(this.fontSize - 4)\n              .fontColor('rgba(255,255,255,0.8)')\n              .margin({ top: 4 })\n          })\n          \n          ForEach(this.riskAssessment.recommendations, (rec: string) => {\n            Text(`💡 ${rec}`)\n              .fontSize(this.fontSize - 6)\n              .fontColor('#4CAF50')\n              .margin({ top: 4 })\n          })\n        }\n        .width('100%')\n        .padding(16)\n        .backgroundColor(this.getRiskBgColor(this.riskAssessment.level))\n        .borderRadius(12)\n        .margin({ top: 12, left: 16, right: 16 })\n      }

      // 光疗建议\n      if (this.currentPlan?.warning) {\n        Column() {\n          Text('💡 光疗建议')\n            .fontSize(this.fontSize)\n            .fontColor('#FFD700')\n            .fontWeight(FontWeight.Bold)\n            .margin({ bottom: 8 })\n          \n          Text(this.currentPlan.warning)\n            .fontSize(this.fontSize - 4)\n            .fontColor('rgba(255,255,255,0.9)')\n        }\n        .width('100%')\n        .padding(16)\n        .backgroundColor('rgba(255,215,0,0.1)')\n        .borderRadius(12)\n        .margin({ top: 12, left: 16, right: 16 })\n      }

      // 底部留白\n      Blank()\n        .height(100)\n    }\n    .width('100%')\n    .height('100%')\n    .backgroundColor('#1a1a1a')\n  }

  @Builder\n  ScoreItem(label: string, score: number, color: string) {\n    Column() {\n      Text(`${score}`)\n        .fontSize(24)\n        .fontColor(color)\n        .fontWeight(FontWeight.Bold)\n      Text(label)\n        .fontSize(14)\n        .fontColor('rgba(255,255,255,0.6)')\n    }\n    .layoutWeight(1)\n  }

  private getCurrentTime(): string {\n    const now = new Date();\n    return `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;\n  }

  private getPhaseLabel(phase: string): string {\n    const labels: Record<string, string> = {\n      'dawn': '清晨',\n      'morning': '晨间光疗',\n      'day': '日间',\n      'evening': '傍晚准备',\n      'night': '夜间',\n      'dawn_transition': '起夜照明'\n    };\n    return labels[phase] || phase;\n  }

  private getPhaseColor(phase: string): ResourceColor {\n    const colors: Record<string, ResourceColor> = {\n      'dawn': '#FFD700',\n      'morning': '#FFF8DC',\n      'day': '#FFFFFF',\n      'evening': '#9370DB',\n      'night': '#3F51B5',\n      'dawn_transition': '#FFE4B5'\n    };\n    return colors[phase] || '#FFFFFF';\n  }
}

3.5 主守护工作室页面(GuardianStudio.ets)

typescript 复制代码
// pages/GuardianStudio.ets
import { AdaptiveGuardianPanel } from '../components/AdaptiveGuardianPanel';
import { GuardianNavBall } from '../components/GuardianNavBall';
import { SeniorLightRhythmEngine } from '../services/SeniorLightRhythmEngine';
import { HealthGuardianAgent } from '../services/HealthGuardianAgent';

@Entry
@Component
struct GuardianStudio {
  private rhythmEngine: SeniorLightRhythmEngine = SeniorLightRhythmEngine.getInstance();
  private healthAgent: HealthGuardianAgent = new HealthGuardianAgent();

  aboutToAppear() {
    // 初始化老年人视觉特征    
    this.rhythmEngine.initialize({      
    age: 75,      
    cataractLevel: 0.3,     
    macularDegeneration: false,      
    glaucoma: false,      
    diabetesRetinopathy: false,      
    preferredColorTemp: 3500,      
    lightSensitivity: 0.8    
    });

    // 加载用药计划    
    this.healthAgent.loadMedications([   
     {       
     name: '降压药',        
     dosage: '1片',       
     times: ['08:00', '18:00'],        
     taken: [false, false],        
     instructions: '饭后服用      
     },      
     {        
     name: '维生素D',        
     dosage: '1粒',        
     times: ['08:00'],        
     taken: [false],       
     instructions: '随餐服用'      
     }   
     ]); 
     }

  aboutToDisappear() {    
  this.rhythmEngine.stopEngine(); 
  }

  build() {
   Stack() {     
// 主守护面板     
AdaptiveGuardianPanel()        
.width('100%')        
.height('100%')

      // 悬浮守护球      
      GuardianNavBall()        
      .position({ x: '78%', y: '75%' })    
      }    
      .width('100%')   
      .height('100%')   
      .backgroundColor('#121212')    
      .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) 
       }
}

四、关键技术难点与解决方案

4.1 老年人视觉适配

问题: 老年人视力下降、色觉退化、对眩光敏感,普通UI设计难以满足需求。

方案:

  • 超大字体:默认20px,可一键放大至28px
  • 高对比度:文字与背景对比度不低于7:1
  • 语音优先:所有关键信息均可语音播报
  • 渐进式亮灯:夜间起夜时从0.1lux缓慢升至30lux,避免眩目
  • 色温自适应:傍晚自动降低蓝光,保护褪黑素分泌

4.2 跌倒检测的精准度

问题: 误报(坐下、弯腰被误判为跌倒)和漏报(缓慢跌倒未检测)同时存在。

方案:

  • 多传感器融合:加速度计+毫米波雷达+声音检测(跌倒时的撞击声)
  • 行为模式学习:学习老人的日常活动模式,区分正常坐下与跌倒
  • 分级响应:高置信度直接报警,低置信度先语音确认

4.3 紧急呼叫的可靠性

问题: 网络故障、老人误触、设备电量不足可能导致紧急时刻无法呼叫。

方案:

  • 多重备份:WiFi + 4G + 蓝牙网关(连接邻居设备)
  • 倒计时取消:跌倒检测后30秒倒计时,老人可取消误报
  • 低电量保护:电量低于20%时自动发送提醒给家属
  • 物理按键:设备侧面保留物理SOS按键

4.4 隐私与尊严保护

问题: 持续监控可能让老人感到被监视,影响尊严。

方案:

  • 本地优先:所有数据分析在端侧完成,不上传原始视频
  • 主动控制:老人可随时关闭监控,仅保留紧急呼叫
  • 数据透明:老人可查看哪些数据被收集、发送给谁
  • 家属授权:非紧急情况下,家属查看数据需老人授权

五、效果展示与场景演示

场景一:晨间光疗唤醒

  • 时间: 7:30,老人卧室
  • 系统响应:
    • 检测到环境照度仅200lux,低于晨间光疗目标8000lux
    • 悬浮球变为金色,语音播报:"该起床了,请拉开窗帘接受光照"
    • 自动联动智能窗帘缓慢打开
    • 10分钟后照度提升至6000lux,显示"光疗完成75%"
    • 8:00语音提醒:"该吃降压药了,1片,饭后服用"
  • 效果: 老人白天精神状态改善,夜间入睡时间提前

场景二:跌倒紧急响应

  • 时间: 15:30,客厅
  • 事件: 老人起身时头晕跌倒
  • 系统响应:
    • 毫米波雷达检测到异常姿态(高度骤降+静止)
    • 加速度计确认高G值冲击
    • 悬浮球立即红色闪烁,语音播报:"检测到跌倒,30秒后将呼叫家属"
    • 屏幕显示倒计时,同时展开"我没事/呼叫家属/联系急救"选项
    • 老人点击"我没事",系统取消呼叫,但记录事件
    • 同时推送通知给家属APP:"老人15:30有跌倒事件,已确认无大碍"
  • 效果: 误报率低,响应及时,家属安心

场景三:夜间起夜安全

  • 时间: 凌晨2:00
  • 事件: 老人起夜去卫生间
  • 系统响应:
    • 毫米波雷达检测到床边运动
    • 床头灯从0.1lux开始,10秒内缓慢升至30lux暖光
    • 过道感应灯依次亮起,形成引导光路
    • 卫生间灯自动开启,色温2500K
    • 老人返回床上后,灯光缓慢熄灭
    • 全程无刺眼强光,不影响后续睡眠
  • 效果: 起夜安全,回床后快速入睡

六、总结与展望

本文基于HarmonyOS 6(API 23)的悬浮导航沉浸光感能力,实战构建了一个**"光佑银龄"**智能老人安全守护与健康管理新范式。核心技术突破包括:

  1. 银发节律光感引擎:基于老年人生物钟特征,生成个性化昼夜节律光疗方案
  2. 适老化悬浮导航:超大字体、高对比度、一键紧急呼叫、语音优先
  3. 多模态健康融合:手环+光感+语音三维数据融合,综合健康评估
  4. 渐进式夜间照明:起夜时从微光缓慢渐升,避免眩目和睡眠中断

未来可拓展方向:

  • 结合HarmonyOS分布式能力,将守护扩展至全屋智能设备(智能床垫、防滑地砖、智能马桶)
  • 接入华为智选健康设备生态,实现"手环-血压计-血糖仪-体脂秤"全链路健康监测
  • 开发家属端鸿蒙应用,利用大屏优势实现"多老人健康对比+长期趋势分析"
  • 引入生成式AI,根据老人健康数据生成个性化养老方案和营养建议

转载自:https://blog.csdn.net/u014727709/article/details/162348310

欢迎 👍点赞✍评论⭐收藏,欢迎指正