-
• #2
This sounds like a really fun project! I guess my initial thought would be that a running step should be quite repetitive and you probably don't need the magnetometer for it anyway as it's all about acceleration and rotation - if you can detect when the foot hits the ground (maybe it's a sudden de-acceleration?) then maybe you can always reset the Puck's velocity to 0 at that point?
In a way, you don't actually want the distance your foot travelled, as that's an arc and would be longer than the real distance- you want to take the distance travelled between the times when the foot hit the ground?
I'm afraid using Tensorflow on the Bangle is probably just a bit too needlessly complicated and error prone - with some effort it could be trained but I wouldn't really trust it over any range you hadn't tested it on. Even just summing up acceleration values over time would probably work better if calibrated!
-
• #3
I have already started considering (at least) a different approach to this. There do exist libraries which take in only accelerometer data from a device fixed to the lower back and use some more advanced statistics to gauge speed and other things from there. What I really want is the speed. Cadence and foot contact time are also on the wishlist, and it seems that the statisticians can manage these.
One more thing on that subject. I do use a Polar H10 when running, and, as has been mentioned on these forums, it has a hidden accelerometer. If there are good open source algorithms which take data from the chest as input, then we can actually forget the puck :D (I'm not saying I wouldn't miss it) As far as I know, a Garmin chest strap basically does this, with the caveat that it only works if you're using a Garmin watch. Most likely they run the algorithms on the watch, rather than on the strap. I suppose that a modern Garmin watch has more computational power than a Bangle.js 2? What are the specs on Bangle.js 3? ;)
What I described above is an idea I had brewing for a bit until I had some time to code. Maybe it is a bit simplistic, but if it can be made work, then that would be fantastic, I won't give up on it just yet.
-
• #4
you don't actually want the distance your foot travelled
Right, I would certainly integrate (twice) the acceleration vector, which, theoretically, should return a vector of displacement, i.e. how far (and in which direction) the foot travelled. There are at least two obstacles here.
The first one is that the vector needs to be in earth coordinats. This means updating the change of coordinates at each step, using the gyroscope. This is basically what Madgwick does, but using also acceleration and the magnetometer to correct errors which do accumulate (and pretty fast).
The second problem is that, even though the change of variables is kept fairly stable, errors do accumulate (fast again). That's what I mean by being a bit simplistic with this approach. Being theoretic discards errors, which is probably a mistake.
Since, as you mentioned, a foot movement should always have a similar shape, I would imagine that a good statistician would simply take data from a bunch of steps with known lengths, and use that to infer lengths of steps from new data. If necessary, I would create the data for myself on a track.
I would certainly get behind this statistical approach, if it works. In fact, I would be surprised if this wasn't precisely the approach taken by stryd or garmin, who do make it work, as far as I know. The only thing is that I doubt they ever published their algorithms, as this way, anyone with a cheap accelerometer could put them out of business.
-
• #5
Btw, is there a simple app to stream data from the acc/gyro/mag to an android phone? Like, could I put up a service like in something involving NRF.setAdvertising() on the puck, and then read it from an android app? On the Bangle.js 2 I guess space would be a problem for anything but pretty short periods, and running around with a laptop doesn't look very cool ...
-
• #6
The first one is that the vector needs to be in earth coordinats
I don't think so actually - if you know when the foot has made contact with the ground, you literally just take the length of the 3D vector between the current and last time it hit the ground - you don't really care what direction it 'thinks' it is?
is there a simple app to stream data from the acc/gyro/mag to an android phone?
From the Bangle? I think https://banglejs.com/apps/?id=sensible will do it (advertise the Bangle's sensors), although i'm not sure how fast.
You've got 8MB of flash - I'd expect that even recording at 100Hz it'd easily last you for the length of a run as long as you store the data in a reasonably compact form?
-
• #7
From the Bangle?
I was thinking collecting sensor data from the Puck.js, and saving it. I'd have the Puck.js stuck to my lower back, or on my foot, depending on which method I'd be using, and the Bangle.js on my wrist. I'm not sure how much space is on the Puck.js, and that's why I would probably want to send it over bluetooth to somewhere, and the phone has infinite space, from this point of view. It could be good to take the data onto the Bangle.js, if it has enough space, but it seems that the sensible app collects sensor data from the Bangle.js, and then advertises that, right?
For the record, I did try to record some data to my phone this morning, using the following method: Run the code below on the Puck.js, and then open an app called nRF-Connect on the phone, connect to the Puck.js (remember to disconnect from the laptop), and the data will appear in a log file there, which can be saved on the phone. After that, the log file can be cleaned on a laptop, and we're done. It's not the most elegant solution, but at this point, nobody cares if the logfile is many megabytes. Unfortunately I had some connection issues, which I didn't resolve out in the field, but I'm sure that can be fixed.
let bla; let mag = {"x":0, "y":0, z:"0"}; Puck.accelOn(104); Puck.magOn(80); Puck.on('mag', function(m) { mag = m; }); Puck.on('accel',function(a) { bla = [ Date.now(), a.gyro.x, a.gyro.y, a.gyro.z, a.acc.x, a.acc.y, a.acc.z, mag.x, mag.y, mag.z, ]; NRF.setAdvertising({}, { name: "blabla", manufacturer: 0x590, manufacturerData: bla }); console.log(bla); });
-
• #8
but it seems that the sensible app collects sensor data from the Bangle.js, and then advertises that, right?
Correct, yes, so that's not what you need. But if you can log from the BLE UART to your phone then I guess that's pretty good.
I am working on a footpod for running, using the Puck.js, which will connect to the Bangle.js 2 and provide speed for one of the data fields of the runnig app. To see if this will work, I put together a webpage which connects to the puck, and displays some data [1].
Once this whole thing is working, it should be possible to implement this on the Bangle.js 2, similarly as the bthrm app. What the code on the website does is the follwing:
Send code to the Puck.js telling it to send data from the accelerometer/gyroscope/magnetometer.
Feed the data into an algorithm devised by Madgwick [2] in order to keep orientation. This is visualized using three.js on the page, using code written by @bre.
Integrate the acceleration twice to obtain position. The code keeps track of a position, but also analyzes each step. Steps are separated by 20 datapoints of 'stillness' (when the foot is on the ground). Empirically, I have found that there is error in the step. However, since each step ends with 20 'still' datapoints, what I do is subtract from the step a linear term, which makes the final 20 points look like they are not moving. So far, this looks like it is giving a good approximation of each step. Try it yourself at home! :D
There is the consideration of calibration: calibrating the gyroscope is easy, just read a 100 values, and subtract the mean. The error introduced is not enormous anyway.
I haven't calibrated the accelerometer, the readings give slight variation from 9.8m/s^2 depending on which way the puck is facing, but the error is not huge. I might implement something for this later, if it is deemed important.
Calibrating the magnetometer is trickier. The error is huge, apparently there is "soft iron" and "hard iron" effects, which fortunately can be detected and computed from a good enogh set of data. Pressing a button on [1] calibrates it using some linear algebra and least squares. I am still not sure if this is the correct way of doing it, there may be an extra matrix factor needed to orient the values.
The code chooses between the MARG and IMU algorithms from Madgwick's paper [2], depending on the sensibility of the values of the magnetometer. Sensible means that the ratio of the norm of the calibrated magnetodata, and the expected norm (the radius) is within a neighborhood of 1.
If it is not calibrated, then it chooses IMU, which only uses the accelerometer and gyroscope. From the graphics, two things are clear: The algorithm works as advertied, the z-axis is stable (the one that points upwards) but errors rotating around the z-axis accumulate. Try placing the puck on a table, and rotating it quickly in one direction, and then slowly in the other. Doing this a few times, you see the errors accumulating.
If it is calibrated, then it chooses MARG, which should keep the direction up and, let's say, north, stable. However, something is clearly wrong, because in the graphics you can see the guy having problems.
Any comments or questions on any of the above topics are very welcome. I apologize if the code in [1] is a bit of a mess, this is a work in progress.
Finally, let me go out on a limb. I don't know how gestures and Tensorflow work, but is it possible to train the Bangle.js 2 to take in accelerometer / gyro / magneto data from a single step, and to approximate it's length, somehow by magic? There is a function in the code which 'corrects' a single step, but this is ruidmentary, and clearly sometimes has errors. It should be easy to obtain accurate data by running along a 400m track, or along any known route. If an AI can be trained on such data, that would be the dream, no?
[1] https://baldursigurds.github.io/gamrun_dev/
[2] https://x-io.co.uk/open-source-imu-and-ahrs-algorithms/