Author Archive

14
Jun

I love this tweet so much I wanted to save it to refer to and share:

Working on a journal article right now, and this is pretty much my routine – minus the window, since there are none in my office. At least I know I’m not alone!

Category : News | Blog
12
Jun

After a conversation with JD Warren, one of authors of the excellent Arduino Robotics book, I decided to try the ACS712 hall effect current sensor. I soldered one to a surface mount to breadboard adapters and plugged it in with some capacitor values recommended on the first page of the data sheet. I first just tried to sense the current used by the “shoulder” motor in the robotic arm I’ve posted about before. Here’s a video (it runs way smoother now, promise):

I also hooked up a quick RC low pass filter at about 7 Hz, then I ran the robotic arm (code below) and graphed shoulder angle vs. current, and it looks like this:

The graph shows several cycles of the shoulder angle increasing and decreasing while the end effector draws an imaginary vertical line. And although the current signal is periodic, it’s not exactly clear what’s causing the profile to look the way it does. Also, there is no way the current scaling is right (should be under an Amp the whole time) but I’ve read the data sheet and looked at my code and can’t find the error. Can you?

/*
Robot arm control using hacked servos and a master arm with pots as control in
master-slave system.
+y is vertical, +x is to the right
drawing line/circle:

http://www.circuitsathome.com/mcu/robotic-arm-inverse-kinematics-on-arduino

inverse kinematics:

http://www.micromegacorp.com/downloads/documentation/AN044-Robotic%20Arm.pdf


http://www.learnaboutrobots.com/inverseKinematics.htm

*/

#include <AFMotor.h>
#include <math.h>
#include <PID_v1.h>

// Instantiate both motors
AF_DCMotor shoulder(3);
AF_DCMotor elbow(4);

// DECLARE PINS
int ShoulderPin = A0; // to potentiometer on shoulder motor
int ElbowPin = A1; // to potentiometer on elbow motor
int ShoulderCurrentPin = A2; // to first ACS712 IC

// INITIALIZE CONSTANTS
double Kp_Elbow = 20; // this is the proportional gain
double Kp_Shoulder = 20;
double Ki_Elbow = 0.1; // this is the integral gain
double Ki_Shoulder = 0.1;
double Kd_Elbow = 1.0; // this is the derivative gain
double Kd_Shoulder = 0.75;

double Elbow_neg = 970; // joint limits of robotic arm using right hand rule for sign
double Elbow_pos = 31;
double Shoulder_neg = 210;
double Shoulder_pos = 793;

const double a1 = 198; // shoulder-to-elbow "bone" length from Solidworks (mm)
const double a2 = 220; // elbow-to-wrist "bone" length from Solidworks (mm) - longer c bracket

double highY = 350; // line drawing targets
double lowY = 250;
double constantX = 200;

boolean elbowup = false; // true=elbow up, false=elbow down

float Volt_per_amp = 0.185; // 185 mV/A resolution according to hardware page
 
// VARIABLES
double rawElbowAngle = 0.0; // initialize all angles to 0
double rawShoulderAngle = 0.0;

double elbowAngle = 0.0; // initialize all angles to 0
double shoulderAngle = 0.0;

double theta1 = 0.0; // target angles as determined through inverse kinematics
double theta2 = 0.0;

double actualX = 0.0;
double actualY = 0.0;
double predictedX = 0.0;
double predictedY = 0.0;
double y = 0.0;

double c2 = 0.0; // is btwn -1 and 1
double s2 = 0.0;

double pwmTemp = 0.0;

double pwmElbow = 50.0; // between 0 to 255
double pwmShoulder = 50.0;

float shoulderCurrentRaw; // the raw analogRead ranging from 0-1023
float shoulderCurrentVolts; // raw reading changed to Volts
float shoulderCurrentAmps; // Voltage reading changed to Amps

