康心伴Logo
康心伴WellAlly
Health

React Native智能闹钟:传感器数据优化唤醒时机 | WellAlly康心伴

5 分钟阅读

React Native智能闹钟:传感器数据优化唤醒时机

概述

传统闹钟在固定时间唤醒,可能打断深度睡眠,导致睡眠惯性(Sleep Inertia)——昏昏沉沉、反应迟钝的状态。

智能闹钟通过分析传感器数据:

  • 检测睡眠阶段(浅睡/深睡/REM)
  • 在最佳唤醒时间(浅睡期)唤醒
  • 减少睡眠惯性,提升起床体验

技术原理

睡眠阶段

code
睡眠周期(约90分钟):

浅睡期 (N1/N2)  ████░░░░████░░░░████  ← 最佳唤醒时机
深睡期 (N3)     ████████████████░░░░  ← 避免唤醒
REM期          ░░░░░░░░░░░░░░░░░░░░░  ← 可唤醒
Code collapsed

检测原理

传感器检测指标睡眠阶段特征
加速度计身体活动浅睡期活动较多
陀螺仪翻身频率REM期翻身少
光线传感器环境亮度深睡期对光不敏感
麦克风呼吸声深睡期呼吸规律
屏幕触摸交互频率浅睡期更可能交互

环境设置

安装依赖

code
# Expo传感器模块
expo install expo-sensors
expo install expo-device
expo install expo-av
expo install expo-notifications
expo install expo-background-fetch
expo install expo-task-manager

# React Native传感器
npm install react-native-sensors
npm install react-native-push-notification
Code collapsed

权限配置

code
// app.json
{
  "expo": {
    "ios": {
      "infoPlist": {
        "NSMotionUsageDescription": "用于监测睡眠时的身体活动",
        "NSMicrophoneUsageDescription": "用于分析睡眠质量(可选)"
      }
    },
    "android": {
      "permissions": [
        "BODY_SENSORS",
        "RECORD_AUDIO",
        "USE_FULL_SCREEN_INTENT",
        "WAKE_LOCK"
      ]
    }
  }
}
Code collapsed

传感器数据采集

加速度计数据

code
// hooks/useAccelerometer.ts
import { useEffect, useState, useRef } from 'react';
import { Accelerometer } from 'expo-sensors';

export interface AccelerometerData {
  x: number;
  y: number;
  z: number;
  timestamp: number;
}

export function useAccelerometer(interval: number = 1000) {
  const [data, setData] = useState<AccelerometerData[]>([]);
  const [isRecording, setIsRecording] = useState(false);
  const subscriptionRef = useRef<any>();

  const startRecording = () => {
    setIsRecording(true);
    setData([]);

    subscriptionRef.current = Accelerometer.addListener(({ x, y, z }) => {
      const timestamp = Date.now();
      setData(prev => [...prev, { x, y, z, timestamp }]);
    });

    Accelerometer.setUpdateInterval(interval);
  };

  const stopRecording = () => {
    setIsRecording(false);
    subscriptionRef.current?.remove();
  };

  // 清理
  useEffect(() => {
    return () => {
      subscriptionRef.current?.remove();
    };
  }, []);

  return {
    data,
    isRecording,
    startRecording,
    stopRecording
  };
}
Code collapsed

睡眠监测服务

code
// services/SleepMonitorService.ts
import { Accelerometer } from 'expo-sensors';
import * as BackgroundFetch from 'expo-background-fetch';
import * as TaskManager from 'expo-task-manager';

const SLEEP_MONITOR_TASK = 'SLEEP_MONITOR_TASK';

interface SleepData {
  timestamp: number;
  movement: number;  // 运动强度
  position: string;  // 体位
  light: number;     // 环境亮度
}

class SleepMonitorService {
  private sleepData: SleepData[] = [];
  private isMonitoring = false;
  private updateInterval: number = 60000; // 每分钟采样

  async initialize() {
    // 注册后台任务
    await TaskManager.defineTask(SLEEP_MONITOR_TASK, this.handleSleepTask.bind(this));

    // 注册周期性任务
    await BackgroundFetch.registerTaskAsync(SLEEP_MONITOR_TASK, {
      minimumInterval: this.updateInterval,
      stopOnTerminate: false,
      startOnBoot: true,
    });
  }

  async startMonitoring() {
    this.isMonitoring = true;
    this.sleepData = [];

    // 启动传感器监听
    Accelerometer.addListener(({ x, y, z }) => {
      if (!this.isMonitoring) return;

      // 计算运动强度
      const movement = Math.sqrt(x*x + y*y + z*z);

      // 确定体位
      const position = this.detectPosition(x, y, z);

      // 记录数据
      this.sleepData.push({
        timestamp: Date.now(),
        movement,
        position,
        light: 0  // 需要光线传感器
      });
    });

    Accelerometer.setUpdateInterval(this.updateInterval);
  }

