Thursday, January 20, 2011

ACS712 Low Current Sensor Breakout test.

Despite finding a solution to solve the problem of knowing when the speed controllers are on or off and have abandoned the ACS712 Low Current Sensor Breakout for this purpose the sensor continues to be useful to monitor the battery status.
Figure 1
After some research and some help that I have got from this forum I could do a procedure to test the current sensor. First I have created the circuit shown in figure 1 above, which consists of two parts. One part that includes the Arduino board, the current sensor and a digital voltmeter (I used a multimeter). The other part includes the 9V battery, a resistance, an on/off toggle switch and a micro motor. The idea is to measure the current that flows from the 9V battery to the micro motor.
Before using the current sensor we must calibrate it. Looking at the sensor specifications I have noticed that the output from the sensor IC is Vcc/2 (~2.5 V, in this case) if there is no current. This is because the sensor IC has a bidirectional output. If the current is "forward", its output is between Vcc/2 and Vcc (~2.5 V and ~5 V, in this case); if the current is "backward", its output is between Gnd and Vcc/2 (~0 V and ~2.5 V, in this case). So let's try to set the board's output to 2.5 V with no input current. According to the note on the schematic, the two trimpots need to be turned in different directions to increase the values they control. The current sensor uses a hall-effect sensor internally. Therefore, it should be used a diddle stick (a small non-metallic, non-conductive screwdriver like tool for making fine adjustments) to adjust the trimpot (I used a toothpick and cut it with a x-ato). With a diddle stick, we rotate the "Vref" trimpot until we get the value of ~2.5 V. If we verify that there are great fluctuations in readings, rotates the "Gain" trimpot until you get readings closer to each other. When we have a more or less stable value, then turns on the motor and immediately we can see an increase in the value of the voltage read by the voltmeter. Remind that the current sensor outputs an analog voltage that varies linearly with sensed current. To read the value of the current sensor in analog pin "0" from the arduino board, I have done a sketch that can be seen below. The values read from the analog pin "0” ranging from 0 to 1023.
/*
  Reads values from a ACS712 low current sensor connected to
  the analog input pin 0 on Arduino board and print them to
  the serial monitor.
*/

void setup() {
  Serial.begin(9600);         // sets the serial port to 9600
}

void loop() {
  int value = analogRead(0);  // read analog input pin 0
  Serial.println(value, DEC); // prints the value read
  delay(1000);                // wait 1s for next reading
}
To see the variation of values read from the analog pin "0” in real time I have created a graph using a pair of perl scripts. The first script is a flexible, command-line-oriented frontend to the program “Gnuplot”. It creates plots from data coming in on STDIN or given in a filename passed on the command line and can be found on this site. The second script - which can be seen below - allows us to read from the serial port the stream of values received in analog pin "0" of the Arduino board and sends them to the STDIN.
#!/usr/bin/perl -w

use strict;
use Device::SerialPort;

my $port = "/dev/ttyUSB0";

my $ob = Device::SerialPort->new($port);
$ob->databits(8);
$ob->baudrate(9600);
$ob->parity("none");
$ob->stopbits(1);

select STDOUT;

$| = 1;

my $n = 0;
my $buffer = "";
while(1) {
  my $byte = $ob->read(1);
  if($byte =~ /^\d/) {
    $buffer .= $byte;
  } elsif($byte =~ /^\n/) {
    if($buffer =~ /^\d+/) {
      print STDOUT "$buffer\n";
      $n++;
      sleep(1);
    }
    $buffer = "";
  }
}
undef $ob;
The connection between the two scripts is done with a pipeline (pipe character "|"). The command to start the plot is shown below.
$ ./ReadSerialPort.pl | ./feedGnuplot.pl --stream --lines --points \
--legend "Readings" --title "ACS712 Low Current Sensor Test" \
--ymin 450 --ymax 600 --xlen 100 --xlabel "Time (s)" --ylabel "Value"
In figure 2 we can see the plot in real time and can be observed that there is a sudden increase of the values readed from the sensor, which corresponds to the moment when the motor is turned on. Later, we can observe a decline of values, which correspond to the moment when the motor is turned off.
Figure 2
As can be seen in the graph of the figure 2, the readings still have a fluctuation although of having reduced the gain as much as possible. The graph displays the raw value from the analog to digital converter (ADC) in the Arduino. After reading some material on the Arduino forum about smoothing out the values, measuring voltage and converting the raw ADC value to something more useful, I decided to reduce the fluctuation of the readings using the average of 10 readings taken at an interval of 10 ms and then send the result to the Serial port. The new sketch can be seen below.
/*
  Computes the average of 10 readings at intervals of 10 ms
  from a ACS712 low current sensor connected to the analog
  input pin 0 on Arduino board and print them to the serial
  monitor.
*/

