Saturday, June 7, 2014

Long time no update.  Progress on the project is on hold until I find a job.  Yeah gotta pay bills and stuff before playing.  I have still been researching ways to proceed though.  So I am asking for a couple more weeks before I update you again.  Once again let me reiterate that I am by no means abandoning this project.  It was still in its infancy when I put it on pause for real life stuff.
What changes to expect first:
New proposal
New initial circuit layout
New device sketches
etc.
Starting a new blog and will post the link here when I get it going.

Friday, May 2, 2014

So its been a week since I last updated this. So just to update anyone who does read this, I am in the middle of finals at the university so the updates will be slow for a couple of weeks.  Meanwhile, I got my ATtiny chips in and we are in the process of designing the circuit.  I think we have agreed on ordering a gyro sensor and a few more chips to make this a true addon system.  The idea is to take in the sensor data from the gyro to control the tilt of the distance sensors to keep them horizontal.  We were doing this by pulling that data and controlling the servos like they were camera gimbal servos.  But this requires a flight controller that has that ability to do.  Moving that to the embedded circuit will make things much better.  I am also toying with an idea to incorporate a different type of design to include a full frame.  Something that would allow someone to either take the addon system to their quad or they could build a new quad using the "smart" frame.  Just an idea.  I want to also look at a way to power the wall avoidance separately.  More information on this later, first I need a working prototype of the system to measure power requirements to see if it can be done effectively.
Embedded chips usage.  We hope to control 2 to 4 sensors on each ATtiny and output pins as high determined on the event triggered.  basically what I am thinking is assigning a 4 digit binary number to events.  so say the front sensor triggers at 2 feet then the four pins would be written as 0010, if it was 4 feet then they would be 0001.  Likewise the rear sensor would give 1110 for 2 and 1101 for 4.  I could add in the right left sensors to be 1010 right 2ft mark, 0110 left 2 ft mark.  The ATtiny chips I am using for this have 12 I/O pins.  If I rewire the ultrasonic sensors to have one shared pin for trigger and echo then I would need one pin per sensor, then 4 pins for output.  Thats only 8 pins used for pitch axis and roll axis.  That leaves me 4 pins available for maybe IR sensors in each direction for better detection of obstacles like sound absorbing humans.
Well back to studying for my finals.  Wish me luck

Friday, April 25, 2014

So I have received a confirmation email of my Atmel chips being shipped. I should get them sometime next week.  In the meantime I have downloaded the AtmelStudio app that is free from Atmels site.  I think I am going to use this to break the code down for the embedded chips.  It is based on Visual Studio which I have used in C++ classes before so its not much of a jump.  It also has an Arduino plugin extension that I may start using to do some further testing with the arduino mega before we fully go to the ATtiny chips.  Jimmy is already in the process of laying out the new board for the embedded chips.  I am also researching the best way to load the chips.  It will be a little bit of a pain to have to manually connect each one to an AVR loader like Jimmy's DragonAVR or one of our UNOs.   If we include a switchable USB onboard AVR loader, maybe Ill just order a chip like the USBasp loaders and add that circuit to the board.  Im sure that there will be a use for it to change values like distances from the wall to react at a later date anyway.  
Well OSH park made some really nice PCBs for our IEEE robot team this year, I guess once we get a working model on the copper milled board we can produce at the school we will order a few of those boards too.
Code update.   The one I posted for demonstration apparently hid a few items when I used the HTML to do the pretty print syntax highlighting stuff on it (aka put it into the dark box and highlighted the codes different pieces).  So don't go selecting and pasting it into your stuff.  Oh the failures to compile will be beautiful.  Hate mail filter engaged.  Not like anyone comments on this blog anyway.  Well there are a few more changes I plan on making to the code before it goes totally embedded.  Ill try to share them with you.
Gyro's?  I think that adding a Gyro control from the embedded system board to drive the servos that are leveling the sensors has got to be done.  This is the only way I can truly make this an addon to any quad copter.  That is my end goal anyway.  No matter your flight controller setup, I want to be able to add this onto it and give your quad copter a small degree of wall avoidance.  Yeah if your silly enough to make it go full pitch forward into the wall, there isn't much anythin can do to stop you from crashing into the wall within a certain range.
Well you guys have fun.  Im out for the night, prob taking the weekend off also, its been a rather full week so far, 2 presentations, one innovative design competition 1st place win, etc. 