//Syntax: PID(&Input, &Output, &Setpoint, Kp, Ki, Kd, Direction)
PID elbowPID(&elbowAngle, &pwmElbow, &theta2, Kp_Elbow, Ki_Elbow, Kd_Elbow, DIRECT);
PID shoulderPID(&shoulderAngle, &pwmShoulder, &theta1, Kp_Shoulder, Ki_Shoulder, Kd_Shoulder, DIRECT);

void setup() { 
  Serial.begin(115200); // set up Serial library

  elbowPID.SetSampleTime(5); // (ms) how often new PID calc is performed, default is 1000
  shoulderPID.SetSampleTime(5);

  elbow.setSpeed(pwmElbow); // set the speed to pwmElbow
  shoulder.setSpeed(pwmShoulder); // set the speed to pwmElbow

  elbowPID.SetMode(AUTOMATIC); // modes in the PID library that have to be set
  shoulderPID.SetMode(AUTOMATIC);

  elbowPID.SetOutputLimits(-255,255); // we'll use the sign to tell us motor direction
  shoulderPID.SetOutputLimits(-255,255);

  move_to_start(); //get to starting position
}

void loop() { 
  line_y();
}

void move_to_start() {
  do {
    get_angles(constantX, lowY);
    drive_joints(); // drive joints until actual position equals expected position
  }
  while( abs(theta1 - shoulderAngle) > 2 && abs(theta2 - elbowAngle) > 2 ); // bail when it's close
}

void line_y() {
  for(y = lowY; y < highY; y += 0.5) { // draw straight line up
    get_angles(constantX,y);
    drive_joints();
  }
  for(y = highY; y > lowY; y -= 0.5) { // draw straight line down
    get_angles(constantX, y);
    drive_joints();
  }
}

// Given predicted x,y position at any time (forward kinematics)
void get_xy() {
  predictedX = a1*cos(radians(theta1)) + a2*cos(radians(theta1+theta2));
  predictedY = a1*sin(radians(theta1)) + a2*sin(radians(theta1+theta2));
  actualX = a1*cos(radians(shoulderAngle)) + a2*cos(radians(shoulderAngle+elbowAngle));
  actualY = a1*sin(radians(shoulderAngle)) + a2*sin(radians(shoulderAngle+elbowAngle));
}

// Given target(Px, Py) solve for theta1, theta2 (inverse kinematics)
void get_angles(double Px, double Py) {
  // first find theta2 where c2 = cos(theta2) and s2 = sin(theta2)
  c2 = (pow(Px,2) + pow(Py,2) - pow(a1,2) - pow(a2,2))/(2*a1*a2); // is btwn -1 and 1

  if (elbowup == false) {
    s2 = sqrt(1 - pow(c2,2)); // sqrt can be + or -, and each corresponds to a different orientation
  }
  else if (elbowup == true) {
    s2 = -sqrt(1 - pow(c2,2));
  }
  theta2 = degrees(atan2(s2,c2)); // solves for the angle in degrees and places in correct quadrant

  // now find theta1 where c1 = cos(theta1) and s1 = sin(theta1)
  theta1 = degrees(atan2(-a2*s2*Px + (a1 + a2*c2)*Py, (a1 + a2*c2)*Px + a2*s2*Py));
}

