HoG & CoG

What have I been up to?

  1. Phoebe’s HoG PCB is now out for manufacturing – hopefully this will fix the I2C problems and allow me to progress with adding the magnetometer (orientation), URF (height) and camera (motion) sensors into the mix.  The new PCBs are due to arrive on or before 29th.
  2. Zoe’s undergone a minor rebuild of her frame to lift her CoG nearer to prop height to reduce the swinging in an otherwise stable flight – her main battery now resides on the top plate, and her power bank underneath.  I could improve this further by getting rid of the power bank completely, but that will require a new PCB to incorporate the regulator.

Other than that, I’m flying Zoe indoors daily tuning her PIDs ready for her upcoming performances.  She still drifts, and because she’s now using the IMU FIFO code, I’m back to considering sensor drift due to temperature.  She performs best in the freezing cold with the large difference between cold ambient and her IMU operating temperature.  However I won’t be investigating this further within the timeframe of the performances.

Yet another “One last chance”.

I simply couldn’t give up on Phoebe so easily: after posting, I spotted all the components needed to build a clean Zoe PCB which I did.  I attached it to Phoebe, and everything worked.  So the I2C problems are the fault of the breadboard-like PCB Phoebe uses.  So once more, Phoebe isn’t dead yet, and the new PCB layout is already in progress for the lovely peeps at Ragworm to produce in the next week or so.

Fed up of Phoebe

I’m giving up on Phoebe and adding new sensors using the IMU FIFO code.  The final nail in the coffin was I spotted I2C errors while I was trying to diagnose the squid problems.  I2C errors rule out using the IMU FIFO as the code then gets out of sync with what data it’s getting from the FIFO.  That means Phoebe has to switch back to using the data ready interrupt code, which in turn means she can’t periodically do something else like read other sensors because she can’t afford to miss the next interrupt and hence the next batch of data.

I’ve never been able to find the source of these I2C errors but I guess it’s the PCB, and I’m not going to get a revised version designed any time soon based on speculation alone.

Zoe on the other hand is still working perfectly – no I2C errors with identical code perhaps because she’s the daughter of Johnny Ball, a science presenter from my era, who also has cartoon appearances on the Raspberry Pi site.  But more likely is that she’s a Zero not an A+ and running a different PCB design as result (both mine and A+/Zero).  Unfortunately Zoe would need a new PCB to add the URF for vertical motion tracking, and she could never have a camera for the horizontal motion tracking – PiZero’s don’t have the camera socket.  So Zoe’s is essentially finished other than fine tuning.

So this project is grinding to a terminal halt for the moment until either an A2 appears on the market allowing me to split the DRI code from the motion processing and connect them with an OS FIFO, or I get a new PCB done for an A+ where there’s no guarantee this will fix anything.

Tangerine Dream

A comment from Jeremy yesterday planted a seed, which has rapidly become a germinating coconut!

It started with using a RPi B2 to give me the extra CPU cores I wanted rather than wait for the fictional RPi A2.  I’d not considered a B2 as only A+ fit between the top and bottom plates of the frame, but in the past I’ve used Bs, As and B+s sat on the top-plate.  Immediately I thought of Chloe who’s currently just a Phoebe clone – but a RPi B2 could make her so much more – I started rebuilding Chloe to shuffle the bits around to make space for the RPi B2 on the top plate.

I’d need a case of course for protection in her new exposed position on the top plate, and the Tangerine PiBow appealed for some reason.  Perhaps it’s the seasonal stocking filler from Santa?

Then it dawned on me that since using Jessie for Zoe, I’d not seen any I2C errors which have plagued the accuracy of sensor readings for a year now, and that meant I could reconsider using some of the FIFO variants of the code I’d shelved.

By using a FIFO, control of time is no longer tied to the data ready interrupt: additional function does not have to be squeezed into the few milliseconds between sensor readings; the code can do what it wants (for example with additonal sensors) and periodically empty the FIFO and process the contents, with accurate elapsed time based on the number of samples retrieved from the FIFO.

For now, I’m going to focus on getting Zoe and her PiZero up to Phoebe’s standard, but I hope to then raise the Tangerine phoenix that’s Chloe from the flames.

 

Zoe the Zero – 3 – I2C

I thought I’d be unable to proceed further until the new PCB had arrived, but I’d forgotten I’d already started building Zoe prior to the release of the Pi Zero; So I had an HAT PCB ready and tested with an A+.  Since the GPIO pins for the A+ and Pi Zero are identical, I could test the I2C and the Quadcopter code itself.

First step is to run

i2cdetect -y 1

which if you are using the Drotek MPU9250 + MS5611 breakout should look like this:

 0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- 77

68 is the MPU-9250 I2C address and 77, the address of the MS5611.

Next, update the baudrate of the I2C interface to the maximum the MPU9250 can use since we want to get as much data as we can as frequently as we can.  Open up /boot/config.txt and set the following and reboot.

dtparam=i2c_arm_baudrate=400000

At this point, it’s possible to run the quadcopter code to test the I2C.

cd ~/Quadcopter
sudo ./qc.py -g
sudo ./qc.py -f fp.csv

Here I got 2 surprises.  First it just worked, and second, there were no signs of I2C errors.  So I upped the dri_frequency in the code

dri_frequency = 500
samples_per_motion = 5

And that worked too; with the A+ and the same PCB this didn’t work – data samples were being missed. Perhaps it’s the 1GHz overclocking? I dunno and I don’t care, it bodes well for when the Pi Zero size PCB arrives.

When the new PCB does, I’ll have to redo this to check the PCB itself, but it’s a reassuring test to have passed.

Oh Jessie, you’re so fine

Installment 2 of updating Chloe to the Jessie level of Raspian: I2C and RPIO.

    • sudo apt-get install i2c-tools python-smbus pypy-smbus-cffi python-codebug-i2c-tether
    • “i2cdetect -y 1” shows 68 and 77 for Drotek MPU9250 + MS5611
    • /boot/config.txt:
      dtparam=i2c_arm_baudrate=400000
    • Python development tools:
      sudo get install python-dev
    • Install and build RPIO
      git clone https://github.com/metachris/RPIO.git
      cd RPIO
      sudo python setup.py install
    • Install and build my custom GPIO – I tried using the standard RPi.GPIO again, and it’s a lot faster than it was, but not quite fast enough
      tar xvf GPIO-0.6.0.tgz
      cd GPIO
      sudo python setup install

Hand-in-glove

First the good bit so you can skip the boring explanation if you wish!

Low drift Phoebe

I did some experimentation to test which of the four different ways the IMU hardware interrupt and my GPIO code can work together to get the most efficient and accurate readings.

The IMU data ready interrupt can either generate a 50us pulse each time the data registers are updated, or it can produce a rising edge which latches high, only falling when the data is read over I2C.

My GPIO tweaks can either capture these interrupts continuously, or just capture the next one, and then capture no more until called again.

So I ran 4 tests to see which pairing worked together best based upon the number of I2C and data corruption errors I got and how accurate the timing was which is directly related to how many sensor readings are missed during motion processing.

Other than the latching, continuous option which just blocked (predictably), it was marginal between the other three in the lab testing, but with a slight advantage shown by 50us pulse, continuous reading.

And a few test flights with Phoebe confirmed this; much reduced drift and improved timing and at 500Hz sampling she missed only 2% of samples which is the best yet by a very long way – previously it’s been more like 20%