Thursday, April 24, 2014

The next steps. Well Since my semester is coming to a close and the Quad is working as intended I have decided to continue the process further. My next step is to start designing an embedded system to perform the functions that the Arduino Mega is currently doing. The reasons behind turning it into an embedded system is that I can break the different processes out onto multiple processors and still reduce the energy consumption while speeding the responsiveness of the system up. Too that end I have ordered a few ATTINY84-20PU chips to test with and requested some samples of some other AVR chips. My plan is to design a board with onboard gyro stabilization for the servos that keep the ultrasonic servos parallel to the ground, control the ultrasonic sensors with dedicated AVR processors, read in the user control on another AVR, then do the overide signals on another AVR before sending the commands to the flight controller. What will this accomplish? Well right now there is a roughly 1/4 second delay between the user sending commands to the quad copter and the quad copter responding. The reason for this is the ultrasonic readings. Having a dedicated processor for the ultrasonic sensors removes that delay. Also, I want to use the Arduino for what it is intended for, early stages of prototyping. I feel it is time to move away from the Arduino and begin fine tuning the process. It will also allow me to use more sensors to cover more area around the quad. I think once I have tested the viability of breaking everything down into different AVRs I will add 4 more sensors to the quad basically at 45 degrees off the existing. The problem there will be the EMI and noise from the motors because the ultrasonic sensors will have to be mounted very near them. I am still massaging the code daily and will post any improvements here until I move to the embedded stage of the process. At that point I feel it is only suitable to either start a new blog or create some sort of webpage to show off my progress. As always, thanks for reading my thoughts here.

Wednesday, April 23, 2014

Adding the full code for demonstration purposes


#include 
#include 

/*
*Version 1.10b
** DISCLAIMER!!!!  This is a work in progress.  If you decide that adding this
** to you own quad copter, please do so at your own risk.  I am not in any way shape
** or form responsible for your silliness.
** While this is listed as a beta, its really between alpha and beta realities, meaning
** If you look hard at it its alpha, if you look away its beta.  Also remember:
** ---Alpha. Software undergoes alpha testing as a first step in getting user feedback. Alpha is Latin for "doesn't work."
** ---Beta. Software undergoes beta testing shortly before it's released. Beta is Latin for "still doesn't work.
** Copyrights???  This is open source man.  Don't make money off of it at my expense
** enjoy.
** This code is intended to be used in conjunction with an Arduino Mega2560 to interpret
** ultrasonic readings and limit or override user RC control on a quadcopter.
** It is flexible in that it doesnt matter what kind of flight controller you are
** using.  Tested with APM 2.5, APM 2.6, MultiWii 328p and Multiwii MegaPirate.
** If it uses a standard RF controller it will use the same type of control signal
** and this only mimics user control when seen from the flight controllers point of view.
** As stated on my blog, Understand that this is heavily influenced by
** Duane's 8 channel RCArduino work at
** http://rcarduino.blogspot.co.uk/2012/04/how-to-read-multiple-rc-channels-draft.html
*/

#define PITCH_CHANNEL_IN 2  // attach from receiver elevator channel for pitch control (Mega2560 interrupt 0)
#define ROLL_CHANNEL_IN 3   // attach from receiver ailerons channel for roll control (Mega interrupt 1)

#define PITCH_CHANNEL_OUT 12  // attach to flight controller elevator pin
#define ROLL_CHANNEL_OUT 13  // attach to flight controller aileron pin

Servo servoChannel1;  // setup servo channel for pwm output
Servo servoChannel2;  // setup servo channel for pwm output

