In Flutter, there is the sensor package https://pub.dev/packages/sensors that allow to know the velocity X, Y and Z.
My question is : how could I calculate the distance of a phone thrown in height ?
Example : you throw your telephone, with your hand at 0.5 meter from the ground. The phone reaching 1 meter from your hand (so 1.5 meter from the ground).
How can I get the 1 meter value ?
Thanks all !
Here is the code I have right now (you need to install sensors package):
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.
);
}
}
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 way my code works is:
Side notes:
Limitations on Accuracy:
Code:
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();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With