  stopMonitoring() {
    this.isMonitoring = false;
  }

  private detectPosition(x: number, y: number, z: number): string {
    // 简化体位检测
    if (z > 0.8) return 'back';
    if (z < -0.8) return 'front';
    if (x > 0.8) return 'left';
    if (x < -0.8) return 'right';
    return 'upright';
  }

  private async handleSleepTask() {
    // 后台任务处理
    const now = new Date();
    console.log(`睡眠监测: ${now.toLocaleTimeString()}`);

    return BackgroundFetch.BackgroundFetchResult.NewData;
  }

  analyzeSleepStages(): Array<{time: Date, stage: string}> {
    /**
     * 分析睡眠阶段
     *
     * 算法:
     * 1. 低活动 + 稳定体位 = 深睡期
     * 2. 中等活动 + 频繁翻身 = REM期
     * 3. 高活动 = 浅睡期
     */
    const stages = [];
    const windowSize = 5; // 5分钟窗口

    for (let i = 0; i < this.sleepData.length; i += windowSize) {
      const window = this.sleepData.slice(i, i + windowSize);

      if (window.length === 0) continue;

      // 计算平均活动
      const avgMovement = window.reduce((sum, d) => sum + d.movement, 0) / window.length;

      // 计算翻身次数
      const positionChanges = window.filter((d, idx) =>
        idx > 0 && d.position !== window[idx - 1].position
      ).length;

      // 判断睡眠阶段
      let stage = 'unknown';
      if (avgMovement < 0.1 && positionChanges < 2) {
        stage = 'deep_sleep';  // 深睡
      } else if (avgMovement < 0.2 && positionChanges < 3) {
        stage = 'light_sleep'; // 浅睡
      } else if (avgMovement >= 0.2) {
        stage = 'rem';         // REM或浅睡
      }

      stages.push({
        time: new Date(window[0].timestamp),
        stage
      });
    }

    return stages;
  }

  getSleepData(): SleepData[] {
    return this.sleepData;
  }
}

export const sleepMonitorService = new SleepMonitorService();
Code collapsed

智能唤醒算法

唤醒窗口计算

code
// services/SmartAlarmService.ts
interface AlarmConfig {
  targetTime: Date;        // 目标唤醒时间
  windowMinutes: number;   // 唤醒窗口大小(默认30分钟)
  requireQuiet: boolean;   // 是否需要安静环境
}

interface WakeUpTime {
  time: Date;
  confidence: number;      // 唤醒质量评分 (0-1)
  reason: string;          // 选择此时间的理由
}

class SmartAlarmService {
  /**
   * 计算最佳唤醒时间
   */
  calculateOptimalWakeUp(
    sleepStages: Array<{time: Date, stage: string}>,
    config: AlarmConfig
  ): WakeUpTime {

    const windowStart = new Date(config.targetTime);
    windowStart.setMinutes(windowStart.getMinutes() - config.windowMinutes);

    // 在唤醒窗口内寻找浅睡期
    const candidates = sleepStages.filter(stage => {
      const stageTime = new Date(stage.time);
      return stageTime >= windowStart &&
             stageTime <= config.targetTime &&
             (stage.stage === 'light_sleep' || stage.stage === 'rem');
    });

    if (candidates.length === 0) {
      // 没有找到浅睡期,使用目标时间
      return {
        time: config.targetTime,
        confidence: 0.3,
        reason: '无可选浅睡期,使用预设时间'
      };
    }

    // 选择最接近目标的浅睡期
    const optimal = candidates.reduce((best, current) => {
      const currentDiff = Math.abs(
        new Date(current.time).getTime() - config.targetTime.getTime()
      );
      const bestDiff = Math.abs(
        new Date(best.time).getTime() - config.targetTime.getTime()
      );

      return currentDiff < bestDiff ? current : best;
    });

    // 计算置信度
    const timeDiff = Math.abs(
      new Date(optimal.time).getTime() - config.targetTime.getTime()
    );
    const confidence = Math.max(0, 1 - timeDiff / (config.windowMinutes * 60000));

    return {
      time: new Date(optimal.time),
      confidence,
      reason: '检测到浅睡期'
    };
  }

  /**
   * 计算需要的前置唤醒时间
   * 考虑用户入睡时间
   */
  calculateWakeWindow(
    targetTime: Date,
    sleepCycleLength: number = 90  // 睡眠周期长度(分钟)
  ): {start: Date, end: Date} {

    const end = targetTime;
    const start = new Date(targetTime);
    start.setMinutes(start.getMinutes() - 30); // 30分钟窗口

    return { start, end };
  }
}