#define PITCH_FLAG 1  // flag to determine if new input is detected on pitch channel
#define ROLL_FLAG 2   // flag to determine if new input is detected on roll channel

volatile uint32_t SharedNewFlag;  // global shared flag for determining if any flag has been triggered

volatile uint32_t PITCH_IN_SHARED;  // global shared flag for pitch input trigger
volatile uint32_t ROLL_IN_SHARED;   // global shared flag for roll input trigger

#define SONAR_NUM       4 // Number of sonar sensors
#define MAX_DISTANCE  179 // Max distance in cm
#define PING_INTERVAL 33 // Milliseconds between sensor pings (29ms is about the min to avoid cross-sensor echo).

unsigned long pingTimer[SONAR_NUM];
unsigned int cm[SONAR_NUM];
uint8_t currentSensor = 0;

NewPing sonar[SONAR_NUM] = { // NewPing v1.5 library and examples used here.
 // Visit https://code.google.com/p/arduino-new-ping/ for more info
 NewPing(4, 5, MAX_DISTANCE),
 NewPing(6, 7, MAX_DISTANCE),
 NewPing(8, 9, MAX_DISTANCE),
 NewPing(10, 11, MAX_DISTANCE)
};

void setup()
{ Serial.begin(115200);
  servoChannel1.attach(PITCH_CHANNEL_OUT);// attach the servo channel 1 to pitch out pin
 servoChannel2.attach(ROLL_CHANNEL_OUT); // attach the servo channel 2 to roll out pin

 pingTimer[0] = millis() + 75;           // First ping starts at 75ms
 for (uint8_t i = 1; i < SONAR_NUM; i++) // Set the starting time for each sensor.
 pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;

 attachInterrupt(0, calcPitch, CHANGE); // attach interrupt to the in pins for pass through
 attachInterrupt(1, calcRoll, CHANGE);  // attach interrupt to the in pins for pass through
void loop()
 {
  // static local copies to be used as output and calculation
  static uint32_t PITCH_IN_LOCAL;
  static uint32_t ROLL_IN_LOCAL;
  // local update flag
  static uint32_t LocalNewFlag;
  // check if global flag has been triggered then turn off interrupts until output has been completed
  if (SharedNewFlag)
  {
   noInterrupts(); // turn off hardware interrupts
   LocalNewFlag = SharedNewFlag; // copy the shared flag status into the local flag to allow the shared one to be reset for the next input
   if (LocalNewFlag & PITCH_FLAG)
   // if the local flag is true (greater than 0) and the pitch flag is true update the local copy of the user input
   {
    PITCH_IN_LOCAL = PITCH_IN_SHARED; // set local copy of input to the global shared value
   }

   if (LocalNewFlag & ROLL_FLAG) // same as before local flag and roll flag must be true
   {
    ROLL_IN_LOCAL = ROLL_IN_SHARED;  // set local copy of input to the global shared value
   }

   SharedNewFlag = 0;  // reset global update flag to zero for next loop
   interrupts();
  }

  for (uint8_t i = 0; i < SONAR_NUM; i++)
  { // Loop through all the sensors.
   if (millis() >= pingTimer[i])
   {       // Is it this sensor's time to ping?
    pingTimer[i] += PING_INTERVAL * SONAR_NUM;  // Set next time this sensor will be pinged.
    sonar[currentSensor].timer_stop();          // Make sure previous timer is cancelled.
    currentSensor = i;                          // Sensor being accessed.
    cm[currentSensor] = MAX_DISTANCE;           // Make distance MAX_DISTANCE if there's no echo.
    sonar[currentSensor].ping_timer(echoCheck); // Do the ping (interrupt will call echoCheck).
   }
  }
  int distancef = map(cm[0], 0, 180, 1, 4);
  int distanceb = map(cm[1], 0, 180, 1, 4);
  int distancer = map(cm[2], 0, 180, 1, 4);
  int distancel = map(cm[3], 0, 180, 1, 4);
  
  if (distancef <= distanceb) // checks to see which distance is closer that involves this channel
  {
   switch (distancef)
   {
   case 1:
    if (LocalNewFlag & PITCH_FLAG)
    {
     if (PITCH_IN_LOCAL > 1700)
     {
      servoChannel1.writeMicroseconds(PITCH_IN_LOCAL);
     }
    }
    else
    {
     servoChannel1.writeMicroseconds(1700);
    }
    break;
   case 2:
    if (LocalNewFlag & PITCH_FLAG)
    {
     if (PITCH_IN_LOCAL > 1500)
     {
      servoChannel1.writeMicroseconds(PITCH_IN_LOCAL); //  no user input over 1500
     }
    }
    else
    {
     servoChannel1.writeMicroseconds(1500); 
    }    
    break;
   default:
    if (LocalNewFlag & PITCH_FLAG)
    {
     servoChannel1.writeMicroseconds(PITCH_IN_LOCAL);
    }
   }
  }
  else
  {
   switch (distanceb)
   {
   case 1:
    if (LocalNewFlag & PITCH_FLAG)
    {
     if(PITCH_IN_LOCAL < 1300)
     {
      servoChannel1.writeMicroseconds(PITCH_IN_LOCAL); // back away from the wall
     }
    }
    else
    {
     servoChannel1.writeMicroseconds(1300);
    }
    break;
   case 2:
    if (LocalNewFlag & PITCH_FLAG)
    {
     if(PITCH_IN_LOCAL < 1500)
     {
      servoChannel1.writeMicroseconds(PITCH_IN_LOCAL); //  no user input over 1500
     }
    }
    else
    {
     servoChannel1.writeMicroseconds(1500);
    }
    break;
   default: 
    if (LocalNewFlag & PITCH_FLAG)
    {
     servoChannel1.writeMicroseconds(PITCH_IN_LOCAL);
    }
   }
  }
  if(distancer <= distancel)
  {
   switch (distancer)
   {
   case 1:
    if(LocalNewFlag & ROLL_FLAG)
    {
     if(ROLL_IN_LOCAL < 1300)
     {
      servoChannel2.writeMicroseconds(ROLL_IN_LOCAL); // back away from the wall
     }
    }
    else
    {
     servoChannel2.writeMicroseconds(1300);
    }
    break;
   case 2:
    if(LocalNewFlag & ROLL_FLAG)
    {
     if(ROLL_IN_LOCAL < 1500)
     {
      servoChannel2.writeMicroseconds(ROLL_IN_LOCAL);
     }
    }
    else
    {
     servoChannel2.writeMicroseconds(1500);
    }
    break;
   default:
    if(LocalNewFlag & ROLL_FLAG)
    {
     servoChannel2.writeMicroseconds(ROLL_IN_LOCAL);
    }
   }
  }
  else
  {
   switch (distancel)
   {
   case 1:
    if(LocalNewFlag & ROLL_FLAG)
    {
     if(ROLL_IN_LOCAL > 1700)
     {
      servoChannel2.writeMicroseconds(ROLL_IN_LOCAL); // back away from the wall
     }
    }
    else
    {
     servoChannel2.writeMicroseconds(1700);
    }
    break;
   case 2:
    if(LocalNewFlag & ROLL_FLAG)
    {
     if(ROLL_IN_LOCAL > 1500)
     {
      servoChannel2.writeMicroseconds(ROLL_IN_LOCAL);
     }
    }
    else
    {
     servoChannel2.writeMicroseconds(1500);
    }
    break;
   default:
    if(LocalNewFlag & ROLL_FLAG)
    {
     servoChannel2.writeMicroseconds(ROLL_IN_LOCAL);
    }
   }
  }

  LocalNewFlag = 0; // reset local flag to zero for next update
 }
 // calculate the PWM pulse lengths and flip flags
void calcPitch()
 {
  static uint32_t ulStart;

  if(digitalRead(PITCH_CHANNEL_IN))
  {
   ulStart = micros();
  }
  else
  {
   PITCH_IN_SHARED = (uint32_t)(micros() - ulStart);
   SharedNewFlag |= PITCH_FLAG;
  }
 }
void calcRoll()
 {
  static uint32_t ulStart;

  if(digitalRead(ROLL_CHANNEL_IN))
  {
   ulStart = micros();
  }
  else
  {
   ROLL_IN_SHARED = (uint32_t)(micros() - ulStart);
   SharedNewFlag |= ROLL_FLAG;
  }
 }
void echoCheck() { // If ping received, set the sensor distance to array.
  if (sonar[currentSensor].check_timer())
  cm[currentSensor] = sonar[currentSensor].ping_result / US_ROUNDTRIP_CM;
 }

Monday, April 21, 2014

Testing is going well.  I am posting the current code which is purely wall avoidance.  I am thinking about extending the range of ultrasonic detection to 4 ft and adding an event at that range for non movement towards the wall.  For now this is what we are going with for our progress review presentation today.

first the current version
one event, triggered at the two ft range.
Wall avoidance one event 2 ft pull away

The next untested version adding another event back in
two events, triggered at two ft and four ft ranges
Wall avoidance two events

Reducing the number of events speeds up the responsiveness, so we are attempting a happy medium with the two events version.  something that will let you get close to the wall.

Wednesday, April 16, 2014

So, some loose solder points and an inexplicable loss of settings on the flight controller caused me to question my code.  Result, a partial redesign of the logic which after testing actually increased the responsiveness by quite a factor.  After going through the code trying to flush out what may have been causing the quad to freak out.  I was told by several people that it must be my code messing up.  Well guess what, it wasn't my code.  BUT WAIT, I did go through redesigning of the code and it ... improved it?
OK long story short.  With the help of a classmate I realized that what I was attempting to do with the different levels of user limitations was not needed as much as I first thought.  If someone is trying to run the thing into the wall there isn't much that can be done to stop it.  So assuming the user isn't a complete moron and they are not trying to hit the wall, I removed the various levels of limitations and went for one trigger event.  Roughly 2 ft away from the wall if you are not trying to pull away, the Arduino Mega will do it for you.  This reduced the code down to around 100 ish lines of code (180 with all the comments).
What does this do?
First the ultrasonic sensors can be sped up.  The max range they will look for was reduced so less time waiting in a timed interrupt cycle.  This allowed me to reduce the time between each sensor firing by half.  The ultrasonic sensors are advertised to accurately range up to somewhere around 20 ft.  Under testing, at best I was getting some accurate readings at 12 ft and about 75% accuracy at 10 ft and beyond.  The best ranges were less than 6 ft which were about 95% or better accuracy.  So I am disregarding anything less than 70 cm (roughly 2 1/4 ft).  I will probably increase this range a bit.  Maybe disregard over 3.5 ft and adjust under 3 ft.  So the loop spends about 20% of the time it used to spend in timed ISRs but I doubled the amount that they do.  Still that's only 40% of the time it was in them before.   I could reduce the number of pings back to what we had before to speed the main loop up further if needed.  I don't think its needed though.
Also I have drastically reduced those nasty nested if else statements.  Since we only have the one trigger event now I only need to check the sensors and do something other than pass through if the event is triggered.

What does this mean.  The quad is very quick.  There is no observable response lag.

I did bring my laptop home which has the only copy of the most current code, but its late and I will try to upload it tomorrow for anyone to look at.

Future:  Possible additions to the project for after I present it to the school are in the research phase.
Breaking the code down into different chucks (e.g., ultrasonic reads and trigger,  RF PWM read in, on-board gyros for sensor leveling independent of the flight controller)  This will allow me to load the different chunks onto individual PIC chips and create a much lighter and faster embedded system.  (my goal is to make an attachment for multiple configurations of multi-rotor flight vehicles).
Additional sensors to provide a better 360 degree coverage for wall avoidance.
These possible future changes will be added to the blog if/when I do them.  but for now if anyone knows a good PIC chip I should be looking into let me know.

Well goodnight whoever may be wasting your time following my project updates.