-
1Dissect Avitron Bionic Bird
Using a precision knife, cut along the seam of the styrofoam body. Be careful to avoid cutting the wires on the electronics and motors. Once the cut has been made all the way around, the body will separate into two halves, one of which has the microcontroller, battery, and motors and one that is only styrofoam.
-
2Replace Microcontroller
The next step is removing the blue microcontroller board from the system. Use a soldering iron to remove the motors and battery from the original microcontroller pins. Next, solder or use a breadboard to connect the motors to an external microcontroller such as the Adafruit Trinket. The motors should be connected to a PNP transistor to receive enough power, then to a digital output pin and ground. Next, the battery can be attached with the Adafruit battery backpack for the Pro Trinket.
-
3Attach IMU
Solder an IMU to the microcontroller. If using the 9-Axis IMU from TinyShield, the VCC, SDA, SCL, and GND pins must be all connected to the microcontroller.
-
4Attach Monitor
void setup() { Serial.begin(19200); Serial1.begin(19200); } void loop() { // read from port 1, send to port 0: if (Serial1.available()) { Serial.println(Serial1.readString()); delay(2000); } }
Using either an external board with Serial Console capability or an LCD screen, connect the Trinket's TX port to the RX port on the external microcontroller/screen. Then, use SerialMonitor.print() to display values using the sample code from the IMU.
-
5Control Motors
Using the IMU values, make proportional adjustments to motor speed. The right values and constants of proportionality can be found through trial and error.
#include <Servo.h> #include <Wire.h> #include "RTIMUSettings.h" #include "RTIMU.h" #include "RTFusionRTQF.h" #ifndef ARDUINO_ARCH_SAMD #include <EEPROM.h> #endif RTIMU *imu; // the IMU object RTFusionRTQF fusion; // the fusion object RTIMUSettings settings; // the settings object // DISPLAY_INTERVAL sets the rate at which results are displayed #define DISPLAY_INTERVAL 300 // interval between pose displays // SERIAL_PORT_SPEED defines the speed to use for the debug serial port #define SERIAL_PORT_SPEED 19200 #ifdef SERIAL_PORT_MONITOR #define SerialMonitor SERIAL_PORT_MONITOR #else #define SerialMonitor Serial #endif unsigned long lastDisplay; unsigned long lastRate; int sampleCount; Servo wingServo; int wing = A2; double curr_x = 0; double prev_x = 0; double curr_y = 0; double prev_y = 0; int vertical = 255; int stop_val = 90; int horizontal = 110; void setup() { wingServo.attach(wing); int errcode; SerialMonitor.begin(SERIAL_PORT_SPEED); while(!SerialMonitor); Wire.begin(); imu = RTIMU::createIMU(&settings); // create the imu object SerialMonitor.print("ArduinoIMU starting using device "); SerialMonitor.println(imu->IMUName()); delay(2000); if ((errcode = imu->IMUInit()) < 0) { SerialMonitor.print("Failed to init IMU: "); SerialMonitor.println(errcode); } if (imu->getCalibrationValid()){ SerialMonitor.println("Using compass calibration"); delay(2000); } else{ ; } lastDisplay = lastRate = millis(); sampleCount = 0; // Slerp power controls the fusion and can be between 0 and 1 // 0 means that only gyros are used, 1 means that only accels/compass are used // In-between gives the fusion mix. fusion.setSlerpPower(0.02); // use of sensors in the fusion algorithm can be controlled here // change any of these to false to disable that sensor fusion.setGyroEnable(true); fusion.setAccelEnable(true); fusion.setCompassEnable(true); } void loop() { unsigned long now = millis(); unsigned long delta; analogWrite(wing,255); delay(2000); if (imu->IMURead()) { // get the latest data if ready yet fusion.newIMUData(imu->getGyro(), imu->getAccel(), imu->getCompass(), imu->getTimestamp()); if ((now - lastDisplay) >= DISPLAY_INTERVAL) { lastDisplay = now; RTVector3 accelData=imu->getAccel(); RTVector3 gyroData=imu->getGyro(); RTVector3 compassData=imu->getCompass(); RTVector3 fusionData=fusion.getFusionPose(); displayAxis("Accelerometer:", accelData.x(), accelData.y(), accelData.z()); // accel data curr_x = accelData.x(); curr_y = accelData.y(); if ((prev_x - curr_x) > 0) { //(imu traveling up) //flap wings slower (to go down) if (vertical - 5 > 190) { vertical = vertical - 5; } analogWrite(wing, vertical); delay(200); } else { //(imu traveling down) //flap wings faster (to go up) if (vertical + 5 < 255) { vertical = vertical + 5; } analogWrite(wing, vertical); delay(200); } // INCOMPLETE // if ((prev_y - curr_y) > 0) { //(imu is traveling left) // //turn tail right // horizontal = stop_val + 20; // analogWrite(tail, horizontal); // delay(200); // } // else { //(imu is traveling right) // //turn tail // horizontal = stop_val - 20; // analogWrite(tail, horizontal); // delay(200); // } prev_x = curr_x; prev_y = curr_y; // displayAxis("Gyro:", gyroData.x(), gyroData.y(), gyroData.z()); // gyro data // displayAxis("Mag:", compassData.x(), compassData.y(), compassData.z()); // compass data // displayDegrees("Pose:", fusionData.x(), fusionData.y(), fusionData.z()); // fused output } } } void displayAxis(const char *label, float x, float y, float z) { // must convert floats to String with 2 decimal points to // avoid overflowing the serial ports String xc = String(x,2); String yc = String(y,2); String zc = String(z,2); SerialMonitor.print(label); SerialMonitor.print(" x:"); SerialMonitor.print(xc); SerialMonitor.print(" y:"); SerialMonitor.print(yc); SerialMonitor.print(" z:"); SerialMonitor.print(zc); delay(2000); } void displayDegrees(const char *label, float x, float y, float z) { SerialMonitor.print(label); SerialMonitor.print(" x:"); SerialMonitor.print(x * RTMATH_RAD_TO_DEGREE); SerialMonitor.print(" y:"); SerialMonitor.print(y * RTMATH_RAD_TO_DEGREE); SerialMonitor.print(" z:"); SerialMonitor.print(z * RTMATH_RAD_TO_DEGREE); delay(2000); } void clearLCD(){ SerialMonitor.write(12); delay(5); } void delay_and_clearLCD(int duration){ delay(duration); clearLCD(); } void newLine() { SerialMonitor.write(10); }
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.