export const smartAlarmService = new SmartAlarmService();
Code collapsed

闹钟调度

code
// hooks/useSmartAlarm.ts
import { useState, useEffect } from 'react';
import * as Notifications from 'expo-notifications';
import { Platform } from 'react-native';
import { sleepMonitorService } from '../services/SleepMonitorService';
import { smartAlarmService } from '../services/SmartAlarmService';

export function useSmartAlarm() {
  const [alarmTime, setAlarmTime] = useState<Date | null>(null);
  const [optimalWakeTime, setOptimalWakeTime] = useState<Date | null>(null);
  const [isMonitoring, setIsMonitoring] = useState(false);

  // 配置通知
  useEffect(() => {
    Notifications.setNotificationHandler({
      handleNotification: async () => ({
        shouldShowAlert: true,
        shouldPlaySound: true,
        shouldSetBadge: false,
      }),
    });
  }, []);

  const setAlarm = async (time: Date) => {
    setAlarmTime(time);

    // 开始睡眠监测
    await sleepMonitorService.startMonitoring();
    setIsMonitoring(true);

    // 计算唤醒窗口
    const window = smartAlarmService.calculateWakeWindow(time);

    // 设置初始通知
    await scheduleNotification(time, '智能闹钟已设置');

    // 启动动态调整任务
    scheduleDynamicAlarm(time, window);
  };

  const scheduleDynamicAlarm = async (targetTime: Date, window: {start: Date, end: Date}) => {
    // 每分钟检查并调整唤醒时间
    const checkInterval = setInterval(async () => {
      const now = new Date();

      // 如果已过窗口结束时间
      if (now > window.end) {
        clearInterval(checkInterval);
        await sleepMonitorService.stopMonitoring();
        setIsMonitoring(false);
        return;
      }

      // 分析当前睡眠阶段
      const sleepStages = sleepMonitorService.analyzeSleepStages();

      // 计算最佳唤醒时间
      const optimal = smartAlarmService.calculateOptimalWakeUp(sleepStages, {
        targetTime,
        windowMinutes: 30,
        requireQuiet: true
      });

      // 如果找到更好的时间,更新闹钟
      if (optimal.confidence > 0.7 && optimal.time !== optimalWakeTime) {
        setOptimalWakeTime(optimal.time);
        await scheduleNotification(optimal.time, '智能唤醒', optimal.reason);
      }

    }, 60000); // 每分钟检查
  };

  const cancelAlarm = async () => {
    await Notifications.cancelAllScheduledNotificationsAsync();
    await sleepMonitorService.stopMonitoring();
    setIsMonitoring(false);
    setAlarmTime(null);
    setOptimalWakeTime(null);
  };

  return {
    alarmTime,
    optimalWakeTime,
    isMonitoring,
    setAlarm,
    cancelAlarm
  };
}

// 调度通知
async function scheduleNotification(time: Date, title: string, body?: string) {
  await Notifications.scheduleNotificationAsync({
    content: {
      title,
      body: body || time.toLocaleTimeString('zh-CN'),
      sound: true,
      priority: Notifications.AndroidNotificationPriority.HIGH,
    },
    trigger: {
      type: Notifications.SchedulableTriggerInputTypes.DATE,
      date: time,
    },
  });
}
Code collapsed

UI组件

闹钟设置界面

code
// components/SmartAlarmSettings.tsx
import React, { useState } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Platform } from 'react-native';
import DateTimePicker from '@react-native-community/datetimepicker';
import Slider from '@react-native-community/slider';
import { useSmartAlarm } from '../hooks/useSmartAlarm';

