Tuesday, October 9, 2012

Inertial measuring experiment

I just posted a video on YouTube, and wanted to comment on that shortly.

What I did was that I connected my navigation board (more on that in a post later) to my receiver. The receiver was sending data from the navigation board down as telemetry, which I recorded. At the same time, I also recorded video using an 808 #11 key chain camera. As the actual test, I just walked around our house holding the device in my hand and pretending it was a space ship.

Here is a picture of the sophisticated sensor instrument:



I used MATLAB to produce a visualization of the measured attitude. This visualization was then synchronized with the video and overlaid to produce the clip uploaded to YouTube. The whole visualization process is very naive, but as a first try it'll do :-).

Saturday, October 6, 2012

Attitude inference

In my grand plan of things, I want to infer both the attitude and location of the plane. Location is somewhat easy to figure out thanks to GPS, but attitude is really a problem. Now, you might think that using an accelerometer or an inclinometer would be the solution. Unfortunately this is not the case, and the reason for this is very closely related to the so-called pendulum rocket fallacy.

The fact that accelerometers can be used to infer the orientation of a cell phone follows directly from the fact, that you can (for the most part) assume that the phone is held stationary in a users hand. The moment this assumption is no longer true, you can't directly measure the direction of gravity. To me this was quite a difficult thing to understand. My intuition originally was that you could always measure gravity as you did in the cell phone. It wasn't until later, when I did a simulation of the attitude measurements, that I realized the acceleration measurements didn't give any information on the direction of gravity. Anyway, if you don't believe me on this, there are some good explanations out there.

Okay. So how do you know which way is up? Very few methods allow you to measure your absolute attitude directly. Many of the methods give you only a part of your attitude and you have to combine many methods, to get your absolute attitude (roll, yaw, pitch). An incomplete list of possible methods:
  1. Guide stars. This is the only method I can think of right now which will actually give your absolute orientation. It only works at night. Requires you to know where you are and what time it is (GPS). Very difficult to measure.
  2. Horizon measurement. This could be either optical or thermal. Thermal works also during night, but not as well in the winter or in heavy overcast conditions. Can at most give you roll and pitch. Combined with GPS heading readings will give full absolute attitude. Sensors are quite expensive. Easy to measure.
  3. Earth's magnetic field measurements. Gives two components of the attitude. On the magnetic poles these components will correspond with roll and pitch. Combined with suitable other means of attitude inference can give full absolute attitude. Easy to measure.
  4. Polarization of the sky. Can be used to infer the components of the attitude, if you know where you are and what time it is (GPS). Combined with e.g. magnetic field measurements can give absolute attitude. Difficult to measure.
  5. Gyroscopes. Gyroscopes can be used to measure the angular velocity at which the plane is rotating. Hence they give you the changing rate of attitude. If you knew your attitude at start, and you had a perfect gyroscope, you could track your changes in attitude and thus know your current orientation. Unfortunately, there is no such thing as a perfect gyroscope (plus you'll have integration error anyway) and you end up with a drift. However, the drift can be quite low even with cheap solid state gyroscopes (in the order of a fraction of a degree per minute). Combined with e.g. magnetic field measurements can give absolute attitude (with a caveat). Easy to measure.
I want to only do easy stuff, so I can only really consider horizon measurement, magnetic field measurement and gyroscopes. Horizon measurements are a bit on the expensive side, and they also have problems during overcast conditions and winter (which sums up to about 90% of the time in Finland), so I'll have to go with magnetic fields and gyroscopes.

As luck would have it, some nice Chinese folks are selling small IMU boards on ebay. They have everything I need for attitude measurement, as well as an accelerometer :-).

Let's begin with the first thing. How to represent the attitude of the plane? The first thing that comes to mind is the classic roll, yaw, pitch formulation, also known as Tait-Bryan angles, or improper Euler angles. This formulation is ultimately the form I would like to look at the attitude, so it would make sense to store them internally in this form as well. This formulation however has a nasty feature known as gimbal locking. Also, the differential of the rotation has a form, which is heavy on computation (a lot of trigonometric functions, that would need to be evaluated at each time step). So this is not the way to go. I hate to say it (because I've been against the idea in the past), but a better solution is to use Euler rotation parameters, also known as unit quaternions. This formulation is minimal on computational complexity and it easily converts to Tait-Bryan angles when needed. See these lecture notes (starting from about lecture 7) for a very good tutorial on different rotation formalisms and their uses. Wikipedia also has an article about this, but it is not as good in my opinion.

