Flights are on hold at the moment until my daughter’s chicken pox clears and she’s allowed back to nursery, so I decided to have a tinker with the code’s performance.
Phoebe’s main processing loop had degraded since the last time I let her run at full speed from about 180 loops / sec (5.55ms per loop) to about 150 (6.66ms / loop). At the same time, the sensor read was taking 60% of that 6.66ms or about 4ms per sensor read / processing. The sensor processing is not heavy so I had to assume i2c was holding things up.
The default i2c clock frequency / data rate on the RPi is 100kbps, but the MPU6050 can operate up to 400kbps. To do so, I created a new file i2c_bcm2708.conf in /etc/modprobe.d/ and added the line:
options i2c_bcm2708 baudrate=400000
At the same time, I increased the MPU6050 sample rate divider from 3 (250Hz data register update rate) to 1 (500Hz data register update rate).
Prior to the change, as I said, a sensor read took 4ms. Afterwards, the code looped at 211.55 loops per second (comared to 150 previously), and only 43% of that time was spent on sensor reading leads to a 2ms sensor read – twice as fast.
Having been poking around alot in the sensor read code, I also spotted another area that could be improved: although there’s a data ready interrupt, I don’t use it; instead I loop polling the data ready register, only reading data once it transitions from low to high. I wondered whether I could use the interrupt to save the hard-loop polling (and hence CPU) and at the same time fractionally increase the speed?
You may remember I use the RPIO library rather than the GPIO library as it offers hardware PWM to drive the ESCs. RPIO claims to be 100% GPIO compatible, but that turns out not to be the case anymore. The function I needed was a blocking call to waiting until there is a rising edge on the MPU6050 interrupt pin. The GPIO API provides GPIO.wait_for_edge() which matches my needs.
So that meant using RPIO for PWM and GPIO for GPIO. Luckily a little bit of hackery made that easy:
import RPIO.PWM as PWM
import RPi.GPIO as RPIO
No further changes to the code were needed to make the GPIO code replace the RPIO code. A fiddly bit of config change to modify when MPU6050 interrupts are generated and cleared, and it all just worked. Sadly, the loop speed dropped to 90Hz from the 211 previously seen which was dissapointing. I’ve reverted to the previous high speed polling of the register as a result. Still it was an interesting trial with interesting results. I’ve left the interrupt code in place if I fancy another play later to track down the cause of the reduced performace – the obvious contender is the GPIO.wait_for_edge performance compared to the high speed polling.
P.S. All these tests were done with the ESCs not powered; it remains to be seen what effect this super-speedy sampling rate renders. If nothing else, it means I can insert a lot more code (such as an RC) and retain the performance I’d had prior to these changes.
P.P.S. Just realized I’m using the complementary filter the wrong way. It’s used for merging angles from the integrated gyro and the accelerometer passed through Euler angle conversion. The integrated gyro is accuarate short term but drifts long term; the accelerometer is better long term but is noisy in the sort term.
Early in the project I’d heard that the gyro drifts, which is why it can’t be used long term. I took that as read. Now I believe that’s not correct otherwise the complementary filter still wouldn’t work, and it’s the integrated gyro that drifts (which makes complete sense to me). That means that this performance improvement can provide more frequent gyro data, integrating more accurately, meaning it can be trusted for longer. Hence tau can be increased to use the integrated gyro for longer whereas I’d been reducing it to include the accelerometer earlier as the noise reduced). That also probably means I can probably increase the dlpf to let more good stuff through.