int counter = 0;
int total = 0;

void setup() {
  // sets the serial port to 9600
  Serial.begin(9600);
}

void loop() {
  // read analog input pin 0
  int reading = analogRead(0);
  // add the reading to the total
  total = total + reading;
  // incrementing the counter
  counter = counter + 1;
  // if have already been done 10 readings...
  if (counter >= 10) {
    // set the counter to 0
    counter = 0;
    // calculate the average
    int average = total / 10;
    // send it to the serial port (as ASCII digits)
    Serial.println(average, DEC);
    // set the total to 0
    total = 0;
  }
  // wait 1 ms for next reading
  delay(100);
}
As one can now see in graph of the figure 3, the moments when the motor is turned on and off is now clearer.
Figure 3
The sketch can be improved to reject the values that are not within the expected range making use of several statistical criteria. After some investigation I found a statistical method easy to implement to reject the "bad" data, known as the Chauvenet's criterion. The implementation of the method can be seen in the following sketch.
/*
  Computes the average of a sample of N readings at intervals
  of 1000/N ms from a ACS712 low current sensor connected to
  the analog input pin 0 on Arduino board and print them to the
  serial monitor.
  This sketch uses the the Chauvenet's statistical criterion
  for rejecting the bad readings.
*/

const int num_readings = 20;
// Chauvenet’s criterion for rejecting a reading
// http://people.ohio.edu/bayless/seniorlab/lecture1.ppt
// http://people.ohio.edu/bayless/seniorlab/chauvenet.pdf
// Consult the table of the pdf document to find the ratio
// of maximum acceptable deviation to precision index for
// this sampling.
const int mad = 2.24;
int readings[num_readings];
int index = 0;

void setup() {
  // sets the serial port to 9600
  Serial.begin(9600);
}

int average() {
  int total = 0;
  for(int i=0; i<num_readings; i++)
    total = total + readings[i];
  return total/num_readings;
}

float standard_deviation(int avg) {
  int total = 0;
  for(int i=0; i<num_readings; i++)
    total = total + pow((avg - readings[i]), 2);
  return sqrt(total/num_readings);
}

void loop() {
  // read analog input pin 0
  int reading = analogRead(0);
  readings[index] = reading;
  // incrementing the index
  index = index + 1;
  // if have already been done 10 readings...
  if (index >= num_readings) {
    // set the index to 0
    index = 0;
    // compute the average
    int avg = average();
    // compute the standard deviation
    float std = standard_deviation(avg);
    
    float madstd = mad * std;
    float lowlimit = avg - madstd;
    float highlimit = avg + madstd;
    
    int count = 0;
    int total = 0;
    for(int i=0; i<num_readings; i++) {
      // Check if the values of the readings are within the limits.
      if(readings[i] >= lowlimit && readings[i] <= highlimit) {
        total = total + readings[i];
        count = count + 1;
      }
    }
    // compute the new average
    int newaverage = total/count;
    // send it to the serial port (as ASCII digits)
    Serial.println(newaverage, DEC);
  }
  // wait 1000/num_readings ms for next reading
  delay(int(1000/num_readings));
}
The figure 4 shows the graph of the average variation of the readings after being analyzed with the Chauvenet's statistical criterion.
Figure 4
When comparing with the graph of figure 3, we can see that there is not much difference between the two graphs. However, these statistical methods are useful because many sensors are very sensitive to external influences that may cause momentary readings outside of the expected values.
Figure 5
In the figure 5 we can see the circuit in operation connected to my Gentoo laptop. I lost a lot of time with this sensor but finally, I think I understand how I can use it.

Tuesday, December 21, 2010

Sometimes the best solutions are the simplest ones.

To solve the problem of knowing when the speed controllers are initialized or not, I decided that the best solution to solve the problem would be to use a current sensor. So I bought a current sensor. But unfortunately this solution has proved difficult to implement since the sensor is not working as I expected. To test the sensor I connected it to a Arduino board. The first problem I encountered is that the sensor reads the same values, regardless of whether or not connected to a circuit. I also tried to calibrate the sensor by following the procedure that is on the product page but without success. The second problem has to do with the little information available about this sensor.
However, while I tried to solve the sensor problem I found a simple solution to the problem of whether the speed controllers were initialized or not using only one on-off switch push. There is a switch designated switch DPST (Double Pole, Single Throw), which consists of a pair of on-off switches which operate together but in different circuits. The quadcopter has two independent circuits, one connecting the motors and speed controllers to one battery and other circuit that connects the Seeeduino board to the other battery. To test the idea I had based on this example from the page of the Arduino board. I designed a scheme that can be seen in figure 1 and that lets me know when the green LED is turned on or off.
Figure 1
As can be seen in the scheme of the figure 1 there are two independent circuits. One include the 9V battery and the LED and the another is on the Arduino board. Both circuits are connected to the on-off switch push. The following sketch lets me know at any moment if the LED is turned on or off.
#define SWITCHPUSH 7  // the input pin where the
                      // on-off switch push is connected

int state = -1;   // state variable will be used to store
                  // the state of the input pin

void setup() {
  Serial.begin(9600);          // open the serial port
  pinMode(SWITCHPUSH, INPUT);  // tell Arduino SWITCHPUSH is an input
}

void loop() {
  int val = digitalRead(SWITCHPUSH);  // read input value
  
  // check if the read value is different of the state value
  if(val != state) {
    // check whether the input is HIGH (switch push pressed)
    if(val == HIGH) {
      // print the string "LED is ON" to the serial port
      Serial.println("LED is ON");
    } else {
      // print the string "LED is OFF" to the serial port
      Serial.println("LED is OFF");
    }
    state = val;  // store the value read in state variable
  }
  delay(100);  // wait 100 ms between each send
}
If I press the on-off switch push turns on the LED. From the Arduino board I get to know that the LED is turned on. If I turn off the Arduino board and re-connect it again I continue to know the status of the LED, because the on-off switch push operating in different circuits. This is very simple.
Figure 2
In Figure 2 we can see the two circuits with the various components in operation. The on-off switch push is in the image upside down so we can see the connections. By comparison we can see the LED as the speed controller. Sometimes the best solutions are the simplest ones.

Monday, November 29, 2010

The ACS712 Low Current Sensor Breakout has just arrived.

As I have mentioned before in a previous post, in order to make working the speed controllers it is necessary to initialize them first. This requires that previously the main board (Seeeduino Mega board) has been switched on, as it is the main board that makes the initialization. This procedure gives no problems when the sequence between the connections of the main board and the speed controllers is respected. However, if for any reason the main board have been previously switched on and after that it is switched off and then switched on again, the initialization happens twice even the speed controllers have already been initialized. This is because the status of the controllers it is not known and thus the motors can run up to the maximum speed. Therefore one needs a current sensor to detect whether the speed controllers are either switched on or off. I have  selected the sensor "ACS712 Low Current Sensor Breakout" which can be seen in the picture above. More information about this sensor can be found on page of its manufacturer. One of its advantage is that one can use it for example, to monitorize the power consumption of motors.

Friday, November 26, 2010

How to convert a string of Euler angles characters read by the 9DOF Razor IMU board into numeric values.

To guide the quadcopter in the space I will use the 9DOF Razor IMU board with the 9 Degree of Measurement Attitude and Heading Reference System (AHRS) code base. This board with AHRS code has the advantage of doing all the calculations and corrections for me. All I need to do is work out the interface. With the code one can chooses three options for receiving the data according to the constants that we define. If one sets the defined value "PRINT_DCM" to 1 then one will print the whole direction cosine matrix. If one sets the defined value "PRINT_ANALOG" to 1 then one will print the analog raw data. If one sets the defined value "PRINT_EULER" to 1 then one will print the Euler angles roll, pitch and yaw. These options are selected in the AHRS code before being compiled.
But first one needs to get the data in a format that can then be used by the Seeeduino Mega board to do the calculations to orient and stabilize the quadcopter. Reading the stream data from the 9DOF Razor IMU to the Seeeduino Mega board is a simple task and it has been shown in a previous post in which the data was transferred via a serial connection between the two boards - the diagram of the assembly can be seen in the image of the this previous post.
The data that I choose consist of the Euler angles roll, pitch and yaw and are sent by the 9DOF Razor IMU board sequentially in a text line. The line has the following format: "!ANG:0.00,0.00,0.00\r\n". To use the angles are necessary to split the line and then convert the numeric characters string parts to a numeric value (in this case a floating point number). After several failed attempts to read stream data sequentially I discovered that if I did not blocking the serial reads until the 9DOF Razor IMU board send the data often do not find the end of the text line which results in incorrect values and buffer overflow problems. To solve the problem of the serial reads may block permanently due to faulty connection with the 9DOF Razor IMU board was added a timeout. If at the end of the timeout has not been read the line the program continues its execution whiles keeping the previous angles values. This, procedure is important because if the Seeeduino Mega board lost the connection with 9DOF Razor IMU board the program can predicts code for example that here the motors are turned off and is activated a parachute, that save the quadcopter from crashing.
In the following sketch we can check the path followed to obtain the Euler angles in a numerical format from the 9DOF Razor IMU board.
#define TIMEOUT 100

// { roll, pitch, yaw }
float angles[3] = {0.0, 0.0, 0.0};

char read_char() {
  unsigned long starttime = millis();
  do {
    if(Serial1.available() > 0)
      return((char)Serial1.read());
  } while((millis() - starttime) < TIMEOUT);
  return NULL;
}

void get_angles() {
  for(int n=0; n<3; n++) {
    char buffer[9];
    int received = 0;
    char ch;
    do {
      if((ch = read_char()) == NULL) return;
    } while(ch != ',' && ch != '\r' && (buffer[received++] = ch));   
    buffer[received] = '\0';
    angles[n] = atof(buffer);
  }
}

void read_9dofahrs() {
  char beginstr[6] = {'!','A','N','G','\:','\0'};
  for(int i=0; i<5; i++)
    if(read_char() != beginstr[i]) return;
  get_angles();
}

void setup() {
  Serial.begin(57600);
  Serial1.begin(57600);
}

void loop() {
  read_9dofahrs();

  Serial.print(angles[0]);
  Serial.print(" | ");
  Serial.print(angles[1]);
  Serial.print(" | ");
  Serial.print(angles[2]);
  Serial.println("");
}
The result of this sketch can be seen in the following figure.

This short sketch will then be added to the main program that will control the stabilization and orientation in space of the quadcopter.

Sunday, November 21, 2010

Brushless Motors and Speed Controllers.

I am a beginner in electronics and although my skills are still basic, I am still searching documentation to see if I understand how I can properly put into operation one or more motors using the Arduino platform. In most open source quadcopter projects are almost always used the same speed controllers, however it should be possible to use any speed controller that works with a radio control (R/C).
I have been taking a look to several web sites and I have found that there were two main ways of how to communicate with speed controllers and put the motors running. One is to write a pulse width in microseconds directly to the i/o pins and the other is to use the Arduino "Servo" library or similar libraries like "SoftwareServo". In a previous post I have described how to put four motors running with the speed controllers that I have got for this project using the "Servo" library. Although, I have not investigated in depth the internals of the library I have noticed that the motor control is achieved by sending pulses to the speed controller or alternatively by sending an angle that can varies between 0º and 180º (the angle is converted internally to a pulse width in microseconds). However and for the speed controllers I am using, if I only send pulses to the i/o pins that will not put the motors in operation since the speed controllers need be initialized before using. So, in order to have more information available, I have downloaded of the speed controller specifications that I am using. After reviewing that document I have tried to develop an algorithm with the all steps to put the motors in operation. The procedure according to my adaptation of the document to initialize the speed controller is as follow (this procedure must be done before each use):
  1. Connect the Arduino board to the computer via USB to power up the board and set the pulse width value of the stop position on output pin (the stop position must be within the range 800 us to 1700 us for my speed controller). This procedure is like move the throttle stick to the desired stop position, and switch on the transmitter.
  2. Connect now the flight battery to the speed controler. The motor responds with 2 x beep.
  3. Increase the pulse width value (microseconds) in one unit on output pin until the motor responds with 3 x beep (the full-throttle position must be at least 350 us greater than the stop position). This is like move the throttle stick to the desired full-throttle position.
  4. Set again the pulse width value of the stop position on output pin. This procedure is like move the throttle stick to the desired stop position.
  5. If you now increase the pulse width value on output pin the motor will start running. This is like move the throttle stick from the stop position towards full-throttle.
I tried to implement the two methods described above but the only one that worked was the method that uses the library "Servo". The implementation of the procedure is now shown:
/*

  Motors: 4 x ROXXY 2827-34
  ___ESC: 4 x Roxxy BL-Control 818
  _Board: Seeeduino Mega v1.1
  
  ATENTION: Do not put the propellers in motors because this
            sketch is not well tested and can cause damage.
  
*/
#include <Servo.h>

// Set the pulse width value of the stop position to mid-point (1250 us).
#define STOPPOSITION          800+(1700-800)/2
// The full-throttle position must be at least 350 us greater
// than the stop position (1600 us).
#define FULLTHROTTLEPOSITION  STOPPOSITION+350
// My upper speed limit for safety.
#define MAXTHROTTLEPOSITION   FULLTHROTTLEPOSITION-100
// Create servo object to control four motors
Servo escservos[4];
// Variable to store the temporarily pulse width value.
int usvalue = STOPPOSITION;

void programpositions() {
  Serial.println("Type the character [p] to program the Stop and");
  Serial.println("Full-Throttle positions after connect the flight");
  Serial.println("battery to the ESC's.");
  Serial.println("The motor responds with 2 x beep.");
  Serial.println("");
  Serial.println("Type the character:");
  Serial.println("[+] to increase the pulse width value.");
  Serial.println("[-] to decrease the pulse width value.");
  Serial.println("[0] to set the pulse width value to stop position.");
  
  // Wait until get the input from the user.
  while(Serial.available() <= 0) {
    // Set the pulse width value of the stop position on output pins.
    for(int s=0; s<4; s++)
      escservos[s].writeMicroseconds(STOPPOSITION);
  }
  
  char ch = Serial.read();
  if(ch == 'p') {
    // Increase the pulse width value in one unit on output pins.
    // Exactly after a pulse width of the 350 microseconds from
    // the stopping position the motor responds with 3 x beep.
    for(int us=STOPPOSITION; us<=FULLTHROTTLEPOSITION; us++)
      for(int s=0; s<4; s++)
        escservos[s].writeMicroseconds(us);
    delay(5000);
  }
}

void set_speeds(int servo, int usvalue) {
  escservos[servo].writeMicroseconds(usvalue);
}

void setup() {
  Serial.begin(9600);
  
  // Attaches the ESC's on pins 2, 3, 4 and 5 to the servo object.
  for(int s=0; s<4; s++)
    escservos[s].attach(s+2, STOPPOSITION, FULLTHROTTLEPOSITION);
  
  programpositions();
}

void loop() {
  if(Serial.available()) {
    char ch = Serial.read();
    if(ch == '+') {
      // Increase the pulse width value.
      // The motor only starts running after increased up to 40 us.
      usvalue++;
      if(usvalue > MAXTHROTTLEPOSITION)
        usvalue = MAXTHROTTLEPOSITION;
    } else if(ch == '-') {
      // Decrease the pulse width value.
      usvalue--;
      if(usvalue < STOPPOSITION)
        usvalue = STOPPOSITION;
    } else if(ch == '0') {
      // Set the pulse width value to the stop position.
      usvalue = STOPPOSITION;
    }
    Serial.println(usvalue);
  }
  for(int s=0; s<4; s++)
    set_speeds(s, usvalue);
}
This implementation works very well for my speed controllers, but for other speed controllers the implementation will probably be different. The difference to the previous post is that instead using the value of the angles, I have used the pulse width value in microseconds and the delay after sending the angle has been removed. One immediate problem is that the Arduino board must always be connected before the speed controller which requires that board will check if the speed controller is switched on or off. If the speed controller is already connected and initialized and if for any reason you switched off the board and switched it on again, the motor can speed up to full speed because the speed controller was already set up, but as the board does not recognize it, it will make a new initialization. Therefore it is necessary to send a character to do or not the initialization of the speed controller. To solve this problem it is necessary a current sensor which will check whether the speed controller is switched on or off.

Wednesday, November 17, 2010

How to Upgrade 9DOF Razor IMU Test Firmware.

When I got the 9DOF - Razor IMU board I tried to upgrade the "Test Firmware" using the "Makefile" from the zip file but without success. So I sent an email to the Sparkfun tech support for them to verify what I was doing wrong. Today I finally got the answer and the best way to upgrade the firmware is upgraded manually.
The first step is to connect the 9DOF - Razor IMU board to FTDI Basic Breakout - 3.3V board following the diagram showed in Figure 1 from the post "How to upload 9DOF AHRS code". The second step is download the latest code from the git repository and execute the following commands (the commands were executed in a terminal on a machine with the Linux OS):
$ mkdir tmp
$ cd tmp
$ wget --no-check-certificate https://github.com/a1ronzo/\
SparkFun-9DOF-Razor-IMU-Test-Firmware/raw/master/9DOF-v18/9DOF-v18.zip
$ unzip 9DOF-v18.zip
$ make all
$ avrdude -p atmega328p -P /dev/ttyUSB0 -c stk500v1 -b 57600 \
-U flash:w:main.hex

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.02s

avrdude: Device signature = 0x1e950f
avrdude: NOTE: FLASH memory has been specified, an erase cycle will be
         performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "main.hex"
avrdude: input file main.hex auto detected as Intel Hex
avrdude: writing flash (9156 bytes):

Writing | ################################################## | 100% 2.99s

avrdude: 9156 bytes of flash written
avrdude: verifying flash memory against main.hex:
avrdude: load data flash data from input file main.hex:
avrdude: input file main.hex auto detected as Intel Hex
avrdude: input file main.hex contains 9156 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 2.28s

avrdude: verifying ...
avrdude: 9156 bytes of flash verified

avrdude: safemode: Fuses OK

avrdude done.  Thank you.
Open now the Arduino IDE and click on "Serial Monitor" icon or from the "Tools" menu select the item "Serial Monitor". Set the baud rate to 38400 and verify if the "9DOF IMU Firmware v18" menu is showed (Figure 1).
Figure 1
It's not straightforward to save the previous firmware directly from the AVR. It's easier to keep the older .hex files around if I need them.

Wednesday, November 10, 2010

How to upload 9DOF AHRS code.

Figure 1
The first step is to connect the 9DOF - Razor IMU board to FTDI Basic Breakout - 3.3V board following the diagram showed in Figure 1. Then download the latest code from the sf9domahrs Google Code repository and uncompress the zip file. Change to the directory SF9DOF_AHRS, and open the SF9DOF_AHRS.pde file in Arduino IDE (0021).
Figure 2
After opening the file will be visible seven tabs (SF9DOF_AHRS, ADC, Compass, DCM, I2C, Output, Vector and matrix) like as shown in Figure 2. Select from the "Tools" menu and then from the "Board" submenu the board Arduino Pro or Pro Mini (3.3 V, 8Mhz) w/ AtMega328 which corresponds with the 9DOF - Razor IMU board.
Figure 3
Compile the code with the Arduino IDE and upload it to the 9DOF - Razor IMU board. Open the Serial monitor, set the baud rate to 57600 and verify if the data are coming (Figure 3), and voila, it's everything that has to do with it.