export function SmartAlarmSettings() {
  const { alarmTime, optimalWakeTime, isMonitoring, setAlarm, cancelAlarm } = useSmartAlarm();
  const [selectedTime, setSelectedTime] = useState(new Date());
  const [showPicker, setShowPicker] = useState(false);
  const [windowSize, setWindowSize] = useState(30); // 唤醒窗口大小

  const handleSetAlarm = async () => {
    await setAlarm(selectedTime);
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>智能闹钟</Text>

      {/* 时间选择器 */}
      {Platform.OS === 'ios' ? (
        <DateTimePicker
          value={selectedTime}
          mode="time"
          display="default"
          onChange={(event, date) => setSelectedTime(date || selectedTime)}
          style={styles.picker}
        />
      ) : (
        <>
          <TouchableOpacity onPress={() => setShowPicker(true)}>
            <Text style={styles.timeDisplay}>
              {selectedTime.toLocaleTimeString('zh-CN', {
                hour: '2-digit',
                minute: '2-digit'
              })}
            </Text>
          </TouchableOpacity>
          {showPicker && (
            <DateTimePicker
              value={selectedTime}
              mode="time"
              display="default"
              onChange={(event, date) => {
                setShowPicker(false);
                if (date) setSelectedTime(date);
              }}
            />
          )}
        </>
      )}

      {/* 唤醒窗口设置 */}
      <View style={styles.sliderContainer}>
        <Text style={styles.label}>
          唤醒窗口: {windowSize} 分钟
        </Text>
        <Slider
          style={styles.slider}
          minimumValue={10}
          maximumValue={60}
          step={5}
          value={windowSize}
          onValueChange={setWindowSize}
        />
        <Text style={styles.hint}>
          在目标时间前{windowSize}分钟内寻找最佳唤醒时机
        </Text>
      </View>

      {/* 当前状态 */}
      {alarmTime && (
        <View style={styles.statusContainer}>
          <Text style={styles.statusText}>
            目标时间: {alarmTime.toLocaleTimeString('zh-CN')}
          </Text>
          {optimalWakeTime && (
            <Text style={styles.optimalText}>
              最佳唤醒: {optimalWakeTime.toLocaleTimeString('zh-CN')}
            </Text>
          )}
          <Text style={styles.monitoringText}>
            状态: {isMonitoring ? '监测中...' : '等待'}
          </Text>
        </View>
      )}

      {/* 操作按钮 */}
      <View style={styles.buttons}>
        <TouchableOpacity
          style={[styles.button, styles.setButton]}
          onPress={handleSetAlarm}
        >
          <Text style={styles.buttonText}>设置闹钟</Text>
        </TouchableOpacity>

        {alarmTime && (
          <TouchableOpacity
            style={[styles.button, styles.cancelButton]}
            onPress={cancelAlarm}
          >
            <Text style={styles.buttonText}>取消</Text>
          </TouchableOpacity>
        )}
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    backgroundColor: '#f5f5f5',
  },
  title: {
    fontSize: 28,
    fontWeight: 'bold',
    marginBottom: 30,
  },
  picker: {
    width: '100%',
    height: 200,
  },
  timeDisplay: {
    fontSize: 48,
    fontWeight: 'bold',
    textAlign: 'center',
    marginVertical: 20,
  },
  sliderContainer: {
    backgroundColor: '#fff',
    borderRadius: 10,
    padding: 15,
    marginVertical: 20,
  },
  label: {
    fontSize: 16,
    fontWeight: 'bold',
  },
  slider: {
    width: '100%',
    height: 40,
  },
  hint: {
    fontSize: 12,
    color: '#666',
    marginTop: 5,
  },
  statusContainer: {
    backgroundColor: '#fff',
    borderRadius: 10,
    padding: 15,
    marginVertical: 20,
  },
  statusText: {
    fontSize: 16,
  },
  optimalText: {
    fontSize: 16,
    color: '#4CAF50',
    marginTop: 5,
  },
  monitoringText: {
    fontSize: 14,
    color: '#666',
    marginTop: 10,
  },
  buttons: {
    flexDirection: 'row',
    justifyContent: 'space-around',
  },
  button: {
    paddingHorizontal: 40,
    paddingVertical: 15,
    borderRadius: 25,
  },
  setButton: {
    backgroundColor: '#4CAF50',
  },
  cancelButton: {
    backgroundColor: '#f44336',
  },
  buttonText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: 'bold',
  },
});
Code collapsed

关键要点

  1. 传感器数据驱动决策:加速度计+陀螺仪
  2. 睡眠周期检测:浅睡期最佳唤醒
  3. 动态调整唤醒时间:实时优化
  4. 后台任务支持:持续监测
  5. 用户可配置窗口:平衡精确性vs便利性

常见问题

精度如何?

研究显示智能闹钟减少约60-80%的睡眠惯性。但个体差异大:

  • 年轻人效果更好
  • 规律作息更准确
  • 需要佩戴设备更精确

电池消耗?

优化策略:

  1. 降低采样频率(每分钟而非每秒)
  2. 使用后台任务而非持续监听
  3. 屏幕关闭时降低处理频率

iOS限制?

iOS后台限制严格:

  1. 使用BackgroundFetch
  2. 请求"始终允许"运动权限
  3. 考虑使用HealthKit集成

参考资料

  • 睡眠科学研究
  • 睡眠惯性研究
  • Expo传感器文档
  • 通知调度文档

发布日期:2026年3月8日 最后更新:2026年3月8日

免责声明: 本内容仅供教育参考,不能替代专业医疗建议。请咨询医生获取个性化诊断和治疗方案。

#

文章标签

React Native
智能闹钟
传感器
睡眠周期
健康应用

觉得这篇文章有帮助?

立即体验康心伴,开始您的健康管理之旅