The most important reason for using the quaternion formulation of rotations is the absolute simplicity of the rotation time derivative. It is given simply as
,
where q is the rotation quaternion, \omega_x, \omega_y and \omega_z are angular velocities, which are measured by the gyroscope and the \otimes is the quaternion product. Using explicit Euler integration, a pseudo code implementation of a rotation update given the instantaneous angular velocity measurement (wx, wy, wz) is

updaterotation(wx,wy,wz) {
   /* old rotation defined by rqs, rqx, rqy, rqz
      length of time step dt */
   rqs_up=rqs+dt*0.5f*(-rqx*wx-rqy*wy-rqz*wz);
   rqx_up=rqx+dt*0.5f*(+rqs*wx-rqz*wy+rqy*wz);
   rqy_up=rqy+dt*0.5f*(+rqz*wx+rqs*wy-rqx*wz);
   rqz_up=rqz+dt*0.5f*(-rqy*wx+rqx*wy+rqs*wz);
   /* normalize the quaternion */
   rqinvnorm = 1.0f/sqrtf(rqx_up*rqx_up+
                          rqy_up*rqy_up+
                          rqz_up*rqz_up+
                          rqs_up*rqs_up);
   rqx=rqx_up*rqinvnorm;
   rqy=rqy_up*rqinvnorm;
   rqz=rqz_up*rqinvnorm;
   rqs=rqs_up*rqinvnorm;
}

That takes care of integrating the angular velocity measurements to approximate our attitude. Now to get rid of the gyroscope drift by using measurements on the absolute direction of the magnetic field. As I only have very limited computational power on board, I had to come up with a really simple way to correct for the drift. I originally wanted to do something along the steps of the extended Kalman filter (extended Kalman filter because it was the least computationally expensive non-linear flavour I could think of), but it would end up using quite a deal of memory since in addition to the current best estimate of the attitude, the covariance matrix of the estimate must also be stored. Most of all, it would also use quite a bit of CPU time, since for each update we need to solve a general 3 unknown linear equation. It probably would work though, but I didn't want to go through the hassle of testing how close it would be.

What I ended up doing can in some sense be explained as approximating the extended Kalman filter, by using a constant scalar covariance for the states. More precisely I did the following. When the plane is in its starting position and we know its attitude, we take a measurement of the magnetic field. This magnetic field direction, as well as the initial attitude, are stored. In the future, since we know what the magnetic field was at the initial attitude, we can compute what the magnetic field should be if we knew our attitude. Let's call the magnetic field measurement m, the actual attitude q and the function which connects the two h. Hence


However, we don't know our actual attitude, we know only an estimate q_est, so in general


As the equality should hold for the correct attitude, it would make sense to change the estimate so that the equality would hold. This however is not a good idea. The solution to the equation lacks uniqueness, and even if we had uniqueness, any measurement errors in the magnetic field would be amplified. In addition, the exact solution takes a lot of computational power, which we don't have. What we do instead, is that we simply take a small step in the negative gradient direction. If you don't know what that means, it just means that we change the estimate only by a little amount, but so that the new estimate is slightly better. The update is then


where \alpha is a relaxation parameter, which defines how long the steps should be that the algorithm takes. The lower the value, the slower the estimate converges. The higher it is, the more unstable the update is.

