My aversion to more sensors

The PC World mention, along with some recent comments got me thinking about why I lack interest in adding an altimeter / magnetometer / GPS / remote control to HoG.  After all, it’s the obvious next step.

I have the sensors already, and none of the above would add much in the way of overheads to the code processing – perhaps only 1Hz polls of the GPS, compass and altimeter fused with the existing data from the accelerometer and gyro about orientation, direction and speed feeding into the existing flight targets.  All relatively straightforward.

Autonomy vs. remote control was purely a personal decision based upon my ineptness at controlling machines with joysticks.  I stopped computer gaming with Half-Life2 2 when I was within seconds of the end and yet  lacked the hand-eye coordination to win that final battle to stop the launch of the missile / rocket / bomb.

It’s a combination of time and testing that is the biggest problem.  Up to now, all testing happens in the back garden – it now takes me less than 5 minutes to run a flight, and get back indoors to analyze the diagnostics.  Even when the weather is terrible, those 5 minute slots exist virtually every day.  But with GPS movement tracking resolution of 10m, the garden is nowhere near big enough to test autonomous path tracking – just too many high stone walls to crash into.  I could move testing to the village play park a hundred meters down the road, but it’s called a kids play park for a good reason.  I could move into the fields a couple of hundred meters away, but that’s just far enough away that time steps in – to-ing and fro-ing between the fields and the “engineering lab” (home office) would just be too inefficient for the limited time available due to a full-time job and two kids under 7.  Oh, and the farmers probably wouldn’t appreciate HoG scything down their crops or sheering their sheep!

So I settled on short term autonomous flights within the bounds of the back garden.

Having said all that, I’m off to the kids’ play park tomorrow during school time just to see how long HoG can maintain a stable hover with limited drift, and perhaps add some horizontal movement to her flight plan if all goes well.  Weather forecast is good, sunshine and only a gentle breeze so hopefully I’ll be able to get a longer video of what she can do!

Oh what now?

I managed to squeeze in another flight today, and got diagnostics. The flight was only 3 seconds before I had to kill it – she then flipped over and landed upside down on the roof before sliding down (phew) and landing on the grass.  No damage done to the house or Phoebe.

During the first second or so, the flight plan was zero movement in all three axes while the props got up to speed. For the next two seconds, Phoebe rose, but she rose to over 3m in those two seconds instead of the target of one meter.  She also drifted gently to the right and possibly backwards (but that’s harder to see from behind).

This graph shows accelerometer readings over the course of the flight, with gravity (a fixed reading taken during the flight warm-up period and rotated to the same orientation) subtracted:

Acceleration Drift

Acceleration Drift

I’ve added linear trend lines to the graph which, at least over these 3 seconds, do fit with a linear drift in the accelerometer readings.  Note that the drift in readings seems independent of whether she’s stationary on the ground for the first second, or climbing for the remaining flight time.

So what could be the cause?  For once, it isn’t my code – at this point no motion processing has happened – this is raw data read from the sensors.  It also can’t be calibration as the trend lines would be horizontal with a fixed error.

My first suspect for the drift was temperature, but this graph of sensor temperature over the course of the flight shows that’s unlikely:

Flight temperature

Flight temperature

Could this be dlpf filtering out important stuff or including unnecessary stuff?  It was set to 3 – 44Hz and 4.9ms lag.

I have 2 plans of attack here:

  1. try upping the dlpf to 2 – 94Hz and 3ms lag and down to 4 – 21Hz and 8.5ms lag and see what happens.
  2. try physical filtering by isolating the sensors from the prop noise.

Option 2 is Zoë – the genetically engineered clone of Phoebe’s software and electronics with Chloë’s hardware, with a special dash of silicone dampers to isolate the Raspberry Pi and Beret lower platform from the noise from the props / motors transmitted along the arms to the top platform.  The remaining parts are arriving tomorrow.

I do hope this works because otherwise this project has just hit the biggest metaphoric brick-wall possible – think of the great wall of China, but built of granite.


XY deadzone

XY deadzone

Same code, different test:  I picked Phoebe off the floor – hence the green peak at about 2.5s.  Then I rotated her in the air so that first the Y and then the X axes pointed down, and read gravity.  All sensors worked fine in this test.

And that suggests that this project as it stands is now dead.

There appears to be a deadzone around 0g that doesn’t detect acceleration.  So for the previous tests, the Z axis acceleration is fine since it’s 1g ± bits of real acceleration , but the X and Y axes don’t pick up this slight acceleration either side of 0g.

The accelerometer is set to maximum resolution of ±2g and it’s a 16 bit ADC – so the minimum detectable acceleration should be 4 / 65536 = 0.000061g or 0.0006ms-2 or 0.6mm-2, which is more than enough to measure me (carrying Phoebe) start walking forwards from a stationary ‘hover’ – yet it does not.  Hence my deadzone around 0g surmise.

I’m off to to see if this is common knowledge – but I don’t hold up much hope – I rarely get responses from anyone over there.


Something very weird is going on here: any kind of horizontal acceleration appears to be not detected.  I’ve tried various tweaks in areas that might be the cause:

  • I’ve increased the dlpf from 5Hz to 20Hz in case good data was getting filtered out
  • I’ve reduced the sampling frequency from 1kHz to 500Hz in case there was corruption in the data due to updates happening faster than could be read by the code.
  • I’ve bypassed my calibration code so only raw values from the sensors are used.

And yet the results are identical each time:  somehow, the positive accelerometer spikes which should appear when Phoebe starts to move forward / sideways, and the corresponding ones when that movement stops simply aren’t happening.  Only the Z-axis is showing anything close to credible.

And now, after this extended config change testing, I’m at a complete loss as to why.  Time for some very out of box thinking – whatever that’s supposed to mean!

Well that explains a lot…

I did some ‘passive’ test flights today going back to check the basics.  Without the ESCs / motors powered up, I did a 12s pseudo-flight, where I picked Phoebe off the floor climbing to a meter in about 4 seconds; then I walked her forwards for 1m for about 4s again; finally I walked her left by about a meter again for 4s.

These are the three positive directions for the accelerometer so I should see positive spikes in acceleration at these points, a raised fixed velocity of each of the axes, and from the distance point of view 3, 1meter 4 second humps.  So this came as a bit of a surprise:

Acceleration - WTF

Acceleration – WTF

Velocity - WTF

Velocity – WTF

Distancde - WTF?

Distance – WTF

Velocity is the clearest – it should be 3 positive bumps – Z axis first for take-off, then X axis for forward movement, and then Y axis for leftwards movement; each bump should be about the same size / shape!

Accelerometer readings worry me most though as this is (almost) raw data from the sensors with little tinkering from me: there should be a positive spike, followed by a negative spike roughly four seconds later.  The spikes should be roughly the same height for each axis, and (for example), the positive spike in X acceleration should happen at the same time as the negative spike in the Z acceleration.

Just in case Phoebe’s sensor was duff, I did the same with Chloe and got almost exactly the same results.

Something is seriously wrong with my accelerometer averaging!  At least that explains why Phoebe keeps crashing into walls.

P.S. Sorry for the change in graph styles – the death of my home computer has forced me to produce the graphs on my work computer running Excel 2007 instead of 2003, and as with everything from Microsoft, there’s a real quality problem between sequential releases; in this case Excel 2007 has chosen to completely change the default colour tablet making it almost impossible to use the same colours as my 2003 graphs, and frankly I don’t want to waste any more of my time than is possible fighting Microsoft ‘enhancements’ to Excel 2007.

What do you get if you cross an accelerometer with an ultrasonic range finder?


Pink is the range finder distance differentiated against time giving velocity in the quad Z axis – note how spikey it is.

Green is the accelerometer output integrated over time giving velocity in the quad Z axis – note that it’s drifting downwards between 6 and 10 seconds when the quad is actually hovering – I know because I’m holding it in my hands.

Orange is what you get when you fuse these together with a complementary filter – note it doesn’t drift downwards, and much of the spikiness is gone.

Velocity fusion

Velocity fusion

Time for real test flight tomorrow – weather is looking good too.

Time to compromise

My aim with this project was to make a quadcopter with Python and a Raspberry Pi and as little else as I could get away with.  As the project slowly progressed, this aim refined to include autonomous control and use of only accelerometer and gyroscope as the sensors.

It’s clear now that the variability in the accelerometer Z axis (despite tuning) is messing up most flights, and I need something else to help it.  I’d hoped to use a barometer / altimeter on the same breakout as the existing IMU but I need to wait for the next revision of the breakout from DroTek, and that won’t happen until their current stock sells out – perhaps not until Easter next year apparently.

So plan B in the meantime is to use one of the ultrasonic range finders I bought strapped to Phoebe’s underside to read vertical height and differentiate over time to get vertical speed, and then mix that with the integrated (accelerometer Z axis – gravity) speed with a complementary filter.

This shouldn’t be hard – I already have built a prototype that works well.  The hardest part it working out where on Phoebe’s underside I can attach the sensor without it getting mauled on landing!


I’ve just posted the latest code to GitHub, and I think it’s nigh on perfect (IMHO) – there’s now nothing on my mental ‘niggle’ list that could make it better.

However (and it’s a very big however), it requires nigh on perfect accelerometer calibration.  To give you an idea, if the accelerometer reads gravity as 1.001g rather 1.0g (0.1% accuracy), then you’ve get drift at roughly 1cm/s2 and that builds up to significant drift beyond a few seconds of flight.  Yet the sensors alone are only rated at ±2% accuracy, or 20cm/s2. The 0.1% accuracy error I mention represents a twenty fold improvement over the base 2% level of calibration and is my current best effort but it still limits the flights to tens of seconds rather than minutes in length.

So here in detail is what I do:

  • find two locations with a broad and stable temperature differential – in my case a beer fridge, and the warmest room in the house – in my case, my home office as it has way too many computers in it!
  • Separate the Raspberry Pi from the quad frame and stick it to the inside of a 5 sided perspex cube aligned closely with one edge of the cube.
  • Within both environments, ensure the platform for the cube is flat and horizontal – I have a 10mm thick perspex square with wing-nut bolts at each core and a 0.5° accuracy spirit level to get the best horizontal possible: I sit the beer fridge on this, or just the cube on this depending on the required temperature environment.
  • Within each of the two temperature environments, I then rotate the cube on each of the six sides, reading sensors (sudo python ./ -c) each time until I have a sample for each that is within half of a degree (celsius / centigrade)  of each other according to the sensor output – that’s about as good as I’ve achieved so far because the sensor warms up the more samples you take!
  • From the sensor readings of 1g over the six sides of the cube at a given temperature, you can calculate the offset and gains of those axes such that each reads ±1g at that temperature.
  • With those offsets and gains at two diverse temperature points, you can then calculate a linear mapping between and beyond those temperature points to allow calculation of offsets and gains at any operating temperature.  The sensor output is reportedly linear across temperature at ±2% also and that should be good enough – 2% error in gain / offset at a given temperature is much better than 2% error in absolute readings.

Here’s the data I’ve obtained and sorted.  The leftmost column is raw temperature readings.  The rightmost 4 columns are gravity measured over 3 axes, in both directions at two temperature ranges.  Column 2 is temperature in celsius / centrigrade – I don’t use this in the calibration, but it makes it easier to spot a small range of temperature values.  The accelerometer scale is set to ±2g so the values of ±16384 are whats expected, and are the targets of the calibration calculation.

-3393, 26.550588, 16537.84, -294.84, -842.44
-3383, 26.58, -16404.88, -14.1, -1062.72
-3390, 26.559412, 179.44, 16369.72, -953.2
-3391, 26.556471, -59.44, -16662.18, -974.54
-3383, 26.58, 45.62, -176.4, 15436.18
-3386, 26.571176, 75.52, -94.92, -17342.8
-6517, 17.362353, 16505.3, -275.08, -531.38
-6543, 17.285882, -16426.64, 1.22, -846.94
-6484, 17.459412, 185.44, 16368.36, -695.24
-6462, 17.524118, -55.6, -16649.48, -707.32
-6552, 17.259412, -38.96, -177.82, 15697
-6552, 17.259412, 96.96, -75.92, -17063.76


A dodgy sensor? Nope, a dodgy coder!

The dual power source isolating the Raspberry Pi from the LiPo / ESC / motors works well.  Test flights showed zero missed sensor readings.

What they did show, and what I’d suspected for a while, was that the Y-axis accelerometer is sluggish / broken.  My suspicion something was wrong originates from the fact Phoebe always drifts right, and has done so for quite a while.  But I didn’t bother trying to confirm the problem because replacing the sensor would mean redoing the calibration, and I didn’t have a beer fridge then!

The nearest thing to proof though appeared in today’s flight diagnostics:

Stuck accelerometer?

Stuck accelerometer?

This shows the distribution of gravity across the sensors as Phoebe pitches and rolls in flight.  The quad-frame raw gravity vector Y axis (qfrgv_y) doesn’t change throughout, as though someone dropped superglue on it!

I have a spare sensor, so now I’m back to doing dynamic calibration of the accelerometer, it’s just a quick swap and a test flight tomorrow to see the change.

P.S. Or of course, it could be due to a typo in my code, d’oh.  Only went hunting carefully after swapping the sensors and still seeing the same flat-line on the accelerometer y-axis.  A down-side of python is not having to explicitly declare variables as long as they are set prior to being read.

Back to dynamic calibration

I test flighted my beer fridge calibration, and frankly the results were rubbish verging on scary.  Turns out that was nothing to do with the calibration, and everything to do with a bug in the code. Having reviewed the calibration math(s), there was a flaw:

G * sin θ = gain * (sensor1 + offset)

The correct version was something along the lines of

G = sensorx * sin θ + sensorz * cos θ

At the same time, I lost confidence I could calculate offsets at anything other than 90° (the math(s) just didn’t feel right), so I ordered a small perspex cube from e-bay to allow me to calibrate all axes at ±90°.

And with that on order, I had time to kill, so I went back to reviewing the dynamic calibration code.  And I found 2 bugs: one related to take-off timings, and the other related to correct handling of gravity when integrating accelerometers to determine speed.

Long story short, dynamic calibration is back at the top of the my priorities, the perspex cube is probably redundant, and the beer fridge is passing the “Ronseal TestTM” and that alone.