Skip to content

Instantly share code, notes, and snippets.

@tophtucker
Last active January 4, 2020 18:26
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tophtucker/9a6c85ec25aad61b80759cdbeeaf34b3 to your computer and use it in GitHub Desktop.
Save tophtucker/9a6c85ec25aad61b80759cdbeeaf34b3 to your computer and use it in GitHub Desktop.
Inferring device position from acceleration

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

<!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