The algorithm to correct for a magnetic field measurement (mx, my, mz) in pseudocode
correctrotation(mx,my,mz) {
   /* old rotation estimate defined by rqs, rqx, rqy, rqz
      north direction in initial coordinates is gmx, gmy, gmz. */
   /* compute difference between estimated and measured north direction */
   mx_diff=mx-(+rqs*(gmx*rqs+gmy*rqz-gmz*rqy)
               +rqx*(gmx*rqx+gmy*rqy+gmz*rqz)
               -rqy*(gmz*rqs+gmx*rqy-gmy*rqx)
               +rqz*(gmy*rqs-gmx*rqz+gmz*rqx));
   my_diff=my-(+rqs*(gmy*rqs-gmx*rqz+gmz*rqx)
               +rqx*(gmz*rqs+gmx*rqy-gmy*rqx)
               +rqy*(gmx*rqx+gmy*rqy+gmz*rqz)
               -rqz*(gmx*rqs+gmy*rqz-gmz*rqy));
   mz_diff=mz-(+rqs*(gmz*rqs+gmx*rqy-gmy*rqx)
               -rqx*(gmy*rqs-gmx*rqz+gmz*rqx)
               +rqy*(gmx*rqs+gmy*rqz-gmz*rqy)
               +rqz*(gmx*rqx+gmy*rqy+gmz*rqz));
   /* update rotation based on measured north direction error */
   rqs_up=rqs+alpha*(+mx_diff*(+gmx*rqs+gmy*rqz-gmz*rqy)
                     +my_diff*(-gmx*rqz+gmy*rqs+gmz*rqx)
                     +mz_diff*(+gmx*rqy-gmy*rqx+gmz*rqs));
   rqx_up=rqx+alpha*(+mx_diff*(+gmx*rqx+gmy*rqy+gmz*rqz)
                     +my_diff*(+gmx*rqy-gmy*rqx+gmz*rqs)
                     -mz_diff*(-gmx*rqz+gmy*rqs+gmz*rqx));
   rqy_up=rqy+alpha*(-mx_diff*(+gmx*rqy-gmy*rqx+gmz*rqs)
                     +my_diff*(+gmx*rqx+gmy*rqy+gmz*rqz)
                     +mz_diff*(+gmx*rqs+gmy*rqz-gmz*rqy));
   rqz_up=rqz+alpha*(+mx_diff*(-gmx*rqz+gmy*rqs+gmz*rqx)
                     -my_diff*(+gmx*rqs+gmy*rqz-gmz*rqy)
                     +mz_diff*(+gmx*rqx+gmy*rqy+gmz*rqz));
   /* normalize the rotation */
   rqinvnorm = 1.0f/sqrtf(rqx_up*rqx_up+
                          rqy_up*rqy_up+
                          rqz_up*rqz_up+
                          rqs_up*rqs_up);
   rqx=rqx_up*rqinvnorm;
   rqy=rqy_up*rqinvnorm;
   rqz=rqz_up*rqinvnorm;
   rqs=rqs_up*rqinvnorm;
}

These algorithms are easily light enough so that they can run on a low-end microcontroller, such as the ATmega328, over 200 times per second. In my tests, even after a long period (hours) of rotating the sensors around, the accumulated error is in the order of 5 degrees, which is about the accuracy of the magnetometer. However, I haven't as of yet done any in flight tests. I'm a bit worried of how much the large currents of the motor affects the magnetic field measurements.

Friday, October 5, 2012

Receiver v2 hardware design & build

As of this writing the receiver v2 is still somewhat of a work in progress, but the design of the main hardware is mostly done. In this new revision I wanted to address a couple of issues that I had with the old receiver, mainly that the old receiver was large, heavy, fragile and complex. The new receiver was thus supposed to be small, light, sturdy and simple :-). I also wanted to finally implement antenna diversity, which I never got around to do in the old receiver. In addition, I decided to dump the analog inputs altogether in favor of an external battery management board connected via I2C and to only have 8 servo channels.

The target size was 2 x 1 inches, which is about the footprint of the regular commercially available receivers. I originally hoped to get everything to fit on one board of that size, but it soon became apparent that if I were to manufacture the board myself using photoresist methods (two-sided this time, however), I would need to split the design in two. I might still do a single board version of the receiver, which could be manufactured by a commercial board manufacturer, for instance through ITEAD studio and their cheap prototyping service. Anyway, the boards of this new design are simply
  1. Connector board (schematic, layout)
  2. Main board (schematic, layout top, bottom, both)
The connector board is completely passive. As its name suggests, it only contains connectors. It mounts on the main board through a pin header and breaks out the servo connectors as well as I2C and UART.

Like before, I didn't want to buy too many parts specifically. So, many of the choices of components were dictated by what I had in my collection. In the end, I only had to order the AS169 antenna selector switch. A local electronics store Partco offers a quasi-cheap way to order a low quantity of components from wholesalers like Farnell. Normally for small orders Farnell would charge a fixed price for shipping and handling, which is in the order of tens of euros. Partco makes larger orders once per week, which make the handling fees more reasonable per item. It still ends up costing me around 25-50% more per component than if I ordered in bulk, but as we're talking about a couple of 0.50€ components for prototyping, it doesn't really matter.

For the RF design, as I am working with 868 MHz (~345mm) and the RF signal path lengths on the board are in the order of 20 millimeters, I was confident I didn't need to worry about impedance matching. Assuming perfect ground, the characteristic impedance of the signal paths would be around 75 ohm, which is way off the target of 50 ohm.

I took some photos along the way, as I was assembling the main board

Bare board, bottom side. Right after drilling and removing the photoresist.
The board looks covered in holes, but those are actually drops of water.
This board was my second ever two-sided board. The first was a success, so I was not too worried about this one.

Top side, with vias soldered in place.
Getting via holes copperized is something I don't know how to do at home. I understand that in commercial fabrication they actually begin (I mean even before applying the photoresist) by drilling the holes and then building a copper layer inside the holes by electrochemical methods. What I do is I push a copper wire through the via hole and cut it almost flush with the board. It leaves quite a bump on the board after soldering, so I can't have vias under components. This makes the board quite a deal bigger than what it could be.

Top side. Much of the components soldered.

