25 June 2013

EtekChopper: Making a 250lb Engine Disappear

TL;DR: We tried to start it. We failed. Now the Engine is gone. 
One of the first steps in getting my CB750 ready for electrification is removing all unwanted crap related to the gas engine, and essentially be left with only wheels, suspension, brakes, lights, etc. However, I want to check whether or not the engine will even remotely start before I go ahead and throw away a perfectly good hunk of steel. I was assisted by MIT alum, former CB750 owner, and my supervisor at my summer job at Aurora Flight Sciences Terrence McKenna, by fellow intern and MIT student Thomas Villalon, and two housemates of mine looking to eventually build their own personal EVs Adam Rodriguez and Miranda Gavrin. 

Because most of the cables were straight-up cut off of the "spinal cord" of electrical wiring, it involved a lot of multimeter continuity checks and squinting at the manual's electrical diagram to figure out what went to what. There is both an electrical starter motor and an alternator (I thought the alternator was the starter for a while, but I was mistaken). The starter is attached to a relay with an integrated circuit tying a handlebar-mounted starter switch to the motor, as well as preventing the system from working unless the keyswitch in the bike is turned to "On". Because none of these switches were spared from the "spinal tap", we had to strip the wires and just electrical-tape them together. We attached a trusty A123 12v7 battery to the starter circuit. 

No dice. Comatose. Not one remote sign of movement, where a coughing engine would have been something. After double checking all the connections and verifying we weren't being idiots, we declared this engine dead, or too borked to warrant spending the time and money necessary to get it back running again. 

The first step in engine removal according to the manual was to unbolt and remove the carburetor from the frame. In order to remove that, though, the bulky air filter unit needed to be moved fully back. In order to do THAT, however, the battery holder framing had to be removed. Even after doing all that, the carbs would not budge from the rubber hose output connection the the engine combustion chambers. 


That's when we pulled out the Sawzall and tore that sucker out. With the hoses permanently cut, there was no going back on our decision to junk the engine. 


The carburetors weren't in bad shape, though. I'm going to take these apart, clean and oil it and maybe sell it on ebay for a half-decent amount.


Next was decoupling the rear wheel from the rest of the transmission. After removing the chain cover and loosening the rear wheel placement adjuster/chain tensioner, we were able to get the massive #530HT (High Tensile) chain off the rear sprocket.


Then came removing the chain from the small 15-tooth transmission output sprocket in order to free the transmission assembly from the rest of the bike.


This involved removing the sprocket itself. I decided to keep everything that I could remotely reuse, chain drive components included.


We then lifted the engine/transmission assembly with a jack and hammered out the attachment shoulderscrew/pins.



A hefty kick...



And the motor comes out! Note the lack of an oil pan in the transmission (we are now looking at the underside of the engine/transmission). Poor poor engine... I called the local scrapyard, Atlas Metals, to pick it up the next day free of charge. 



