Skip to content

Instantly share code, notes, and snippets.

@micahstubbs
Last active October 8, 2017 05:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save micahstubbs/0d7ac58c57c9cd663de5ae136e8bc405 to your computer and use it in GitHub Desktop.
Save micahstubbs/0d7ac58c57c9cd663de5ae136e8bc405 to your computer and use it in GitHub Desktop.
Inferring device position from acceleration
license: mit

so getting device position from an accelerometer and gyroscope is, like, nontrivial, due to noise and very severe error propagation in double-integrating the sensor data. here is a good answer that will smash your dreams.

but i'm just lookin for somethin very very rough..... like, oh, you moved the phone upwards.

runge-kutta would be better than euler integration: python, javascript.

and a kalman filter might help clean up the sensor data.

ugh : )

gnight for now

forked from tophtucker's block: Inferring device position from acceleration

<!DOCTYPE html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* {
box-sizing: border-box;
}
html, body {
margin: 0;
width: 100%;
height: 100%;
position: relative;
}
body {
perspective: 100px;
padding: 100px;
}
svg {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
path {
stroke: black;
stroke-width: 1;
fill: none;
}
#phone {
border: 5px solid black;
width: 100%;
height: 100%;
}
</style>
<body>
<svg>
</svg>
<div id="phone"></div>
</body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
// store current rotation in euler angles
var rotation = {
alpha: 0, beta: 0, gamma: 0
};
// store whole history of acceleration and implied velocity and position,
// starting from these initial conditions
var z = [
{
position: 0,
velocity: 0,
acceleration: 0,
time: undefined
}
];
// visualize motion through a box
var phone = d3.select('#phone');
window.addEventListener('devicemotion', handleMotion);
window.addEventListener('deviceorientation', handleOrientation);
window.addEventListener('mousemove', handleMousemove);
d3.timer(renderState);
d3.interval(renderHistory, 500);
// not the focus here, but it also shows rotation!
function handleOrientation(e) {
if(e.gamma === null || e.beta === null || e.alpha === null) return;
rotation = {
gamma: e.gamma || 0,
beta: e.beta || 0,
alpha: e.alpha || 0
}
}
// accelerate according to z-axis device motion
function handleMotion(e) {
if(e.acceleration.x === null || e.acceleration.y === null || e.acceleration.z === null) return;
accelerate(e.acceleration.z, e.timeStamp);
}
// for testing on desktop, basically: map horizontal mouse position to acceleration
function handleMousemove(e) {
var mouseAccelerator = d3.scaleLinear()
.domain([0,innerWidth])
.range([-.2,.2]);
accelerate(mouseAccelerator(e.pageX), e.timeStamp);
console.log(mouseAccelerator(e.pageX));
}
// step forward with new acceleration, applying some very crude filtering & friction
function accelerate(a, t) {
var newZ = Object.assign({}, z[0]);
newZ.acceleration = Math.abs(a) > .1 ? a : 0; // noise filter
newZ.time = t;
newZ = eulerStep(z[0], newZ);
newZ.velocity *= .9; // friction
newZ.velocity = Math.abs(newZ.velocity) < .01 ? 0 : newZ.velocity; // noise filter
newZ.position *= .999; // tend back to zero
z.unshift(newZ);
}
// euler double integration
function eulerStep(state0, state1) {
var interval = (state1.time - state0.time) / 1000; // convert ms to s
if(interval) {
state1.position = state0.position + state0.velocity * interval;
state1.velocity = state0.velocity + state0.acceleration * interval;
}
return Object.assign({}, state1);
}
// transform lil box representing your phone
function renderState() {
phone.style('transform', ''
// + 'rotateZ('+rotation.alpha+'deg) '
+ 'rotateX('+rotation.beta+'deg) '
+ 'rotateY('+rotation.gamma+'deg) '
+ 'translate3d('+0+'px,'+0+'px,'+(-z[0].position*1000)+'px)'
);
}
// draw graph
function renderHistory() {
// draw three lines: x, dx, ddx
var data = ['position','velocity','acceleration'].map(function(d,i) {
return z.filter(function(dd) { return dd.time; }).map(function(dd,ii) {
return {
'value': dd[d],
'time': dd.time
}
});
});
var svg = d3.select('svg');
var x = d3.scaleLinear()
.domain(d3.extent(d3.merge(data), function(d) { return d.time; }))
.range([0,svg.node().getBoundingClientRect().width]);
var y = d3.scaleLinear()
.domain(d3.extent(d3.merge(data), function(d) { return d.value; }))
.range([0,svg.node().getBoundingClientRect().height]);
var line = d3.line()
.x(function(d,i) { return x(d.time); })
.y(function(d,i) { return y(d.value); });
var path = svg.selectAll('path')
.data(data);
path.enter().append('path')
.style('stroke', function(d,i) {
var colors = {
0: 'red', //position
1: 'green', //velocity
2: 'blue' //acceleration
}
return colors[i];
});
path.attr('d', line);
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment