Saturday, 14 June 2014

Arduino...

posted 5 Feb 2013 23:19 by David Taylor

I have been porting my .Net MF code ofer to the Arduino ProMicro 5v/16MHz and finaly got the code to do something today.
The arduino IDE is really bad if you're used to using Microsoft's Visual Studio. This process was made allot easier once I set Visual Studio up to create Arduino projects using Visual Micro.
Turns out though that the main problem was in the firmware I had written, as detailed in my post entitled "Arduino Pro Micro: how to access A1" at http://electronics.stackexchange.com/.
My arduino interface board still has a couple of buggs and requires one bodge wire which I have identified so far, but here it is:
Arduino Interface Board
Once I get this thing flying, I'll upload the Eagle files here.
This is proving to be a slow and painfull process.

3D printable model of TriRot

posted 21 Feb 2012 08:29 by David Taylor   [ updated 21 Feb 2012 09:08 ]

I have just uploaded a 3D printable model  of TriRot to cubify.com. (http://cubify.com/store/model_view.aspx?reference=XL4Vbfjrw3Ww)
I've uploaded individual models of each part, and also a complete model with all the parts. If you use their cloud printing service you could get the full kit, or order individual replacement parts after a crash.
I've also included STL (Stereo Lithography) files for each part so you could print it yourself if you had access to a 3D printer.
I haven't been able to test any of the 3D print models yet though, so I'd love to hear from anyone who gives it a try.

PID Success

posted 7 Feb 2012 06:09 by David Taylor

I was able to spend a bit of time this weekend working on TriRot again. I've really been trying to figure out why the PID loop I wrote was not working at all. I've done research and tried tuning it to no avail. In the end, it turned out to be a simple bug in my code. (Using the Last IMU data as opposed to the Last Error data in the loop).
The bonus is that it now does not randomly go completely insane while trying to eat anything in range with its three rotors.
Right now it blocks disturbances, but then takes a while to fully correct the movement. In my head this sounds like I need to increase the Integration Gain, which I would probably only be able to test this weekend. (Studying is taking priority over all else at the moment ;-).
In fact; the only reason I am spending any time at all on TriRot is because I am using it as part of my research for my Thesis this year. (Whish me luck, I think I'm going to need it ;-)
Anyway, I'll post picks and/or videos of TriRot testing in its training rig, and hopefully of it flying on its own soon.

TriRot in "training wheels"

posted 23 Jan 2012 15:01 by David Taylor

So now that the hardware is done, and I have a PID control loop coded in, it’s on to PID Tuning. Even the limited research that I have done to date suggests that this is going to be difficult. Not wanting to break parts of TriRot and purchase new rotors on every attempt I opted to building a kind of training ring.

TriRot fits perfectly inside. I’ve already used it a couple of times without much success though. I did find a bug in my code today which (once fixed) improved things about 500%. At this stage I do think it really is down to just tuning the PID loop since everything else now actually works as expected.
I’ll post another video as soon as things improve a bit.
I am however really contemplating buying an Arduino Mini Pro board for this as I’ve reads lots of stuff on the net saying that .Net is just not the right environment to do real time processing in. I’ve even asked Clint Rutkas from Microsoft the question and had it basically confirmed, although I’m still trying to understand exactly why this is.
Chris from Secret labs did send me a link via Clint showing a working .Net quad: http://forums.netduino.com/index.php?/topic/1439-quadcopter-early-flight/
As you can see it’s not quite stable. Is this due to the implementation or is the .net framework really just not capable of doing this? If ye, why not?

PID Control

posted 24 Nov 2011 05:36 by David Taylor   [ updated 24 Nov 2011 05:43 ]

After reading through some more research trying to understand the reason TriRot was not just perfectly stable when it lifted off for its first flight, I came across that old jewel that I last looked at when I was studying engineering, The PID controller, where the acronym stands for Proportional, Integral, Differential controller.

What I had originally was a 1:1 control system. If the IMU said I was tilting 5 degrees, I would correct exactly the same in the other direction. Looking back now, this does not account for the inertia of the system and any overshoot would be dealt with very slowly resulting in an unstable system (as evidenced by that last video ;-).

Proportional ControlThis simply means that if the error is X you respond with X*Y where Y is the relative proportion you want to respond with. I.E., if Y = 1.5 and the error is 2 you will at that stage respond with 3. The idea being that you slightly exaggerate your response to get a quick correction. Y is referred to as the Gain Factor or Kp for Proportional Gain.

Integral ControlHere we look at the time the error has been around. So you take the amplitude of the error and you multiply it with the time it’s been around. You also add a gain value to this for good measure (Ki). Effectively, you are working out the Area a graph is occupying. The graph here would be the output from the IMU. The best way to work out the area a graph occupies is to use integration.

Differential ControlThis looks at the rate of change, or effectively the slope of the graph. This term also has a Gain factor, but in this case actually limiting the output, since your gain is generally in the 0.002 range.

I don’t think I need to do full integration nor am I able to do full differentiation since I don’t know the function of the graph that will be generated by the random movements.

So For integration I simply multiply the current amplitude with the timespan since the last loop and then sum that with previous calculations. For the differentiation, I again take the difference in amplitude (or y) and divide it by the difference in time (or again the timespan since the last loop).

I’ve only just uploaded this to TriRot last night, spun up the motors (trying not to wake the kids) and moved it around a bit in my hand, but it seems allot more aggressive.

I do still have to do the PID Tuning, which I’m expecting to be the hard part.

Here is a Graph of a previous Pitch Test I took from TriRot. Effectively it’s just an output from the IMU in the Pitch axis, to which I have applied the PID algorithms in Excel. This is what convinced me that this might just work.

In this test:
  • Kp = 1.1,
  • Ki = 0.25, and
  • Kd = 0.01

TriRot – First flight Tests

posted 18 Oct 2011 02:26 by David Taylor

Over the last couple of weeks I’ve been trying to get TriRot airborne… and it’s been interesting.
My initial worry was that TriRot may be too heavy, so I trimmed off all the excess and brought the weight down from 553grams to 410grams. This included getting a smaller battery. I even turned the motors around to get rid of the prop adapters in favour of the built in prop savers. The vireo below shows why I now hate prop savers ;-)
TriRot - First Flight Test
I then spent the next week building arms that could hold my motors the other way around so that I could fit prop adapters and also support bigger props. And then I had success. TriRot lifted quite easily, just to crash about 2 seconds later… See the next Video.


TriRot - First Flight Test ... Again
Broken Props, and one arm
The problem now is that I to fix the brazing on the broken arm, I’ll have to take the motors completely out again as I will be heating them to about 600 degrees Celsius if I don’t. The plan at the moment is to fix it temporarily with cable ties or glue and work on getting TriRot to fly first. Once everything is stable, I’ll braze the mounting plate back onto the arm.
I think I know what caused the crash too. I used the z axis output from the IMU to auto correct the yaw using the servo angle. I did get some early comments saying that this was not a good idea, but I tried it anyway. Watching the video seemed to show that it was a Z-axis spin that prevented TriRot from correcting its hover, so I’ll update the code before the next test.
I’ve ordered new props last night, so hopefully they will arrive soon.
Ps. Thanks to everyone who have made various suggestions so far. Keep them coming. I need them, because as I mentioned right in the beginning, this is my first attempt at flying anything [remote controlled].

410 grams all in

posted 29 Sep 2011 03:41 by David Taylor

I will do a flight test this weekend.
I will post an update with a video but hopefully without any pictures of mangled parts though. :-/

Losing more weight

posted 28 Sep 2011 03:14 by David Taylor

I’ve now replaced 15 metal nuts and bolts at 2grams each with Nylon version which weigh 0.32grams each.
TriRot Estimated weight is currently 428grams. Subtracting (15 x 2) = 30grams and then adding (15 x 0.32) = 4.8 and we’re down to 402.8 grams.
I have another 9 sets to replace dropping another 15grams and bringing TriRot to 388grams all in. This might vary slightly because I have also added slightly bigger rotors though, so I’ll post final stats once I’m done.
I also compiled, what I believe to be, flight ready code in release mode and deployed it to TriRot last night. I’m currently in that state between fear and excitement about trying to fly it for the first time.

Pitch Mixing (Video)

posted 23 Sep 2011 01:55 by David Taylor

I recorded a quick video this morning showing me testing TriRot’s Pitch Mixing routines. I zip-tied a pivot beam to the chassis to limit movements to the Pitch axis only and also commented out the mixing code for Roll and Yaw.


In the video you can also see TriRot going through its full pre-flight self-check. Flashing the LED’s 5 times after the motors spin up for the first time means all is good and we are ready to go. I temporarily set channel 5 on my remote to be a kill switch. When this switch is toggled it either sends command to the motors and servo, or not. This is very handy for this part of the testing, where I’m not quite sure what TriRot will do as soon as it lifts off.
Below is a graph based on the data I captured during this test:
·         The top blue line is both the left and right motor speeds.
·         The Green line is the throttle
·         The orange line shows the rear motor speed.
·         The red line shows the input from the Received. It shows that I did not control TriRot from the Transmitter and that it is self-stabilising. (Notice that it is biased a bit forward in this graph and not on zero, because the pivot is a bit too far back so there is more weight in the front.)
·         The blue line in the bottom shows the Yaw input from the IMU.
I have done exactly the same testing to sort out the Roll axis. Tonight (possibly) I will be testing the Yaw axis. I think this is going to be a bit more complicated because I need to be able to override the IMU Yaw over time; otherwise TriRot will always try to turn back into the direction it started up in. But, when I’m not applying any Yaw input it should hold in that direction.
Watch this space.

-125grams = TriRot now down to 428g

posted 19 Sep 2011 02:22 by David Taylor

I went out of Saturday and bought a new 850mAh 20C 3S LiPo from the local hobby shop. It weighs in at a measly 75g. My original 2200mAh 30C battery was 200g. (I still have to update my partslist)
I’m hoping to drop another 30-60g with the replacement of all the metal nuts and bolts. As soon as by bigger rotors arrive I should also increase the lift by quite a bit.
At the moment, I’m working on refining the flight control routine. This is where I take the incoming receiver channels and mix them with the IMU data to control TriRot during flight. Some initial testing shows that the logic seems to work, but I may need to change some offsets and multipliers.

1st [almost] Flight test

posted 16 Sep 2011 14:28 by David Taylor

After some minor software issues, including removing some debug code. The fact that the throttle was reversed made a fairly nervous moment even more exciting.
At full throttle TriRot was sort of just leaning upwards but not actually lifting off. At least it gave me a chance to test the rest of the controls. I seem to be reading the Radio control pretty well and everything else seems good.
I have a couple of ideas in mind some of which are already in progress:
1.       Replace the triple rotor 5x3 GWS props with 6x3 props. This adds a substantial amount of trust without exceeding the motor and ESC current. Under some earlier testing, the total current drawn from the battery was less than 1 amp. (I’ve ordered these already and they should be arriving on Monday)
2.       Replace all steel nuts and bolts with nylon versions. From some basic (online) checking they seem to be roughly half the weight. I’m estimating that this should save roughly 50g. I ordered these today and they should be arriving next week.
3.       Replace the 190g 2200mAh battery with a 96g 1050mAh version. This would give me reduced flight time but also drop close to 100g off the total weight.
4.       Rebuild the motor mounts so that I can fit a propeller to the top and bottom of the motors effectively adding 3 more blades to every motor. (Initial calculations indicate a marginal increase in thrust though.) I’ve seen some commercial UAV’s that are built like this.
Current weight:553g
replace current battery:-190g
with a lighter version:+ 96g
replace current fixings:25 
x 5g
- 125g
with lighter nylon versions:+ 63g
= 397g
I’m hoping that dropping roughly 150g (28%) in addition to the additional thrust produced by bigger (and/or) more propellers will get it off the ground.

More Parts

posted 29 Jul 2011 01:12 by David Taylor

I've updated the parts list to show my latest procurements being the RC Transmitter and receiver pair and an 8 Channel PPM Encoder from DYIDrones.
I also bought some tools to help with TriRots diet, but that's a different post.

IMU Test - Video

posted 19 Jul 2011 04:55 by David Taylor

So now that I have delved into the depths of 2’s compliment and also made sure I was using my incoming data in the correct order for the BitConverter, the Complementary filter from NuclearProjects.com, my IMU is working perfectly.

TriRot - IMU Test


In the video I am using the Altitude indicator that Jesse wrote to test it with. (He even made the full source code available on CodeProject).
He has even implemented some calculations that now allows TriRot to track the z-Axis as well for Yaw control. Although this implementation is only based on Gyro’s (ITG-3200) and Accelerometers (ADXL345) ,which could drift over time, it is holding rock solid in tests so far.
The C# code that makes the above possible:
  1: using System;
  2: using Microsoft.SPOT;
  3: using Tri_Rot_Version_1;
  4: using System.Threading;
  5: using GHIElectronics.NETMF.System;
  6:
  7: namespace IMUComplementaryFilter
  8: {
  9:     public class Program
 10:     {
 11:
 12:         private static Int32 gXZeroValue = 0;
 13:         private static Int32 gYZeroValue = 0;
 14:         private static Int32 gZZeroValue = 0;
 15:
 16:         private static Int32 aXZeroValue = 0;
 17:         private static Int32 aYZeroValue = 0;
 18:         private static Int32 aZZeroValue = 0;
 19:         private const Double Rad2Deg  = 57.2957795; // 1 radian = 57.2957795 degrees
 20:         private const Double Deg2Rad = 0.0174532925; // 0.0174532925 rads = 1 deg
 21:
 22:
 23:         public static void Main()
 24:         {
 25:             Debug.EnableGCMessages(false);
 26:             int i = 0;
 27:             Int16[] g;
 28:             Int16[] a;
 29:
 30:             using (Gyro = new ITG3200I2C())
 31:             {
 32:                 Gyro.StartUp();
 33:             }
 34:
 35:             using (Acc = new ADXL345I2C())
 36:             {
 37:                 Acc.StartUp();
 38:             }
 39:             Thread.Sleep(100);
 40:
 41:             Int16 gXTemp = 0;
 42:             Int16 gYTemp = 0;
 43:             Int16 gZTemp = 0;
 44:
 45:             Int16 aXTemp = 0;
 46:             Int16 aYTemp = 0;
 47:             Int16 aZTemp = 0;
 48:
 49:             while (i < 100)
 50:             {
 51:                 g = getGyroData();
 52:                 a = getAccData();
 53:                 Thread.Sleep(20);
 54:                 i++;
 55:             }
 56:             i = 0;
 57:             while(i < 100)
 58:             {
 59:                 g = getGyroData();
 60:                 a = getAccData();
 71:
 72:                 gXTemp += g[0];
 73:                 gYTemp += g[1];
 74:                 gZTemp += g[2];
 75:
 76:                 aXTemp += a[0];
 77:                 aYTemp += a[1];
 78:                 aZTemp += a[2];
 79:
 80:                 Thread.Sleep(20);
 81:                 i++;
 82:             }
 83:             gXZeroValue = (Int16)(gXTemp / 100F);
 84:             gYZeroValue = (Int16)(gYTemp / 100F);
 85:             gZZeroValue = (Int16)(gZTemp / 100F);
 86:                               
 87:             aXZeroValue = (Int16)(aXTemp / 100F);
 88:             aYZeroValue = (Int16)(aYTemp / 100F);
 89:             aZZeroValue = (Int16)(aZTemp / 100F);
 90:
 91:             Double GyroRateX = 0.0;
 92:             Double GyroRateY = 0.0;
 93:             Double GyroAngleX = 0.0;
 94:             Double GyroAngleY = 0.0;
 95:             Double GyroAngleZ = 0.0;
 96:
 97:             Double AccelX = 0.0;
 98:             Double AccelY = 0.0;
 99:             Double AccelZ = 0.0;
100:             Double AccelAngleX = 0.0;
101:             Double AccelAngleY = 0.0;
102:             Double AccelAngleZ = 0.0;
103:             Double Roll = 0.0;
104:             Double Pitch = 0.0;
105:             Double Yaw = 0.0;
106:
107:             Int16 minValx = -4095;
108:             Int16 maxValx = 4095;
109:             Int16 minValy = -4095;
110:             Int16 maxValy = 4095;
111:             Int16 minValz = -4095;
112:             Int16 maxValz = 4095;
113:
114:             long currentTime;
115:             Single interval;
116:             long lastTime = DateTime.Now.Ticks;
117:
118:             long TicksPerMillisecond = TimeSpan.TicksPerMillisecond;
119:             Single GyroAngleZ_dt = 0;
120:
121:             Telemetry telemetry = new Telemetry();
122:
123:
124:             while (true)
125:             {
126:                 currentTime = DateTime.Now.Ticks;
127:                 interval = ((currentTime - lastTime) / TicksPerMillisecond)/1000F; //in milliseconds
128:                 lastTime = currentTime;
129:
130:                 g = getGyroData();
131:                 a = getAccData();
132:
133:                 GyroRateX = -1.0 * interval * (g[0] - gXZeroValue) / 14.375F;
134:                 GyroRateY = interval * (g[1] - gYZeroValue) / 14.375F;
135:
136:                 GyroAngleZ_dt = interval * (g[2] - gZZeroValue) / 14.375F;
137:
138:                 GyroAngleZ += -1.0 * GyroAngleZ_dt * (1 / (MathEx.Cos(Deg2Rad * Roll))); // convert Roll angle to Rads, find sin to use as scaler for Yaw
139:
140:                 if (GyroAngleZ < 0) GyroAngleZ += 360;    // Keep within range of 0- 360 deg
141:                 if (GyroAngleZ >= 360) GyroAngleZ -= 360;
142:                 //----------------------------------------------------------------
143:
144:                 //convert read values to degrees -90 to 90 - Needed for atan2
145:                 Single xAng = map(a[0], minValx, maxValx, -90, 90);
146:                 Single yAng = map(a[1], minValy, maxValy, -90, 90);
147:                 Single zAng = map(a[2], minValz, maxValz, -90, 90);
148:
149:                 //Caculate 360deg values like so: atan2(-yAng, -zAng)
150:                 //atan2 outputs the value of -π to π (radians)
151:                 //We are then converting the radians to degrees
152:                 AccelAngleX = Rad2Deg * (MathEx.Atan2(-xAng, -zAng) + MathEx.PI);
153:                 AccelAngleY = Rad2Deg * (MathEx.Atan2(-yAng, -zAng) + MathEx.PI);
154:
155:                 // Keep angles between +-180deg
156:                 if (AccelAngleX > 180) AccelAngleX = AccelAngleX - 360;
157:
158:                 if (AccelAngleY <= 180) AccelAngleY = -1.0 * AccelAngleY;
159:                 if (AccelAngleY > 180) AccelAngleY = 360 - AccelAngleY;
160:
161:                 // Final values...
162:                 Roll = (0.98) * (Roll + GyroRateX) + (0.02) * (AccelAngleX);
163:                 Pitch = (0.98) * (Pitch + GyroRateY) + (0.02) * (AccelAngleY);
164:                 Yaw = GyroAngleZ;
165:
166:                 //Debug.Print(Roll.ToString() + "," + Pitch.ToString() + "," + Yaw.ToString());
167:                 telemetry.SendData(new TelemetryData(0, 0, 0, 0, (Single)Roll, (Single)Pitch, (Single)Yaw));
168:                 Thread.Sleep(50);
169:             }
170:         }
171:
172:         private static Single map(Single x, Single in_min, Single in_max, Single out_min, Single out_max)
173:         {
174:             return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
175:         }
176:         
177:         private static ITG3200I2C Gyro;
178:         private static ADXL345I2C Acc;
179:
180:         private static Int16[] getGyroData()
181:         {
182:             Int16[] data;
183:             using (Gyro = new ITG3200I2C())
184:             {
185:                 data = Gyro.AdcR();
186:             }
187:             return data;
188:         }
189:
190:         private static Int16[] getAccData()
191:         {
192:             Int16[] data;
193:             using (Acc = new ADXL345I2C())
194:             {
195:                 data = Acc.AdcR();
196:             }
197:             return data;
198:         }
199:
200:     }
201: }

I have now also finally updated my PartsList after noticing that I had never added the Gyro or the Accelerometer to the it.
Next steps would be to buy the Transmitter and Receiver pair and a PPM Encoder for interfacing into TriRot's Interface board.