Here Adam gets a feel for the bike. It looks insanely high (like you can't touch the ground with your feet), but that's because it is up on the large kickstand. Still, I think it would be worth it to lower the front fork and possibly replace the rusty rear suspension with new, shorter shocks, so my stocky 5'8" build can comfortably and safely sit in the driver's seat.

Without the ~250lb engine, the ~500lb motorcycle was now SOO MUCH MORE MANEUVERABLE. Lowering the bike (and thus lowering the center of gravity) will help the bike feel lighter by decreasing its tendency to tip over. 


Goodnight, sweet prince. Soon you will live again, kissed by the power of electricity. 
Next step: drive and power train design!

20 June 2013

So I Bought a Motorcycle... EV CONVERSION TIME!

A 1979 Honda CB750K to be exact. I'm gonna convert it to electric, but first the story of HOW I GOT THE THING: 

So fellow MITERS denzien Bayley decided to buy a for-parts 1982 CB750 for $250 off of Craigslist. His goal is to use a Prius motor controller to drive a Ford hybrid motor, and get an effective electric motorcycle. Often called the first superbike, or "King of Motorcycles", the CB750 (first produced in 1969) is a legendary motorcycle for being the original Universal Japanese Motorcycle (UJM). It was also one of the first commercial motorcycles to sport four separate carburetors (which mix gasoline and air in preparation for cumbustion), a straight 4-cylinder single overhead (later double-overhead) cam 75-horsepower (~50Kw) engine, an electric starter, turn signal blinkers, a 5-speed transmission, front disk brake, etc all in one ~$2000.00 (in 1969 dollars, which is like $12,000.00 today) package, which was unheard of at the time. With a top speed of 125 mph and plenty of motor torque, this bike can soar. 

Many people like the badassery associated with Kawasaki Ninja type motorcycles, but I've always wanted a classic-looking motorcycle. Not WWII classic, but one like the one above (from the US version of The Girl With The Dragon Tattoo). After researching the CB750 and hearing Bayley got his for $250, it also had my attention. 

The motorcycle was originally around 500lbs, but after helping him remove the non-functioning 250-lb engine, the remaining 250-lb frame felt both light and strong, certainly worthy of EV-ifying. 

Now, before Bayley found the Prius controller, he was going to use a Sevcon Gen 4  motor controller on it. This controller is found in commercial electric vehicles, and is a favorite among EV enthusiasts. I've been on his back lately about finishing the Melonchopper custom motor controller, but it seems the magic transistor he was going to use is still unavailable. Imagine my surprise when he just says "here, have a Sevcon 4!" and hands me a $925.00 motor controller!

Looking through the specifications of the controller, it can output 350 Amps peak at up to about 96 Volts (~35kW!!!!!!!). However, it seems the 80mm-class "melon" motor which powers Melonchopper is barely powerful enough to take advantage of the what the controller has to offer: a waste of a $1k controller Bayley handed me for free. 

What should I do with this Sevcon Gen4 controller? Should I perhaps build an electric motorcycle? Where can I get my hand on a good enough motor? As with most things, I consult my good friend and mentor Charles Guan, but he didn't have to do much convincing: next to him sat an electric motorcycle that was undergoing some maintenance. 

The motorcycle belonged to an IDC student Lennon Rodgers, who documented the entire build on his website, electricmotion.org. Charles said there was an ME0907 etek-style motor lying around MITERS up for grabs, which can draw 220 Amps peak, 80 Amps continuous at 72 Volts, for a peak power of about 18 kW (about 25 horsepower, a third the power of the CB750 motor), nearly 3 times as powerful as the "melon" motor.

And so, I decided I'm doing my own EV conversion of a motorcycle. I have the knowledge and experience, a decent (and free!) motor controller capable of putting out 35kW peak, a decent (and free!) etek-style motor capable of handling about 18 kW peak, access to a number of machine tools, and a rewarding summer job. When Bayley offered to lend me his craigslist-scoping skills, I took his offer. Within minutes, I was looking at a Craigslist post for a for-parts 1979 Honda CB750K for only $100. I took the offer. 

The next day after work I went to Revere, MA to pick up the motorcycle, UHaul motorcycle trailer in tow, accompanied by both Bayley and Thomas Villalon (interning with me at Aurora Flight Sciences this summer and a member of the MIT Solar Electric Vehicle Team). 

And here she is! Exactly as ordered, a CB750 worth $100. We tried to start the engine with the seller's handheld car starter, to no avail. It's either bad wiring, a faulty starter, or a seized motor. 
At least the rear drum brake works! The front disk brake needs to be tuned, but should be okay. The frame, tank, and body looks alright, surface rust is nothing a little sanding and repainting won't fix. I'm really digging the two-person leather seat, and the wheels and tires look to be in good shape as well. 

Here she is ratchet strapped and otherwise rigged to the motorcycle trailer. 

The seller also gave me a spare front kit from another Honda cycle (with a headlight and blinkers) as well as a saddlebag frame addition and two hard-case lockable saddlebags (In which I may store extra batteries for higher range). 

Arrival at MITERS

Thomas checking out the bike with the add-ons thrown on it. $100.00 was a pretty good deal for everything I got!

Here it is with just the seat on. Sexy. 

The next day I decided to run a random check of how things are working out. If it isn't difficult, I may go the Mikuvan way and at least attempt to get the engine running. First thing I noticed was there was no throttle, just a box with the steel cables. Also, the choke (which controls how much air goes into the fuel-air mixture) seemed to not want to budge. 

To check under the hood, you use your key to remove the seat and access the battery area. 

Part of the seat adapter was bent out of shape, so I'll need to make sure that gets back on correctly. I also need to clean out some of the rust on the underside of the seat. 

The fuel tank just... comes off. 0_0 I was expecting there to be some sort of cable I have to remove, or for the gas to start pouring out the moment I removed the tank, but nothing. 

And here's why: the fuel movement from the tank to the carburetors is controlled with this valve here, located under the tank, and it is currently turned shut. But how does fuel get from here to the engine?

Oh. Fuel line's cut.
Bummer. 

At least there's oil in the... OH WAIT. THERE IS NO OIL BECAUSE THERE IS NO OILPAN INSTALLED ON THE BIKE. The entire bottom of the transmission is simply... open. At least I don't need to drain it!

As for the electronics/lights on the bike, most of it looks like this. It's manageable, and all of the colors for the wires are detailed in the manual, but still kind of annoying. 

The front brake doesn't seem to do anything, so I need to drain and replace the old brake fluid. The manual seems to detail all. 

YAYYYY!!!!!!!! :D 

27 May 2013

My First Quadrotor: Control Overview and Filtering Sensor Feedback

So I'm about to sit down and write the flight controller for my Quadrotor (My First Quadrotor? MFQ? MFQuad? MFCopter?), but rather than simply document that "I wrote the code and it worked", like I have been doing, I am going to try and be more explicit with my thought processes, in order to become more aware of my coding style, as well as to receive criticism in my style. 

So, a quad flight controller. What's that?

In its most basic form, a Quadrotor Flight Controller (QFC for now, because Three Letter Acronyms (TLAs) are Really Freaking Cool (RFQ)) does the following: 

  • Reads data from the Gyroscope and Accelerometer
  • Filters it into a usable form
  • Generate commands the four motors based on the sensor values and user input in order to keep the quadrotor in a certain state, which is usually balancing pitch and roll to keep a stable hovering platform. 
  • Drives motors with these commands

Additionally, a QFC should be able to wirelessly receive commands from an external source in order to be able to move around. This can be as simple as 4 servo commands from a handheld receiver, or as complicated as X,Y,Z,Pitch,Roll,Yaw, and their derivatives. To keep the amount of data being transferred wirelessly down, however, I will keep this down to pitch, roll, yaw, and altitude.

Finally, for robustness, the QFC should be able to keep the quadrotor stable in the case of a broken connection mid-flight. Should bytes be dropped or a sudden change in communication occur, an onboard "Lost Connection" timeout will trigger, and the quadrotor will orient itself to a more stable orientation (The Pitch and Roll commands will reset to 0, or a landing sequence will initiate). 

Okay.

*cracks knuckles*
Let's do this. 


So first of all our QFC needs a way to read sensor data. The gorgeous MultiWii board Shane was kind enough to give me is essentially an Arduino Nano 328 with a built-in 3-axis accelerometer, 3-axis gyroscope, magnetometer, barometer, and a bunch of servo-style breakout pins. The sensors all communicate with the Atmega 328 using the i2c protocol. While I have dealt with i2c communication at my job, every device is different, and requires slightly different requests/commands. 

Shane gave me some example MultiWii code to be able to start talking with the sensors over i2c.

 #include <Wire.h>  
   
 // I2C addresses for each sensor:  
 #define GYR_ADR 104  
 #define ACC_ADR 64  
 #define BMP085_ADDRESS 119  
 #define MAG_ADR 30  
   
 // sensor scaling  
 #define GYR_K 0.06956           // [(deg/s)/LSB]  
 #define ACC_K 0.01399           // [deg/LSB]  
 #define VEL_K 0.002395          // [(ft/s^2)/LSB]  
 #define ACC_Z_ZERO 4096          // [LSB]  
 #define RADTODEG 57.2957795  
   
 float gyro_pitch, gyro_roll, gyro_yaw;       // [deg/s]  
 float accel_pitch, accel_roll;           // [deg]  
 float accel_x;                   // [ft/s^2]  
 float accel_y;                   // [ft/s^2]  
 float accel_z;                   // [ft/s^2]  
 float rate_z;                   // [ft/s]  
   
 void setup(){  
  Wire.begin();                  // init I2C  
  TWBR = ((F_CPU / 400000) - 16) / 2;       // fast I2C   
  initSensors();  
 }  
   
 void initSensors(){  
  // initialize the ITG-3205 gyro  
  // range: 2000deg/s, bandwidth: 188Hz  
  Wire.beginTransmission(GYR_ADR);  
  Wire.write(0x16);            
  Wire.write(0x19);            
  Wire.endTransmission();  
    
  // initialize the BMA180 accelerometer  
  // range: 2g, bandwidth: 150Hz (all defaults)   
 }  
   
 void readSensors(){  
  unsigned char i;  
  unsigned char raw_data[6];  
  signed int raw_value;  
    
  // read the ITG-3205 gyro  
  Wire.beginTransmission(GYR_ADR);  
  Wire.write(0x1D);                    
  Wire.endTransmission();  
    
  i = 0;  
  Wire.requestFrom(GYR_ADR, 6);  
  while(Wire.available())  
  {  
   raw_data[i++] = Wire.read();  
  }  
    
  // convert raw gyro data to [deg/s]  
  raw_value = (raw_data[0] << 8) + raw_data[1];  
  gyro_pitch = -1.0*(float) raw_value * GYR_K;  
  raw_value = (raw_data[2] << 8) + raw_data[3];  
  gyro_roll = (float) raw_value * GYR_K;  
  raw_value = (raw_data[4] << 8) + raw_data[5];  
  gyro_yaw = (float) raw_value * GYR_K;  
    
  // remind the gyro to stay in 2000deg/s mode???  
  // range: 2000deg/s, bandwidth: 188Hz  
  Wire.beginTransmission(GYR_ADR);  
  Wire.write(0x16);            
  Wire.write(0x19);            
  Wire.endTransmission();  
    
  // read the BMA180 accelerometer  
  Wire.beginTransmission(ACC_ADR);  
  Wire.write(0x02);                    
  Wire.endTransmission();  
    
  i = 0;  
  Wire.requestFrom(ACC_ADR, 6);  
  while(Wire.available())  
  {  
   raw_data[i++] = Wire.read();  
  }  
    
  // convert raw accel data to [deg] for pitch/roll  
  raw_value = ((raw_data[1] << 8) + raw_data[0]) >> 2;  
  accel_x = (float)(raw_value) * VEL_K;  
  accel_roll = -(float) raw_value * ACC_K;//(atan2(accel_z,accel_x)*RADTODEG-90.0);  
    
  raw_value = ((raw_data[3] << 8) + raw_data[2]) >> 2;  
  accel_y = (float)(raw_value) * VEL_K;  
  accel_pitch =-1.0*(float) raw_value * ACC_K; //(atan2(accel_z,accel_y)*RADTODEG-90.0);  
    
  // convert raw accel data to [ft/s^2] for z  
  raw_value = ((raw_data[5] << 8) + raw_data[4]) >> 2;  
  accel_z = (float)(raw_value) * VEL_K;  
 }  
   

The above code contains the proper library inclusion (Wire.h) for i2c interfacing, the proper i2c addresses and conversion constants for the various MultiWii board sensors, a function to initialize each of these sensors, the i2c initialization code needed within the Arduino setup() function, and a function to read the raw sensor values from the Accelerometer and Gyroscope and convert them to usable forms (angle, acceleration, angle rate, etc). 

Note: I eventually want to be able to read the Barometer data, so I found some excellent explanations and example code here (http://www.sparkfun.com/tutorials/253) as well as a more official source of barometric altimetry theory (which i used to great affect in 2.671). For now, we'll ignore the barometer data. 

In addition to reading sensor data, we are going to need to filter it. The Second Order Complementary Filter that i'm used to requires a PI controller in order for it to work.  Additionally, a PID controller will necessary for quadrotor self-balancing, so I opted to write a generic C++ PID class. For more info on PID Feedback Control, just Google it. Wikipedia offers an excellent writeup, as well as an excellent textbook document hosted by Caltech

Here's the generic PID Controller class I've written:


 class PIDController{  
   double kP;  
   double kI;  
   double kD;  
   double xError;  
   double xDesired;  
   double xErrorPrev;  
   double xErrorIntegral;  
   boolean feed;  
   public:  
   PIDController(double myKP, double myKI, double myKD, boolean isFeed){  
     kP = myKP;  
     kI = myKI;  
     kD = myKD;  
     xError = 0;  
     xDesired = 0;  
     xErrorPrev = 0;  
     xErrorIntegral = 0;  
     feed = isFeed;  
     }  
   
     void setDesired(double myXDesired){  
     xDesired = myXDesired;  
     }  
   
   double update(double x,double dx){  
     double command = 0.0;  
     xError = (xDesired - x);  
     xErrorIntegral+=xError;  
     if(feed)  
      command = kP*xError + kI*xErrorIntegral +kD*dx;  
     else  
      command = kP*xError + kI*xErrorIntegral +kD*(xError-xErrorPrev);  
     xErrorPrev = xError;  
     return command;  
   }  
 };  

When the class is instantiated, it requires the three PID constants and an option of whether the rate (derivative) is fed to the controller at each update, or if it should estimate it from past timesteps. This last option is useful if you have separate feedback for rate and position (like on this quad, which has both a filtered andle and a dedicated gyro for feedback). Before the controller is used, however, the set point for the control variable must be set via the setDesired() function. The update() function takes the current sensor feedback, as well as the (optional) derivative feedback, and spits out an actuator command. 

This controller can command the motors of my quadrotor to counteract its roll or pitch angle deviating from the angle I want it at (which is usually 0), maintain a constant altitude with barometer feedback, or act as the backbone for the Second Order Complementary Filter in order to get proper pitch and roll angle feedback by combining both the Accelerometer and Gyroscope data, as I did in 2.12. 

Here is the entire filter implemented in Arduino:


 #define FREQ 200.0  
   
 double dt = 1.0/FREQ;  
 long loopStart;  
 double pitch = 0;  
 double roll = 0;  
 double initialPitch = 0;  
 double initialRoll = 0;  
 float wn = .045;  
 float filterKP = wn*2.0;  
 float filterKI = wn*wn*dt;  
   
 PIDController rollFilter(filterKP,filterKI,0, 0);  
 PIDController pitchFilter(filterKP,filterKI,0, 0);  
   
 void IMUFilterUpdate(){  
  pitchFilter.setDesired(accel_pitch);  
  pitch = pitch + (gyro_pitch + pitchFilter.update(pitch,0))*dt;  
    
  rollFilter.setDesired(accel_roll);  
  roll = roll + (gyro_roll + rollFilter.update(roll,0))*dt;  
 }   

Sweet! Now in my main loop I can call IMUFilterUpdate() immediately after updating the raw sensor values to get actual pitch and roll, provided I ensure the main loop runs at a frequency of 200 Hz. This can be done with a simple if statement that checks if the time elapsed since the previous run of the loop is greater than or equal to the period (dt = 1/Frequency = 1/(200 Hz) = 5ms). 

An excellent forum post about the Second Order Complementary Filter (SOCF?) can be found here (post #1286), and a writeup pdf by the author here. Also, is Shane Colton's incredibly useful whitepaper on the First-Order Complementary Filter (Not quite as robust, but super intuitive, easy to understand, and easy to implement). 




The Second Order Complementary Filter (block diagram pictured above) works by first integrating the gyroscope data over time ( or multiplying it by 1/s in Laplace space), resulting in an estimated angle. To do that, the the old angle estimate is added to the new change in angle: 

angleNew = angleOld + (dThetadt * dt);

where dThetadt is the change in angle read directly from the gyroscope, in [degrees/s], and dt = 0.005 seconds. 

However, the gyroscope drifts! Over a short amount of time, the angle estimate will have accumulated error, leading to an incorrect angle estimate (and causing the quadrotor to possibly become unstable). For short-term situations, especially when the helicopter is moving, the gyro is pretty accurate. 

So this is corrected by taking advantage of the fact that the direction of gravitational acceleration can be found from the  3-axis accelerometer, regardless of the orientation, by taking the arctangent of the horizontal and vertical values of the accelerometer (Or linearizing the angle only using the horizontal component to save computation time, as I did above. The full atan version is commented out). This value is only useful if the quadrotor is not moving, however, and can be waaaay off in situations where the vehicle is accelerating on its own accord. 

So, we want the integrated gyro data when the vehicle is in motion, and the accelerometer angle estimate when the vehicle is static. 

We can use both simultaneously, by providing the angleFilter PI controller the estimated angle as feedback, and the current Accelerometer estimated angle as the set point. The difference will generate a rate command to overcome the gyroscope drift, resulting in a more accurate angle that is robust against both gyro drift AND vehicle accelerations. 

The values for the proportional and integrator constants, Kp and Ki respectively, can be determined if we model the PI controller as a second order system. The transfer function for the PI controller turns out in a fashion such that 
Kp = 2 * wn * zeta;
and
Ki = wn*wn;

where wn is the natural frequency of the system and zeta is the damping ratio. Because we are designing this system (It isn't a physical system with its own uneditable system dynamics), we can set the damping ratio to be a perfect 1 (effectively eliminating the need for  a zeta term) and the natural frequency wn to be the frequency at which we trust the gyroscope (or, alternatively, how often we correct the gyroscope with the Accelerometer estimate). I chose a natural frequency of 0.55Hz (period 1.81 seconds), though this may depend on your sensors, loop frequency, etc. I multiply the integral term by dt in order to take into account that our system is in discrete (digital) time. 

UPDATE on wn: After testing some with this configuration and seeing delay between copter motion and , I've decided to trust the gyroscope even more and make wn 0.045, about an order of magnitude less. The resulting behavior leads to much faster response to copter motion. 

A good test to see if your natural frequency is correct is to run the code and wait a while and see if the angle estimate drifts. Then, shake (accelerate) your vehicle to try and induce spikes in the angle estimate due to the accelerometer picking up the unwanted noise. If the angle remains stable for both tests, it works! :D 

angleFilter.setDesired( AccelerometerEstimate );
angleNew = angleOld + (dThetadt + angleFilter.update(angleOld,0)) * dt;

The code all together, which prints out the filtered roll and pitch values of a multiWii328 board:


 /*  
 Quadrotor Sensor and Filter Firmware  
 Daniel J. Gonzalez  
 dgonz@mit.edu  
 January 2013  
  */  
   
 #include <Wire.h>  
 #include <math.h>  
   
 // I2C addresses for each sensor:  
 #define GYR_ADR 104  
 #define ACC_ADR 64  
 #define BMP085_ADDRESS 119  
 #define MAG_ADR 30  
   
 // sensor scaling  
 #define GYR_K 0.06956           // [(deg/s)/LSB]  
 #define ACC_K 0.01399           // [deg/LSB]  
 #define VEL_K 0.002395          // [(ft/s^2)/LSB]  
 #define ACC_Z_ZERO 4096          // [LSB]  
 #define RADTODEG 57.2957795  
   
 float gyro_pitch, gyro_roll, gyro_yaw;       // [deg/s]  
 float accel_pitch, accel_roll;           // [deg]  
 float accel_x;                   // [ft/s^2]  
 float accel_y;                   // [ft/s^2]  
 float accel_z;                   // [ft/s^2]  
 float rate_z;                   // [ft/s]  
   
   
 #define FREQ 200.0  
   
 double dt = 1.0/FREQ;  
 long loopStart;  
 double pitch = 0;  
 double roll = 0;  
 double initialPitch = 0;  
 double initialRoll = 0;  
 float wn = .55;  
 float filterKP = wn*2.0;  
 float filterKI = wn*wn*dt;  
   
 class PIDController{  
   double kP;  
   double kI;  
   double kD;  
   double xError;  
   double xDesired;  
   double xErrorPrev;  
   double xErrorIntegral;  
   boolean feed;  
   public:  
   PIDController(double myKP, double myKI, double myKD, boolean isFeed){  
     kP = myKP;  
     kI = myKI;  
     kD = myKD;  
     xError = 0;  
     xDesired = 0;  
     xErrorPrev = 0;  
     xErrorIntegral = 0;  
     feed = isFeed;  
     }  
   
     void setDesired(double myXDesired){  
     xDesired = myXDesired;  
     }  
   
   double update(double x,double dx){  
     double command = 0.0;  
     xError = (xDesired - x);  
     xErrorIntegral+=xError;  
     if(feed)  
      command = kP*xError + kI*xErrorIntegral +kD*dx;  
     else  
      command = kP*xError + kI*xErrorIntegral +kD*(xError-xErrorPrev);  
     xErrorPrev = xError;  
     return command;  
   }  
 };  
   
 PIDController rollFilter(filterKP,filterKI,0, 0);  
 PIDController pitchFilter(filterKP,filterKI,0, 0);  
   
 void setup(){  
  Serial.begin(57600);               // init UART  
  Wire.begin();                  // init I2C  
  TWBR = ((F_CPU / 400000) - 16) / 2;       // fast I2C   
  initSensors();  
  loopStart = millis();  
 }  
   
 void loop(){  
  if(millis()-loopStart>=1000.0/FREQ){  
   loopStart = millis();  
   readSensors();  
   IMUFilterUpdate();  
   Serial.print(pitch-initialPitch);  
   Serial.print(" | ");  
   Serial.println(roll-initialRoll);  
  }  
 }  
   
 void initSensors(){  
  // initialize the ITG-3205 gyro  
  // range: 2000deg/s, bandwidth: 188Hz  
  Wire.beginTransmission(GYR_ADR);  
  Wire.write(0x16);            
  Wire.write(0x19);            
  Wire.endTransmission();  
    
  // initialize the BMA180 accelerometer  
  // range: 2g, bandwidth: 150Hz (all defaults)  
  loopStart = millis();  
  for (int i;i<1000;i++){  
   while(millis()-loopStart < 1000.0/FREQ);  
   loopStart = millis();  
   readSensors();  
   IMUFilterUpdate();  
  }  
  initialPitch = pitch;  
  initialRoll = roll;  
 }  
   
 void readSensors(){  
  unsigned char i;  
  unsigned char raw_data[6];  
  signed int raw_value;  
    
  // read the ITG-3205 gyro  
  Wire.beginTransmission(GYR_ADR);  
  Wire.write(0x1D);                    
  Wire.endTransmission();  
    
  i = 0;  
  Wire.requestFrom(GYR_ADR, 6);  
  while(Wire.available())  
  {  
   raw_data[i++] = Wire.read();  
  }  
    
  // convert raw gyro data to [deg/s]  
  raw_value = (raw_data[0] << 8) + raw_data[1];  
  gyro_pitch = -1.0*(float) raw_value * GYR_K;  
  raw_value = (raw_data[2] << 8) + raw_data[3];  
  gyro_roll = (float) raw_value * GYR_K;  
  raw_value = (raw_data[4] << 8) + raw_data[5];  
  gyro_yaw = (float) raw_value * GYR_K;  
    
  // remind the gyro to stay in 2000deg/s mode???  
  // range: 2000deg/s, bandwidth: 188Hz  
  Wire.beginTransmission(GYR_ADR);  
  Wire.write(0x16);            
  Wire.write(0x19);            
  Wire.endTransmission();  
    
  // read the BMA180 accelerometer  
  Wire.beginTransmission(ACC_ADR);  
  Wire.write(0x02);                    
  Wire.endTransmission();  
    
  i = 0;  
  Wire.requestFrom(ACC_ADR, 6);  
  while(Wire.available())  
  {  
   raw_data[i++] = Wire.read();  
  }  
    
  // convert raw accel data to [deg] for pitch/roll  
  raw_value = ((raw_data[1] << 8) + raw_data[0]) >> 2;  
  accel_x = (float)(raw_value) * VEL_K;  
  accel_roll = -(float) raw_value * ACC_K;//(atan2(accel_z,accel_x)*RADTODEG-90.0);  
    
  raw_value = ((raw_data[3] << 8) + raw_data[2]) >> 2;  
  accel_y = (float)(raw_value) * VEL_K;  
  accel_pitch =-1.0*(float) raw_value * ACC_K; //(atan2(accel_z,accel_y)*RADTODEG-90.0);  
    
  // convert raw accel data to [ft/s^2] for z  
  raw_value = ((raw_data[5] << 8) + raw_data[4]) >> 2;  
  accel_z = (float)(raw_value) * VEL_K;  
 }  
   
 void IMUFilterUpdate(){  
  pitchFilter.setDesired(accel_pitch);  
  pitch = pitch + (gyro_pitch + pitchFilter.update(pitch,0))*dt;  
    
  rollFilter.setDesired(accel_roll);  
  roll = roll + (gyro_roll + rollFilter.update(roll,0))*dt;  
 }   

See you next post! 
break;