React Native如何通过Accelerometer/Gyroscope获取手机运动加速度与方向
Expo 加速度计数据解析与运动加速度、方向获取方案
先搞懂原始返回数据的含义
Expo加速度计返回的采样结果是包含x、y、z三个字段的对象,单位为m/s²,注意原始值默认包含重力加速度分量,三个轴的对应方向以手机自身坐标系为基准:
- x轴:沿手机左右方向,手机右侧方向为正,左侧方向为负
- y轴:沿手机上下方向,手机顶部方向为正,底部方向为负
- z轴:垂直于手机屏幕平面,屏幕朝向观察者时,指向观察者的方向为正,反之为负
举个最直观的例子:手机水平静止平放在桌面、屏幕朝上时,你拿到的原始值大概是
{x: 0, y: 0, z: 9.8},这个9.8就是竖直方向的重力加速度,是计算纯运动加速度时必须过滤掉的干扰项。
提取纯运动加速度(去除重力干扰)
新手最容易上手的方案是用低通滤波分离重力分量,不需要额外依赖,代码直接可用:
import { Accelerometer } from 'expo-sensors'; import { useState, useEffect } from 'react'; import { StyleSheet, Text, View } from 'react-native'; // 重力滤波系数,取值在0.1-0.3之间调整即可,值越大重力跟踪越慢、结果越平稳 const GRAVITY_ALPHA = 0.2; // 噪声阈值,小于该值的加速度直接判定为静止,过滤传感器固有抖动 const NOISE_THRESHOLD = 0.2; export default function MotionSensor() { const [pureAcceleration, setPureAcceleration] = useState({ x: 0, y: 0, z: 0 }); // 缓存实时计算的重力分量 const gravityVec = { x: 0, y: 0, z: 0 }; useEffect(() => { // 设置采样间隔,20ms对应50Hz采样率,完全满足普通运动检测需求,过高会增加耗电 Accelerometer.setUpdateInterval(20); const subscription = Accelerometer.addListener(rawData => { // 低通滤波迭代计算当前重力分量 gravityVec.x = rawData.x * GRAVITY_ALPHA + gravityVec.x * (1 - GRAVITY_ALPHA); gravityVec.y = rawData.y * GRAVITY_ALPHA + gravityVec.y * (1 - GRAVITY_ALPHA); gravityVec.z = rawData.z * GRAVITY_ALPHA + gravityVec.z * (1 - GRAVITY_ALPHA); // 原始值减去重力分量,得到仅由用户运动产生的加速度 const motionAccel = { x: Math.abs(rawData.x - gravityVec.x) < NOISE_THRESHOLD ? 0 : rawData.x - gravityVec.x, y: Math.abs(rawData.y - gravityVec.y) < NOISE_THRESHOLD ? 0 : rawData.y - gravityVec.y, z: Math.abs(rawData.z - gravityVec.z) < NOISE_THRESHOLD ? 0 : rawData.z - gravityVec.z, }; setPureAcceleration(motionAccel); }); // 组件卸载时取消监听,避免内存泄漏 return () => subscription.remove(); }, []); return ( <View style={styles.container}> <Text style={styles.title}>纯运动加速度(单位:m/s²)</Text> <Text>左右方向(x): {pureAcceleration.x.toFixed(2)}</Text> <Text>上下方向(y): {pureAcceleration.y.toFixed(2)}</Text> <Text>垂直屏幕方向(z): {pureAcceleration.z.toFixed(2)}</Text> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', paddingHorizontal: 24 }, title: { fontSize: 16, fontWeight: 'bold', marginBottom: 16 } });
如果需要更高精度的结果,可以直接调用Expo内置的设备运动接口,它已经在原生层做好了重力分离、姿态融合,会直接返回去除重力的用户加速度、设备旋转角度,不需要自己写滤波逻辑。
运动方向判断逻辑
拿到过滤后的纯加速度值之后,判断逻辑非常直接:
- 单轴方向:看对应轴的数值正负即可,比如x值为正就是向手机右侧运动,为负就是向左侧运动;y值为正就是向手机顶部方向运动,为负就是向底部方向运动;z值为正就是向屏幕朝向的方向运动,为负就是向屏幕背面方向运动
- 运动剧烈程度:计算合加速度大小
const totalForce = Math.sqrt(pureAcceleration.x ** 2 + pureAcceleration.y ** 2 + pureAcceleration.z ** 2),数值越大说明运动幅度越大,比如快速晃手机时这个值会超过10,静止状态下基本为0 - 做动作识别(比如甩动、计步)的时候,记得加时间窗口判断:只有加速度超过阈值的状态持续一定时长(比如100ms以上),才判定为有效动作,避免偶然抖动造成误判
新手踩坑提醒
- 安卓高版本需要在项目配置里声明运动传感器权限,否则会拿不到数据
- 不要在传感器回调里做复杂计算,不然会阻塞UI线程造成卡顿,复杂逻辑可以丢到工作线程处理
- 采样间隔不要设得太短,普通场景20ms间隔足够,10ms以下的采样率对运动检测没有明显提升,还会大幅增加耗电
内容的提问来源于stack exchange,提问作者Riadh-Saad
相关产品推荐
相关产品推荐

