如何计算手机抛掷高度的距离

Hap*_*wan 5 velocity flutter

在 Flutter 中,有一个传感器包https://pub.dev/packages/sensors可以用来了解速度 X、Y 和 Z。

我的问题是:如何计算手机投掷高度的距离?

示例:您将手机扔到距离地面 0.5 米的地方。手机距您的手 1 米(即距地面 1.5 米)。

我怎样才能得到1米的值?

谢谢大家!

这是我现在的代码(您需要安装传感器包):

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:sensors/sensors.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  List _velocityY = [];
  DateTime time;
  List<double> distances = [];

  List<StreamSubscription<dynamic>> _streamSubscriptions =
  <StreamSubscription<dynamic>>[];

  @override
  void initState() {
    super.initState();

    _streamSubscriptions
      .add(userAccelerometerEvents.listen((UserAccelerometerEvent event)
    {
      setState(() {
        if (event.y.abs() > 0.1) {
          if (time != null) {
            _velocityY.add(event.y);
          }
          //print((new DateTime.now().difference(time).inSeconds));
          if (_velocityY.length > 0) {
            distances.add(_velocityY[_velocityY.length - 1] * (new DateTime.now().difference(time).inMicroseconds) / 1000);
          }

          time = new DateTime.now();
        }

        //print('time' + time.toString());
      });
    }));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: ListView(
        children: [
          for(double distance in distances.reversed.toList())
            Text(
              distance.toStringAsFixed(2),
              style: Theme.of(context).textTheme.headline4,
            ),
        ],
      ),// This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

Boo*_*unz 3

Correct me if I'm wrong, but if you're trying to calculate a phone's absolute position in space at any moment in time directly from (past and present) accelerometer data, that is actually very complex, mainly because the phone's accelerometer's frame of reference in terms of x, y, and z is the phone itself... and phones are not in a fixed orientation, especially when being thrown around, and besides... it will have zero acceleration while in the air, anyway.

It's sort of like being blindfolded and being taken on a space journey in a pod with rockets that fire in different directions randomly, and being expected to know where you are at the end. That would be technically possible if you knew where you were when you started, and you had the ability to track every acceleration vector you felt along the way... and integrate this with gyroscope data as well... converting all this into a single path.

But, luckily, we can still get the height thrown from the accelerometer indirectly, along with some other measurements.

This solution assumes that:

  • The sensors package provides acceleration values, NOT velocity values (even though it claims to provide velocity, strangely), because accelerometers themselves provide acceleration.
  • Total acceleration is equal to sqrt(x^2 + y^2 + z^2) regardless of phone orientation.
  • The accelerometer will read zero (or gravity only) during the throw
  • This article in wired is correct in that Height = (Gravity * Time^2) / 8

The way my code works is:

  • You (user) hold the "GO" button down.
  • 当您抬起手机时,您自然会松开按钮,这会启动计时器,并且手机开始侦听加速计事件。
  • 我们假设手机在空气中的总加速度为零(或仅重力,取决于所选的加速度计数据类型)......所以我们实际上并没有尝试直接根据加速度计数据计算距离:
  • 相反,我们仅使用加速度计来检测您何时拿起电话……通过使用阈值检测加速度的突然变化。
  • 当达到该阈值时,计时器停止。
  • 现在我们有了从开始到结束的投掷总时间值,并且可以计算高度。

旁注:

  • 我使用的是 AccelerometerEvent(包括重力),而不是 UserAccelerometer 事件(不包括重力),因为我使用 UserAccelerometerEvent 在测试设备上得到了奇怪的数字(静止时非零)。
  • 它有助于轻轻地接电话***
  • 我的数学可能已经完成了......我还没有其他人看过这个......但至少这个答案让你开始了解一个可行的基本理论。
  • 我的手机掉到了狗屎里,所以我希望你接受这个答案。

准确性限制:

  • 放手和接住的高度自然会不一致。
  • 该阈值是实验性的......自己测试不同的值。我已经定了10了
  • 按下 GO 按钮和计时器开始之间可能存在一些延迟。
  • *** 如果减速结束得太快,阈值可能无法始终被准确检测到,或者根本无法检测到,因为传感器包提供的加速度计更新频率非常低。也许有一种方法可以使用不同的软件包以更高的频率获取更新。
  • 总是有可能过早按下 GO 按钮(当手机仍在您手中时),因此此时加速度将不为零,并且可能足以触发阈值。
  • 可能还有其他事情尚未考虑。

代码:

import 'package:flutter/material.dart';
import 'package:sensors/sensors.dart';
import 'dart:async';
import 'dart:math';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Phone Throw Height',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Phone Throw Height'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<StreamSubscription<dynamic>> _streamSubscriptions =
      <StreamSubscription<dynamic>>[];

  DateTime? startTime;
  DateTime? endTime;
  bool isBeingThrown = false;
  final double GRAVITATIONAL_FORCE = 9.80665;
  final double DECELERATION_THRESHOLD = 10; // <---- experimental
  List<double> accelValuesForAnalysis = <double>[];

  @override
  void initState() {
    super.initState();

    _streamSubscriptions
        .add(accelerometerEvents.listen((AccelerometerEvent event) {
      if (isBeingThrown) {
        double x_total = pow(event.x, 2).toDouble();
        double y_total = pow(event.y, 2).toDouble();
        double z_total = pow(event.z, 2).toDouble();

        double totalXYZAcceleration = sqrt(x_total + y_total + z_total);

        // only needed because we are not using UserAccelerometerEvent
        // (because it was acting weird on my test phone Galaxy S5)
        double accelMinusGravity = totalXYZAcceleration - GRAVITATIONAL_FORCE;

        accelValuesForAnalysis.add(accelMinusGravity);
        if (accelMinusGravity > DECELERATION_THRESHOLD) {
          _throwHasEnded();
        }
      }
    }));
  }

  void _throwHasEnded() {
    isBeingThrown = false;
    endTime = DateTime.now();
    Duration totalTime = DateTime.now().difference(startTime!);
    double totalTimeInSeconds = totalTime.inMilliseconds / 1000;
    // this is the equation from the wired article
    double heightInMeters =
        (GRAVITATIONAL_FORCE * pow(totalTimeInSeconds, 2)) / 8;

    Widget resetButton = TextButton(
      child: Text("LONG PRESS TO RESET"),
      onPressed: () {},
      onLongPress: () {
        startTime = null;
        endTime = null;
        print(accelValuesForAnalysis.toString());
        accelValuesForAnalysis.clear();
        Navigator.pop(context);
        setState(() {
          isBeingThrown = false;
        });
      },
    );

    AlertDialog alert = AlertDialog(
      title: Text("Throw End Detected"),
      content: Text("total throw time in seconds was: " +
          totalTimeInSeconds.toString() +
          "\n" +
          "Total height was: " +
          heightInMeters.toString() +
          " meters. \n"),
      actions: [
        resetButton,
      ],
    );

    showDialog(
      barrierDismissible: false,
      context: context,
      builder: (BuildContext context) {
        return alert;
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: SizedBox.expand(
        child: Container(
          color: Colors.green,
          //alignment: Alignment.center,
          child: SizedBox.expand(
            child: (!isBeingThrown)
                ? TextButton(
                    child: Text("GO!",
                        style: TextStyle(
                            color: Colors.white,
                            fontWeight: FontWeight.bold,
                            fontSize: 40)),
                    onPressed: () {
                      setState(() {
                        isBeingThrown = true;
                        startTime = DateTime.now();
                      });
                    },
                  )
                : Center(
                    child: Text("weeeeeeeeee!",
                        style: TextStyle(
                            color: Colors.white,
                            fontWeight: FontWeight.bold,
                            fontSize: 40)),
                  ),
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    // cancel the stream from the accelerometer somehow!! ugh!!!
    for (StreamSubscription<dynamic> subscription in _streamSubscriptions) {
      subscription.cancel();
    }
    super.dispose();
  }
}
Run Code Online (Sandbox Code Playgroud)