A couple of weeks ago I got bit by the bug to build a quadcopter from scratch. I am purchasing the motors, ESC, battery, propellors, ect..., but I am designing and building the frame from raw materials. I am programming the control system from a blank slate. One of the things that will make this quad different than many others is that I will be attempting to track it's position in relation to it's start point purely from the accelerometer and gyro. If that feature is successful, I plan to add the ability for it to fly autonomously by following pre-programmed way-points. If the inertial position tracking doesn't work out, I'll just reprogram is to use standard Quadcopter controls and have fun with it.
Here's a link to my Blog where I am writing about the process:
Let me know what you think.
I expect the accuracy of the sensors to be my weak link, although keeping the arduino from butchering my accuracy has been more of an issue than I expected. My goal is to be able to return to within 1ft (300mm) of my starting point after a 5 minute flight. I am 99% certain this will not be accurate enough for long flights, but at this point I'm fine with that.
Due to noise levels in the accel signals and reletively low resoloution, I don't think this is possible with the sensors. You will have to double integrate the accelerations to get distance data and errors wittl add up very quick.
I agree, that will be my biggest issue. I am hoping that a high sample rate and filtering will help with this. We will see.
You can perform a filter on the noise, which should mitigate problems from it. Only downside to doing that is that your effectively numbing your accelerometer... but I think if you can design your filter just right, it shouldn't be a problem for a 5 minute flight.
The sampling rate can cause stability issues if it is too low, which is a major factor for very small quad's that have a low moment of inertia.
I looked at your blog, and was curious as to what you do during idle time, where the core's not scheduled to process anything and is just waiting for the next scheduled time?
Also, you have to be careful about frames of reference. Remember that your IMU is giving you data relative to the orientation of the craft.
Right now I plan on tuning the cycle time by increasing the number of accelerometer samples I take. Taking 16 samples from the the accelerometer's FIFO buffer takes 5ms on a 400kHz I2C buss. The time increases linearly as I increase the samples. The buffer holds a max of 32 samples. I can then adjust the sampling rate on the accelerometer to make sure it always stays ahead. Alternately I could fire an interrupt from the accelerometer whenever it get to a set number of samples in the buffer. As a third option, I may investigate using a non-blocking I2C library.
The last paragraph on the third post titled "Quadcopter Inertial Position Tracking" gives a basic overview of the math I'm using to convert my incremental position changes to the global reference frame.
So, your counting on the time it takes for the accelerometer to pump X amount of samples over the I2C as your system clock? I don't quite follow... could you walk through your hypothetical control loop step by step please?
Sorry that last reply wasn't as clear as it should have been.
I will very likely have a dwell at the end of my control loop. I intend to adjust the number of samples I pull from my accelerometer as a way to keep that dwell from being any longer than it needs to be. I will trade "dead time" for accelerometer samples.
I have a link to my control loop diagram in my latest post.
You could stick your RC receiver signals onto the PCINT pins. That way, you can have the MCU operate an ISR every time a rising or falling edge of the PWM pulse reaches the pins and not worry about waiting for just the right time to catch it.
The ISR wouldn't do any calculations other than perhaps to transform the pulse width time into a % position or % throttle.
As far as your dead time goes, it doesn't do you much good if your accelerometer data isn't put to use (which, I think you mean your going to try to run your IPS during this time). Also, check the datasheet of your accelerometer, there's a likely chance that it has a bandwidth that will tell you the maximum rate you can sample it.
The max accelerometer sample rate is listed as 3200hz, but there is a note which indicates I shouldn't set the sample rate to be more than 2x the I2C frequency in kHz. So with a 400kHz I2C buss, then max accelerometer sample rate would be 800Hz.
hmm... in reviewing the data sheet again, there's another reason I might want to stay at 800Hz. Both 1600Hz and 3200Hz loose the LSB, as it will always read zero. The loss of precision is definitely not going to be helpful, although one might argue that the increased sample size might allow you to "interpolate" to the same accuracy.
At 800Hz I can take exactly 16 samples per control loop cycle (20ms). This may just be the limit. If that is the case, I may have to figure out something else to do with my cycle time. Maybe look at more exotic filters. Have it play chess with itself... who knows...
Good idea with using the interrupt to catch the RC inputs. I'm a bit concerned by this statement in the Arduino documentation: "Inside the attached function,... the value returned by millis() will not increment." Hopefully this doesn't effect the micros() function as I might need to "manually" time the pulse that fires the interrupt (if it has risen too far to be caught by a pulseIn() function). Assuming the RC receiver fires the remain pulses sequentially I should be able to catch those with pulsIn(). I only have 2 pins that support interrupts, so I can't do this for all four channels.
Thanks for the reply. Phillip
Huh? If you use the PCINT, then you can get an interrupt on most of the GPIO pins. Don't tell me you've already used up all of your GPIO. :P
At any rate, 800Hz is more than enough. You should put the core to low-power mode whenever it's idle, just have another ISR wake it up after some time or at a specific time.
Oh... but the problem with using this trick is that the Arduino millis() function is tied to Timer0... which is not a wakeup source for any of the power saving modes (according to Atmel's documentation). You may be able to try with Timer2, however...
oh... PCINT, I was looking at attachInterupt()... I'll play with that.
I'm not sure I understand the advantage of putting the core to low-power mode while idling. The Arduino can't be using a significant amount of power compared to power system, so the battery saving over the ~5ms per cycle should be of no consequence.