So I’m going to ignore the take-off “Elephant in the room / Skeleton in the cupboard” for the moment before I go mad, and instead, start the drone at roughly hover speed, hold it in the air, let go, and tune the PIDs for a sustained hover, plus gentle landing. I’ll let you know how it goes in a separate post in the future.
In the meantime, I’ve been looking at performance improvements, not to the extent of exchanging interpreter CPython with JIT compiled PyPy or pre-compiled Cython, but just to fine tune the code.
As of yesterday 1 loop of the code read sensors, ran them through PIDs, and sent results to motors taking about 0.02s or 50 loops a second. That’s actually fast enough, but quicker looping allows for extra code to be added while maintaining this 50 loops a second or faster reaction to problems.
My main target is reading the data from the MPU6050; part one was to increase it’s sampling rate from 100Hz to 200Hz, and fixing a minor bug (not sure if this contributed to the performance or not but hey). As a result, it’s gone up to 65 loops per second. Great.
But I’d chosen 100Hz sample rate deliberately to ensure that MPU6050 output stayed stable long enough for the Python code to read a complete consistent set of sensor results within the 100Hz / 0.01s timeframe of the MPU6050 sensor sample frequency. A “sample” comprises reading 12 registers – high and low bytes for each for the accelerometer’s and gyro’s x, y, and z axes. That’s 12 calls that Python interprets, passes to the smbus driver, and then interprets the result from the smbus driver back to the Python code. This just felt like an obvious place to try to optimize. If those 12 Python calls to read a register each could instead be changed to 1 Python call for the smbus driver to read the 12 registers, there should be a significant improvement by reducing the number of interpreted Python calls from 12 to 1!
And there was – based upon the 65 loops per second code from the 200Hz sampling rate increase, I added i2c python support for read_i2c_data_block() – this gets the smbus device to read a series of sequential registers – in this case from 0x3B to 0x48, and only then return the results to the python code in an array. And it worked. Reading those 14 registers as one Python call took the loop count up to 87 loops per second or 0.0114ms per loop – compared the the starting point of 0.2ms per loop, that’s a pretty amazing 75% speed increase opening a decent hole for extra code, or better responses or both!
Here’s the updated code for the i2c and MPU6050:
P.S. After some checking of the stats from the last run, I think I’ll drop back to the 100Hz sampling rate for the MPU6050. At 200Hz, there were a couple of duff readings suggesting the reading of the 14 registers could take marginally longer than the 5ms allowed by the 200Hz cycle. On the plus side, these duff values were swallowed up in the faster reaction meaning they had no real net-effect.