void drive_joints() {
  // read the actual values from all the pots
  rawElbowAngle = analogRead(ElbowPin);
  rawShoulderAngle = analogRead(ShoulderPin);

  // constrain robot arm to ignore out of range values
  elbowAngle = constrain(rawElbowAngle, Elbow_pos, Elbow_neg);
  shoulderAngle = constrain(rawShoulderAngle, Shoulder_neg, Shoulder_pos);

  // now map the angles so they correspond correctly
  elbowAngle = map_double(elbowAngle, Elbow_neg, Elbow_pos, -110.0, 85.0);
  shoulderAngle = map_double(shoulderAngle, Shoulder_neg, Shoulder_pos, 5.0, 135.0);

  elbowPID.Compute();
  shoulderPID.Compute();

  // DRIVE ELBOW: CW is forward, CCW is backward
  pwmTemp = abs(pwmElbow);
  elbow.setSpeed(pwmTemp); // now use the new PID output to set the speed

  if (pwmElbow < 0) {
    elbow.run(FORWARD); // turn it on
  }
  else if ( pwmElbow > 0) {
    elbow.run(BACKWARD); // turn it on
  }
  else elbow.run(RELEASE); // stopped

  // DRIVE SHOULDER: CCW is forward, CW is backward
  pwmTemp = abs(pwmShoulder);
  shoulder.setSpeed(pwmTemp); // set the speed

  if (pwmShoulder < 0) {
    shoulder.run(BACKWARD); // turn it on
  }
  else if (pwmShoulder > 0) {
    shoulder.run(FORWARD); // turn it on
  }
  else shoulder.run(RELEASE); // stopped
  
  shoulderCurrentRaw = analogRead(ShoulderCurrentPin);
  if (shoulderCurrentRaw > 512) {
   shoulderCurrentRaw = shoulderCurrentRaw - 512; 
  }
  else {
   shoulderCurrentRaw = 512 - shoulderCurrentRaw; 
  }
  shoulderCurrentVolts = shoulderCurrentRaw * (5.0/1024.0);
  shoulderCurrentAmps = shoulderCurrentVolts/Volt_per_amp;
 
  Serial.print(millis());
  Serial.print(',');
  
  //Serial.print(elbowAngle);
  //Serial.print(',');
  Serial.print(shoulderAngle);
  Serial.print(',');
  //Serial.print(pwmElbow);
  //Serial.print(',');
  //Serial.print(pwmShoulder);
  //Serial.print(',');

  Serial.println(shoulderCurrentAmps);
  //Serial.print(',');
  //Serial.print(shoulderCurrentAmps);
  //Serial.print(',');
  //Serial.print(shoulderCurrentAmps);
  //Serial.print(',');
  //Serial.println(shoulderCurrentAmps);
  
  //Serial.print(actualX);
  //Serial.print(',');
  //Serial.print(predictedX);
  //Serial.print(',');
  //Serial.print(actualY);
  //Serial.print(',');
  //Serial.println(predictedY);
}

double map_double(double x, double in_min, double in_max, double out_min, double out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
Category : News | Robotics | Blog
12
Jun

After a recent post on current sensing, I talked to a couple of friends at NYC Resistor (my usual sounding board) and got some great advice (as usual).

One thing I didn’t remember was the difference between passive and active filtering, and that it might take more than a simple RC circuit to filter out the noise I was seeing. I was pointed to a few good reads:

http://ww1.microchip.com/downloads/en/devicedoc/41233A.pdf

http://www.maxim-ic.com/app-notes/index.mvp/id/1762

http://www.swarthmore.edu/NatSci/echeeve1/Ref/DataSheet/IntroToFilters.pdf

But before I got too far into digital signal processing land and making hardware based 2nd or 3rd order filters, I wanted to see if there was a better way. I got in touch with JD Warren, one of authors of Arduino Robotics. One of the projects in the book used a custom built motor driver with a hall effect current sensor. I’ve shied away from these until now because they seemed to be more voodoo than the more intuitive current sensing resistor method, but after reading parts of his book and talking with JD, I ordered up some ACS712s and some surface mount to breadboard adapters from Digikey. He always had good luck with them so hopefully that luck rubs off and they’ll be less noisy than the method I’m using now.

I also did some more research and found that the L298 h-bridge, basically the next step up from the L293 that comes on the Adafruit Motor Shield, has current sensing already built in! Luckily both the Sparkfun Ardumoto Shield and the Arduino Motor Shield use this h-bridge, but only the Arduino shield has those pins broken out. So I ordered both, figuring I could hack the Sparkfun one by cutting the traces and soldering some little jumper wires if necessary (I studied the schematics for quite a while before convincing myself this would be possible). From what I can gather from the datasheet, I still need to add a current sensing resistor, and it uses basically the same method I was using before, but something about the integrated solution I suppose could make a difference. We’ll see.

First Test: Arduino Motor Shield
The documentation on this shield is a little spotty, but from the schematic and Eagle files, I figured out that the current sensing resistors were already integrated along with an op-amp that presumably amplifies the tiny voltage drop. The hardware page says the resolution is 1.65 A/V, so it looks like they did the math already on what resistors and what gain was used to get an actual current rating from a detectable voltage drop. So I wrote up some code (below) and after a little tweaking got this:

Isn’t that beautiful?!? A little jumpy still but might be good. I think it’s time to swap this in where the Adafruit Shield is now and see what happens with the robot arm.

   
/*
Curent sensing with the Arduino Motor Shield: http://arduino.cc/it/Main/ArduinoMotorShieldR3
 
 One small DC motor attached to screw terminal block A
 External power from a 4 x AA batter pack
 
 CC-BY-SA
 by Dustyn Roberts
 6/12/2012
 */

// declare pins used on channel A
int directionPin = 12;
int pwmPin = 3;
int brakePin = 9;
int currentPin = A0;

int switchPin = 2; // motor on/off switch
// connect one side of switch to power, the other to ground through a 10k resistor
// read the switch state where the switch and resistor meet into digital pin 2
// similar to this lab: http://itp.nyu.edu/physcomp/Labs/DigitalInOut

// constants
float volt_per_amp = 1.65; // resolution according to hardware page

// variables
float currentRaw; // the raw analogRead ranging from 0-1023
float currentVolts; // raw reading changed to Volts
float currentAmps; // Voltage reading changed to Amps

void setup() {
  pinMode(directionPin, OUTPUT);
  pinMode(pwmPin, OUTPUT); // necessary according to forum post http://arduino.cc/forum/index.php/topic,89468.0.html
  Serial.begin(9600); 
}

void loop() {
  // read the switch input:
  if (digitalRead(switchPin) == HIGH) {
    // if the switch is closed:
    digitalWrite(pwmPin, HIGH); // necessary according to forum post http://arduino.cc/forum/index.php/topic,89468.0.html
    digitalWrite(directionPin, HIGH);
  }
  else {
    // if the switch is open:
    digitalWrite(pwmPin, LOW); // necessary according to forum post http://arduino.cc/forum/index.php/topic,89468.0.html
    digitalWrite(directionPin, LOW);
  }

  currentRaw = analogRead(currentPin);
  currentVolts = currentRaw *(5.0/1024.0);
  currentAmps = currentVolts/volt_per_amp;

  Serial.println(currentAmps);
}

Category : News | Blog
3
Jun

So I’m teaching and participating in ITP Camp this month, and one thing on the schedule for today is a 1 in 1:

  • Start and complete a creative project in 24 hours (you don’t need to work on it through the night – but nobody will be surprised if you do.)
  • It can be anything creative – there are no rules here
  • The Counselors will be around to help you out with tutorial and brainstorming
  • Each project needs a name and documentation posted by Monday morning – you can document it in the Campers Blog!
  • You’re encouraged to exhibit your completed project at ITP during the week!

Okay okay so cleaning up my Windows laptop so it boots in less than 15 minutes isn’t exactly creative, but it’s meant to enable creativity for the rest of the month by freeing up space and making things faster. This might be one of my problems…

I also have a Windows machine at work that’s almost as full. This will all change in the next 24 hours. If you have any tips on cleanup/defragmenting/cloud services/backup software/etc., please leave them in the comments! I’ll spare you the updates since I’m fairly sure you can imagine how this ends.

Category : News | Blog
2
Jun

In a nice departure from a normal week day, I got to accompany the Makerbot team to the Metropolitan Museum of Art yesterday for their “3-D Scanning and Printing Hackathon” event.

We used 123D Catch to grab scans of some of the sculptures, cleaned them up, then uploaded models to Thingiverse and printed some out on Replicators both at the Museum and back at the Botcave in Brooklyn. Here’s a screenshot of a 123D Catch of a sculpture I shot called Queen Marie-Amelie that was printing out when I left the museum yesterday:

Thanks to Jonathan (former Makerbot Artist-In-Residence) for taking the scan and cleaning it up to make an .stl file for printing! Can’t wait to see how it turns out. The Makerbot team is still there wrapping up, so make sure to check out their blog, Thingiverse, etc. for all the updates.

Category : News | Blog
23
May

In my last post, I talked about sensing the current of the motors in a robotic arm, which was a necessary step in determining energy consumption for the robot. But in order to calculate the energy being used by each motor, I need not only current data but also angular velocity. To get angular velocity, I just need to know the angle and the time, since angular velocity = (angle now – last angle)/(time now – last time). However, the map() function I used in Arduino to turn the potentiometer signals into degrees only deals in integer math, so I was losing a lot of data and getting 0 as my angular velocity a lot, which was really messing up the graph of shoulder speed (see below for an early screenshot – ack) and therefore messing up the energy consumption graph.

I figured I wasn’t the first person to come across this issue, so I posted on the forum and used the feedback to create a custom map_double function:

   
float map_double(double x, double in_min, double in_max, double out_min, double out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

then just replaced map with map_float everywhere I had it in my original code. Yeah! More to come on the energy consumption of the entire system…

Category : News | Blog
23
May

In my last post, I talked about using an RC filter to get rid of some of the noise in the current signal I was getting from the Arduino. While this worked okay, I still wasn’t super happy with the results, and the elbow motor on the robotic arm seemed to require different RC values than the shoulder motor, and there was a lot of guessing and testing going on. Some comments on the posts suggested using the Teensy with the Teensyduino software since it has a faster data rate, which could work, but the libraries I’m using for the Adafruit Motor Shield and the current sensing are not compatible, and the hardware itself would have to be rewired and it’s not clear to me exactly how I would wire a Teensy to an Arduino shield without it getting messy pretty fast. Another suggestion was to follow in the footsteps of some of the Arduino oscilloscope projects that use digital or lower level commands to send the data, but I have to admit I just really don’t understand what the code is doing there and I won’t just copy and paste code that doesn’t make sense to me.

So, basically my idea was to do enough filtering in hardware that the ~200Hz print rate from the Arduino gave me data that was smooth enough to actually work with.

I’ve been trying to avoid using LabVIEW and find an open source alternative to visualize the incoming data to confirm my RC values are working. One thing that sort of worked is using a DSO Nano instead of a fancy oscilloscope, but adjusting the DSO Nano to find the signal every time I changed RC values got tedious very fast. I also didn’t want to bug my friends in the lab down the hall every time I needed to use their multi-thousand dollar scope. So, I realized I do have a student edition of LabVIEW and a NI USB-6008 DAQ board that I bought a while ago. So, I decided to just use LabVIEW as my scope and the DAQ to read the current and allow me to test the RC values faster, while the Arduino did its thing in the background.

Getting started with LabVIEW is WAY harder than getting started with Arduino. Once you assemble the USB-6008 and put the stickers on, then read through the other documentation, you can plug something in like this:

NI USB-2008

The black ground wire is plugged into the same ground as Arduino, and the red wire coming from AI7 (analog input 7) is connected to the RC filtered output of the shoulder motor current sensor (A2 in the image here). Through the Measurement & Automation Explorer that comes with the software install, we can get a quick glimpse of what is going on by just choosing the correct analog input port:

This is great, but I want a way to scale the graph and tweak more things than I can tweak here, as well as save the output to a file when I need to. In order to take advantage of the capabilities of the DAQ, you have to install the NI-DAQmx software that comes with it, which takes approximately forever. The examples given and tools available in the student version are not always that helpful, but after some playing around, I got this:

The screenshot shows two graphs: the one on the left is the RC filtered signal, and the one on the right is the raw output of the op-amp software filtered with a 3rd order Butterworth low pass filter in software using the same exact cutoff frequency as determined by the R and C values of the hardware filter (7.234 Hz), so I can compare them. I knew I wanted a low pass filter, but as for those other details, they were just the defaults and seemed to work well. The graph on the right is definitely smoother, so there is clearly something that the software filtering can do that I can’t do with just a simple hardware filter. This does not bode well for choosing an RC filter and just using the 200 Hz data from the Arduino to map the current consumption. More to come when I work through this issue.

Category : News | Blog
14
May

So after some comments on my last post, some great comments on my Adafruit forum post about the actual robotic arm project I’m working on, some help from Twitter followers and some awesome fellow NYC Resistors, I realized there were a few more things I needed to do before getting meaningful current data from this robotic arm. I looked at the signal with a fancy oscilloscope with the help of a friend at NYU-Poly, and there was a great looking signal beneath the noise. So…

Step 1) Filter the noise
It was pretty clear from the comments that I would need to do some hardware filtering even before I worked with software filtering, so that meant creating an RC low pass filter. In order to verify that the RC filter was working, I finally had a good reason to figure out how to use my DSO Nano v2. I watched this video that talks through updating the firmware and getting started. There’s also some good info on the forum. I used a “real” oscilloscope from another lab here as a sanity check that what I was seeing on the DSO Nano was real. This is what the current for the robotic shoulder motor looks like with no filtering:

Untitled

I then used an RC low pass filter at the output of the op-amps to get rid of some high frequency noise. From some limited experience, I decided to shoot for a cutoff frequency of around 45 Hz – that should take care of any ambient 60 Hz noise from lights, etc. Using Adafruit’s awesome Circuit Playground app, I chose values of components I had on hand (R value of 330 ohm and a C value of 10uF) and designed the circuit to have a cutoff frequency of 48.229 Hz. That’ll do to start.

Untitled

So the 48 Hz low pass filter was better, but not great. I then decided to use the R and C values suggested by coffey in the Adafruit forum post above (R = 570 ohm and C = 47 uF) to create a cutoff frequency of 5.941 Hz. I didn’t have those exact values, so I kept the 10 uF caps in and swapped the resistor for a 2.2k ohm to get a cutoff frequency of 7.234 Hz.

7.234 Hz

Much better! Now let’s see what it looks like if I log this data through Cool Term as fast as Arduino is spitting it out:

Great! I graphed the Cool Term data in Excel quickly and you can see that even at the slow speed that I can log data with (about 265 Hz in this trial) I’m getting a pretty representative signal.

I’ll update this or make another post when I’ve finalized all the filtering, scaling, and math about it, but it’s getting pretty close to done.

Category : News | Blog
11
May

I’ve been working on a robotics project for a while* that requires me to log data from various analog sensors, and I’ve been doing it all with an Arduino so far. The objective is to basically use the Arduino as a data acquisition board and avoid using a more expense NI DAQ with LabVIEW or MATLAB. I don’t want to write to an SD card, because I want to be able to run repetitive experiments and graph the data in real time (through Processing) or close to real time (in Excel). I really need to log data very quickly and am having some issues getting above 200 Hz, so I thought I would simplify things and get back to basics to try to find the maximum speed I can write to a file. So I tried just using a photocell hooked up like one of the FSRs here , then I tried a speed test to see just how fast I could get data to print. While you can see data scroll in the serial monitor screen of Arduino, you can’t save it to a file directly from there. I’ve found that CoolTerm is the easiest way to do this, and it’s available in Mac, Windows, and Linux versions (yes, I’m still a PC).

So here’s the initial code:

const int analogInPin = A0; // Analog pin that the photocell is attached to
int sensorValue = 0; // value read from the photocell

void setup() {
// initialize serial communications at 9600 bps:
Serial.begin(115200);
}

void loop() {
// read the analog in value:
sensorValue = analogRead(analogInPin);

// print the results to the serial monitor:
Serial.print(millis());
Serial.print(',');
Serial.println(sensorValue);
}

And here’s a section of the initial output:
953,828
954,828
954,827

955,828
956,828
957,828
957,827

958,828

The millis() function outputs milliseconds of time elapsed, and as you can see above, I’m getting some readings at the same ms! Which means this is printing faster than 1000Hz. So, is there a way to get more resolution in the time function so each time stamp is associated with only one sensor reading? Turns out there is! The micros() function outputs in, you guessed it, microseconds- sort of. According to the reference:

On 16 MHz Arduino boards (e.g. Duemilanove and Nano), this function has a resolution of four microseconds (i.e. the value returned is always a multiple of four).

So then I checked the Arduino Uno hardware page (the version I’m using) to check if it is a 16Mhz version, and it is indeed. So, lets give this another shot and replace millis() with micros() in the above code.

1064,824
1908,824
2776,824
3664,824

Bingo! Now this time is going by so fast that two sensor readings never get logged to the same time reading. Okay, now to make sense of the data given the resolution…
I was confused by the resolution part of the statement above, but a user on the Arduino forum post I made cleared that up:

All it means is that, in effect, instead of incrementing a variable by one every microsecond, the variable is incremented by four every four microseconds.

So, there are 1,000 microseconds in 1 millisecond, and 1,000 milliseconds in 1 second, so there are 1,000,000 microseconds in 1 second. So all I have to do is divide each reading by 1,000,000 to get seconds. For example, the first time reading I get is 1024 microseconds = 1024/1000000 seconds = 0.001024 seconds. If I do that calculation for the first few data points, I get a delay of about 0.000888 seconds between readings, which means I’m logging data at about 1150 Hz. Not bad, but not great, given that analogRead() happens at at the rate of about 10,000 Hz. So can it get better?

One possible thing slowing down the write is using 3 different commands to print the microseconds, comma, and sensor. I remember someone telling me to write data out all at once in a string. I had a little trouble figuring out how to make a string of a number, a comma, and another number at first, but finally the code below worked:


const int analogInPin = A0; // Analog pin that the photocell is attached to
int sensorValue = 0; // value read from the photocell
String toprint;

void setup() {
// initialize serial communications at 9600 bps:
Serial.begin(115200);
}

void loop() {
// read the analog in value:
sensorValue = analogRead(analogInPin);
toprint = 0;
toprint += micros();
toprint += ',';
toprint += sensorValue;

// print the results to the serial monitor:
Serial.println(toprint);
}

However, the frequency of data writing actually decreased to about 885 Hz. So then, I posted the above on the Arduino forum and sent out a tweet. I LOVE twitter helpers! Big thanks to @talldarknweirdo for this insight:
1150 is about what you should be getting, here’s the math…
115,200 is your serial rate, thats approximately in bits. 9 bits to a character so 115,200/9=12,800
9 characters to a complete reading, 12,800/9=1422. you’re losing a tad to overhead.
now, analogread returns a value that is really only 10 bits long (0-1023 1,024=2^10) and Millis returns a value 32 bits long.
And to those commented on the other forum thread I posted here.

So it seems that 1150 Hz is about the fastest I can write serial data to a file without getting into some tricky optimization of code and lower level programming. Here are some references on that for those who are interested:

https://github.com/practicalarduino/ScopeDigitalOptimized (thanks @jonoxer !)

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1208715493/11

http://www.arduino.cc/en/Reference/PortManipulation

*This is an attempt at blogging as note-taking and blogging as code repository, as mentioned by Matt Mights. Hopefully this will be useful to other people while also giving me a searchable reference for the next time I try this!

Let me know if there’s something I didn’t try that can make the regular code better without going into lower level programming! Thanks

Category : Robotics | Blog
27
Mar

I have been working with Greg Borenstien for a while now on creating an open source tool for visualizing human movement data. You can see all his code here on github. We needed some data that had x, y, and z positions of human joints over time during a certain task, as well as angular velocities and joint torques. The only freely available source we found that had all of this kinematic and kinetic data was the appendix of Biomechanics and Motor Control of Human Movement by the late David Winter. There was a helpful thread on the Biomch-L forum that led to some digital versions of this appendix. However, there was never a comprehensive Excel file posted with all the data.
So, here it is: Winter_Appendix_data.

It’s just the right leg and a torso point, but Greg got a visualization up and running quickly:

Biomechanic Leg Visualization from Greg Borenstein on Vimeo.

Category : News | Blog