如何基于Flutter传感器包计算被竖直抛起的手机的上升距离
竖直抛手机上升距离计算方案
核心原理
要通过加速度传感器计算上升高度,需要做两次积分:
- 第一次:对竖直方向的加速度按时间积分,得到当前竖直速度
- 第二次:对竖直速度按时间积分,得到累计的竖直位移
当手机上升到最高点时,竖直方向速度为0,此时的累计位移就是你要的上升高度。
现有代码的问题
- 单位换算错误:时间差取的是微秒,转成秒需要除以
1000000,不是1000,量级差了1000倍 - 积分逻辑错误:直接用单次加速度乘时间差当作位移,跳过了速度累积的步骤,结果完全不准确
- 没有结束判断:没有检测最高点的逻辑,会一直累加位移
修正后的实现方案
参考如下代码修改,核心是增加速度累积、基线校准、最高点判断逻辑:
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:sensors/sensors.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter 抛高计算', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: const MyHomePage(title: '抛高计算 demo'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { final List<StreamSubscription> _streamSubscriptions = []; // 加速度基线校准值,抵消静止时的传感器偏移 double _accelYOffset = 0; // 当前竖直速度 单位:m/s double _currentVelocityY = 0; // 累计上升位移 单位:m double _totalHeight = 0; // 上次采样的时间 DateTime? _lastTime; // 是否处于抛射计算状态 bool _isCounting = false; // 存储历史高度结果 List<double> heightResults = []; @override void initState() { super.initState(); // 先校准1秒的加速度基线,降低静态误差 _calibrateOffset(); } Future<void> _calibrateOffset() async { List<double> tempOffsets = []; StreamSubscription sub = userAccelerometerEvents.listen((event) { tempOffsets.add(event.y); }); await Future.delayed(const Duration(seconds: 1)); sub.cancel(); // 取平均值作为偏移值 _accelYOffset = tempOffsets.reduce((a, b) => a + b) / tempOffsets.length; // 校准完成后开始监听加速度数据 _startListening(); } void _startListening() { _streamSubscriptions.add( userAccelerometerEvents.listen((UserAccelerometerEvent event) { setState(() { // 减去偏移得到真实的Y轴加速度,Y轴向上为正方向 double realAccelY = event.y - _accelYOffset; DateTime now = DateTime.now(); if (_lastTime != null) { // 计算两次采样的时间差,转换为秒 double deltaT = now.difference(_lastTime!).inMicroseconds / 1e6; // 第一次积分:加速度*时间差 = 速度增量,累加得到当前速度 _currentVelocityY += realAccelY * deltaT; // 检测抛射开始:速度超过0.5m/s阈值就启动位移计算 if (_currentVelocityY > 0.5 && !_isCounting) { _isCounting = true; _totalHeight = 0; } // 处于计算状态时累加位移 if (_isCounting) { // 第二次积分:速度*时间差 = 位移增量,累加得到总上升高度 _totalHeight += _currentVelocityY * deltaT; // 检测最高点:速度从正变负,说明到达最高点 if (_currentVelocityY <= 0) { _isCounting = false; // 保留两位小数存入结果列表 heightResults.insert(0, double.parse(_totalHeight.toStringAsFixed(2))); // 重置速度等待下一次抛射 _currentVelocityY = 0; } } } _lastTime = now; }); }), ); } @override void dispose() { for (var sub in _streamSubscriptions) { sub.cancel(); } super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: ListView( padding: const EdgeInsets.all(16), children: [ const Text('抛射高度结果(单位:米):', style: TextStyle(fontSize: 18)), const SizedBox(height: 16), for (double height in heightResults) Text( '$height m', style: Theme.of(context).textTheme.headline4, ), ], ), ); } }
使用注意事项
- 测试时竖直握持手机,保证Y轴方向和重力方向平行
- 抛射时尽量保持手机竖直向上,不要有旋转,避免其他方向加速度干扰结果
- 消费级传感器存在固有误差,多次测试取平均值可以提高精度
内容的提问来源于stack exchange,提问作者Happyriri
相关产品推荐
相关产品推荐