Two final tweaks: I loosened the tests for duff data and changed the butterworth filter values:

  • duff data can be identified when the sequence of ax, ay, az, temp, gx, gy, gz has some values set to -1 (0xFFFF) starting from the gz end.  Previously I’d been checking gy and gz.  Now I check just az, the reasoning being that errors in gyro of -1 have a very short term effect on the accuracy of angles, whereas -1 for az (which represents freefall in a vacuum) will plague a flight thereafter due to integration for velocity
  • The butterworth changes were experimental to see what settings might provide better flights by filtering more acceleration out of gravity; a 0.05Hz (20s) 6th order was used in these flights compared to the previous 0.1Hz (10s) 4th order resulting in much better drift control.

Wasted time.time()

I’ve been doing some fine tuning of the HoG code as a result of the FIFO trial I did.  The FIFO timing came from the IMU sampling rate – there’s a sample every 1ms and so there’s no need for using time.time() when the IMU hardware clock can do it.

I’ve now applied the same to the hardware interrupt driven code; motion processing now takes less time as it was the only call to time.time(), and as such that means there’s a smaller chance of missing samples too.  I’ve also added code so that when there are I2C / data corruption errors, that counts as another 1ms gap since the code then waits for the next hardware interrupt.

I’ve also removed the zero 0 calibration again, as I’m fairly convinced it’s pointless – I _think_ the deletion of (Butterworth filter extracted) gravity from acceleration means that calibration is pointless.  That also adds a very marginal performance increase to motion processing.

A few test flights showed no regression (nor noticeable improvement), so I’ve uploaded the code to GitHub.  You also need to grab the latest version of my GPIO library as in now imports as GPIO rather than as a clone of RPi.GPIO.

Probable source for I2C read errors

This is really just a note to myself that there is a known issue with the I2C kernel drivers / hardware for the Raspberry Pi covering all models.  Essentially the I2C clocks between master and slave can get stretched, the MPU-6050 can do this, but the Raspberry Pi ignores this, which would result in reading data before it’s ready.  Doesn’t seem like a solution is forthcoming, and it’s made worst at high-baudrates and large data reads i.e. exactly what my code is doing.  Feck.

 

I2C is error proof

Yesterday was proof of error, today is proof of the protective fix:

    def readSensors(self):
        #-------------------------------------------------------------------------------------------
        # For the sake of always getting good date wrap the interrupt and the register read in a try
        # except loop.
        #-------------------------------------------------------------------------------------------
        while True:
            try:
                #-----------------------------------------------------------------------------------
                # Wait for the data ready interrupt
                #-----------------------------------------------------------------------------------
                RPIO.edge_detect_wait(RPIO_DATA_READY_INTERRUPT)

                #-----------------------------------------------------------------------------------
                # For speed of reading, read all the sensors and parse to SHORTs after.  This also
                # ensures a self consistent set of sensor data compared to reading each individually
                # where the sensor data registers could be updated between reads.
                #-----------------------------------------------------------------------------------
                sensor_data = self.i2c.readList(self.__MPU6050_RA_ACCEL_XOUT_H, 14)
            except:
                self.num_i2c_errs += 1
                continue

            #---------------------------------------------------------------------------------------
            # Convert the array of 14 bytes to 7 shorts
            #---------------------------------------------------------------------------------------
            for index in range(0, 14, 2):
                if (sensor_data[index] > 127):
                    sensor_data[index] -= 256
                self.result_array[int(index / 2)] = (sensor_data[index] << 8) + sensor_data[index + 1]

            #-------------------------------------------------------------------------------------------
            # +/- 4g * 16 bit range for the accelerometer
            # +/- 250 degrees per second * 16 bit range for the gyroscope
            #-------------------------------------------------------------------------------------------
            [ax, ay, az, temp, gx, gy, gz] = self.result_array
            if gz == -1 and gy == -1 and gx == -1 and temp == -1 and az == -1:
               self.num_data_errs += 1
               continue 
            break

        return ax, ay, az, gx, gy, gz

And here’s the proof duff data was caught triggering another sampling loop:

lps: 990.767934
mpu6050 2 misses, i2c 3 misses

Next step is to take her out and fly her!