Bottom side. Everything except the pin header and output series resistors in place.
As I got most of the stuff in place I tested that I could talk with the processor. This was just to make sure everything was working correctly before attaching the radio module, which is the most expensive component in the board by a factor of two. In the pictures, along with the radio module the top side is also missing the antenna selector switch, which I had to order specifically (I didn't have a hand-solder friendly version in my junk box). The bottom side is still missing the large female header as well as output channel protection resistors. I didn't want to end up killing the receiver if I accidentally shorted the servo connectors or if I plugged them the wrong way around. They limit the short-circuit current to the absolute maximum rating of the processor GPIOs. None of the servos I am using seem to have any ill effects due to the resistors or due to the fact that the signal fed to them is only 3.3V high. Getting rid of the resistors and instead being really careful, or using 0603 size instead of 0805 could help make this board still smaller, if I ever go for that.

Top side. Everything except for the antenna selector switch soldered.

Bottom side. Everything in place.
Here the board is otherwise complete, but I'm still waiting for my order of the antenna selector to arrive. I also haven't yet attached the antennas.


While waiting for the antenna selector, I assembled the connector board and tried it on. The two white connectors in the picture below are (from left to right) the I2C interface and the UART interface. The I2C is for connection to a battery management board, which will contain a high side battery current and voltage measurement IC (I found a suitable one from Texas Instruments, but haven't yet ordered it). In the current plan, the UART will be used to interface a navigation board that infers the attitude and the position of the plane (or other platform) from inertial measurements (gyroscopes), Earth's magnetic field (magnetometers) and GPS. More on that in a later post.



Connector board is completed and tested on.

View from the back.

The receiver is looking pretty good. I'm a bit worried about the strength of the connection between the main board and the connector board. My plan is to stick the connector board down to the main board with double sided foam tape. That way there is less stress on the header.

A close up, now with the antenna selector and the antennas connected.

The AS169 comes in a SOT-23-6 package,
which is among the largest cases for these things.

The antennas are just RG-174 coax, which have ~1/4 wavelength (82 millimeters) of the shield stripped at the end. These are by no means optimal antennas, and it is something I still need to look into. The idea anyway is to have one of the antennas vertically polarized and the other horizontal. This way I'm hopefully less likely to lose the connection due to orientation, or at least have less variation in the received signal strength. However, there still will be an orientation at which both of the antenna polarizations are orthogonal to the transmitter antenna. This is probably just a theoretical problem of ideal antennas, since with my old receiver I've been flying with just one vertically polarized antenna at a distance of 1.5km without any problems. Another choice would be to go with circular polarized antennas, but the size of them at 868 MHz seems a bit intimidating.

The bounding box of the entire assembly (not including the antennas) is approx. 51 x 26 x 22 millimeters. A plastic case would be very nice. I'll probably investigate getting one printed at Aalto University's fablab in the near future.

Tuesday, October 2, 2012

Receiver v1 after a year of flying

I found a set of pictures I had taken of my old receiver a couple of months ago, from around the time I switched to using my new receiver. The poor thing has gone through multiple hard impacts, which have resulted in torn and bent connectors and cracked solder joints. It has been through the rain and the snow. For all practical purposes however it still works today. I think I'll keep this one in my Elektro Rookie after I finally get my Skywalker in the air.

Top view. The sharpie markings used to read "5GS" to remind me of the incorrect pinout
You can see the deformations left behind by multiple crashes
The microcontroller (Atmel Atmega168)
A bodge wire routes 5V to the GPS. The original design had it connected to the 3.3V regulator
There is no antenna connected to the receiver in these pictures. I can't quite remember what I had done with it or why. Right now it has a 1/4 wave whip (that's just a wire about 82 millimeters long) connected and it works great.

Transmitter v1.5

After flying with my transmitter v1 for about 4 months, I thought it was time to upgrade. The biggest reason being that the PlayStation joysticks I ended up using were closer to digital than analog. What I mean by this, is that the joysticks had a very large dead zone and also the control saturated quickly. Moving the stick from the center to the right would change output for the first 1/3 of the range. It would then give a nice proportional output 1/3 of the range after that, and the last 1/3 of the range would again not change the output any further. This was not that big of a problem with an airplane, but I wanted eventually to fly also multicopters and I thought this was something that would really cause problems with them. Also, the way you trimmed the control in the old interface was horrible and I wanted to change that.

Randomly browsing through DealExtreme I came across this. That looked like something I could use! I would just remove all the electronics that were already in and replace them with my own. It would even have mechanical trims in place. Sure it was a bit on the expensive side, but the whole idea of building my own transmitter to save money was long gone anyway.

After a couple of hours of tinkering, I had removed the digitizer and transmitter boards from my old transmitter and moved them in the new enclosure and everything was working well.
Inside the transmitter it's a big mess of wires.
Front view of the transmitter. That antenna sure looks suspicious...
I originally used a small rubber ducky antenna with this transmitter and I never had any bigger problems with that during a couple of months of flying. I was planning to do FPV in the near future, however, and wanted to be sure my transmitter could do at least 1 kilometer. So I did what any sensible person does: I designed and built a 10dBi gain Yagi-Uda antenna and hot glued it to the back of the transmitter.
It is very awkward to carry this thing around. Flying with it is no problem though.
Based on RSSI (receive signal strength indicator) data sent in the plane telemetry, the antenna adds around 9 dB to the signal strength (so only 1dB less than what it was designed for), which is surprising considering it is made of scrap brass tubing, a couple of scrap pieces of pine and a lot of hot glue. Also the only sufficiently long piece of 50 ohm coaxial cable I could find in my scrap heap was RG-58, which I would not really consider ideal for the job. Like half of the stuff I do, this was first made as a prototype, which was to be refined later. But like always, this later never came and I was stuck with the prototype. And range wise, I've never run out of range with this thing. A simple extrapolation (with 5% packet loss) from the RSSI and range data of the telemetry would suggest a 6 km range at 100mW transmit power.

Monday, October 1, 2012

Transmitter v1

I had a receiver and I knew from testing it with my USB radio dongle, that it in fact worked. Now I needed a transmitter. So... you need two joysticks, a couple of buttons and that's it. Perhaps an LCD and a menu system later on for configuring and swapping models.

I was first considering using a ready made game controller, specifically one made for the Sony PlayStation. These were fairly cheap and readily available, and there were previous projects in which people had interfaced them with microcontrollers. I can't remember why I chose not to go with those. I guess it had to do with price.

I found thumb joysticks similar to those on the PlayStation controllers for a fairly cheap price on Sparkfun. They also provided the schematic symbol and the footprint of the component for CadSoft EAGLE, which made my work a lot easier. So I ordered a couple and made a couple of break-out boards for them (see schematic, layout). The 8 pin DIP in the board (misleadingly labeled IC1) is the 8 pin ribbon cable connector I was using.

The break-out boards are then to be connected to a digitizer board, which contained a microcontroller and would do the analog-to-digital conversion to read the position of the joysticks and would then communicate with the actual transmitter board via a serial link (see schematic, layout). Again, the 8 pin DIP parts in the board are the ribbon cable connectors.

I put the two joysticks and the digitizer in a plastic case, that was a nice size to hold in your hands.
The thumb joysticks use ribbon cables to connect to the digitizer, which in turn is connected to the PC via a RS232 serial link through a level shifter board I had made earlier (notice the bodge wire in the level shifter).
I guess I'm developing the digitizer software in this picture. Also, my bench is a mess.

Completed joystick and digitizer assembly on the dining room table, with some miscellaneous crap. 

The transmitter board itself was a bit more complicated, mostly because I wanted to add an extension port to it. I was planning on extending the two board design into a larger one, where I'd have an LCD and a menu system working on that. This never came to be, so most of the effort designing the more complex transmitter board was a waste (see schematic, layout). Both the transmitter and the digitizer use a 3.1V low-dropout regulator to be able to operate from a single lithium-polymer cell (and also because I happened to have a hundred of such regulators).
The transmitter board has just finished etching.
This picture was taken before removing the photoresist.
Top view of the transmitter board
Bottom view of the transmitter board
The software on the transmitter board basically does the following in an endless loop:
  1. Request a measurement from the digitizer board
  2. Wait a certain time for the digitizer board to respond
  3. Check integrity of received measurement
  4. Transmit packet to receiver over radio
  5. Wait a certain time for the receiver to respond over radio

It the digitizer doesn't respond within the window, a default (fail safe) control is sent. This is also the case, if the integrity of the measurement cannot be validated. I've actually never had the control fail due to problems with the transmitter, but the fail safe actions are still good to have there in place.

While the joysticks and digitizer did get a nice enclosure, the transmitter board wasn't so lucky. I ended up putting it inside an Orthex freezer container together with the battery I used to power the whole thing. Prior to operation, you'd have to put the box very close to your face (~1cm) to see if the green power LED was flashing inside the container. Any further away and the sun would prevent you from seeing the light. The whole process of using that transmitter was silly-looking.

And talking about silly, as I had never flown a model when I was designing all of this, I completely overlooked how important trimming the controls were. To set the trim on the left stick, you would move the stick so that control surfaces corresponded to your new zero, then you would tap the right stick. To trim the right stick, you would just do the same, but with sticks reversed. This effectively made it impossible to trim while in flight. I tried it a couple of times, and each time it ended badly. What I ended up doing, was I coarsely trimmed the plane on the ground, only eyeballing the control surface positions. In the air I would then just compensate. Even though I have proper trims on my current revision of the transmitter, I still tend to do this.

Probably the weirdest RC transmitter you've seen in a while.
Notice the strip of packing tape keeping the two halves together.
It turned out that the PlayStation thumb joysticks have a large dead zone and also their usable range is not to the very edge, which leaves them with quite a limited dynamic range, which I don't think is a very good thing for RC (especially multicopters). However, I did learn to fly my first plane (the Graupner Elektro Rookie) with this transmitter and it worked without any real problems. It might have been a bit easier to get a hang on things with a real transmitter, especially due to trimming the controls being very difficult.

The transmitter board and the digitizer board are still in use in my transmitter v1.5, which is what I use today. Basically I just changed the joysticks for something a bit nicer. I will write an article on that upgrade. However, I'm currently in the process of completely redesigning the transmitter to have support for an antenna tracker, a head tracker and most importantly: a ground-based OSD to my FPV video feed.