3 June 07

Let’s subtitle this article “My Adventures inside the Head of a Robot”

I love writing code for my little iRobot Create. It’s profoundly more satisfying than writing web software in PHP. I especially love the low-level nature of it; it reminds me of when I was learning to program years ago.

Writing web software 40 hours a week, I live in a world of (nearly) unlimited computer processing power and data storage. If you run out of memory while working on a website, it’s because you screwed up somewhere. Working on an 8-bit micocontroller is way cooler because there are so many more gotchas.

(Also, there’s no cross-browser compatibility to worry about. Your code only has to work on the one machine in front of you.)

The Problem of Division

I was excited to try to apply the lessons of the excellent Joseph L. Jones book, so I was reading the iRobot Control Module manual in hopes of getting started soon.

The following paragraph stopped me in my tracks:

Do not use floating point numbers or division in your Command Module code. The ATMega168 doesn’t have hardware to handle floating point numbers or divide operations. If you use these, your compiled code gets big very fast. Instead, use integers, and use right shifts (>>) instead of divides.

The right shift operator is similar in many ways to dividing by powers of two: right shifting by n lops the n right-most bits off a number. For example, I want to do 12 >> 2. 12 is 1100 in binary, and by lopping off the 2 right-most bits, I get 11 in binary, which is 3 in base 10. So 12 >> 2 = 3 (which is similar to 12 divided by 22).

The trick is, how can I implement division this way? I sure didn’t know, and Google was less helpful than one would expect.

I eventually came up with this solutions, and I’m certainly open to comments:

int divide(int numerator, int denominator){
  int power_of_two = 0;
  int result;

  switch(denominator){
    case 1:
      return numerator;
      break;
    case 0:
      return NULL;
      break;
  }

  if(numerator == 0)
  {
    return 0;
  }

  while((denominator << (power_of_two+1)) <= numerator)
  {
    power_of_two++;
  }

  result = denominator << power_of_two;

  if((numerator - result) <= 0){
    return 1 << power_of_two;
  }
  else{
    return (1 << power_of_two) + divide((numerator - result),denominator);
  }
}

It doesn’t use a right shift, but it does use left shift, so hopefully that’s good enough.

Robot Brain

The iRobot kit comes with three helpful demo programs to get you started, so I dove right in and started hacking away at the “drive” program.

The task I set for myself was to get it to function more like the behavior-based model in the Jones book. The trick of course is that Jones isn’t writing iRobot code, but just suggesting generic code structure and ignoring specific platform implementations (as he should).

A lot of what I did today involved making the demo work more the way my brain wanted it to, having read Jones. Here’s an example:

The “drive” demo program comes with a subroutine to power the robot's wheels.

void drive(int16_t velocity, int16_t radius)
{
  byteTx(CmdDrive);
  byteTx((uint8_t)((velocity >> 8) & 0x00FF));
  byteTx((uint8_t)(velocity & 0x00FF));
  byteTx((uint8_t)((radius >> 8) & 0x00FF));
  byteTx((uint8_t)(radius & 0x00FF));
}

byteTx sends data from the Command Module to the iRobot Open Interface (which I should note is excellently well documented). CmdDrive is a constant for the command that says “I’m about to send you velocity and radius data for the wheels.” (The rest of the function is the velocity and radius sent in two 8-byte chunks each.)

What I wanted instead was a way to power each wheel separately. Rather than saying “go left while turning through such-and-such a turn,” I wanted to say “power the right wheel at 200mm/s and the left wheel at -200mm/s.”

The Open Interface specification says:

Drive Direct Opcode: 145 Data Bytes: 4

This command lets you control the forward and backward motion of Create’s drive wheels independently. It takes four data bytes, which are interpreted as two 16-bit signed values using two’s complement. The first two bytes specify the velocity of the right wheel in millimeters per second (mm/s), with the high byte sent first. The next two bytes specify the velocity of the left wheel, in the same format. A positive velocity makes that wheel drive forward, while a negative velocity makes it drive backward.

So, my modified drive function looks like this:

void drive(int16_t left_velocity, int16_t right_velocity)
{
	//nb - this is changed from the default drive command to control each wheel independantly
  byteTx(CmdDriveWheels);
  byteTx((uint8_t)((left_velocity >> 8) & 0x00FF));
  byteTx((uint8_t)(left_velocity & 0x00FF));
  byteTx((uint8_t)((right_velocity >> 8) & 0x00FF));
  byteTx((uint8_t)(right_velocity & 0x00FF));

}

It’s almost the same; the biggest difference is that the command given uses a different constant for the different Open Interface command being implemented.

Done for the Day

There’s a lot to be done from where I left off today, but I’ve met my goal of implementing a behavior-based model.

I also started working in error handling. One of my next tasks will be to have the Create’s power LED flash red to communicate error codes. It the code linked below, you’ll see the beginning of some of that, though the code itself lives in a separate file I haven’t put online yet.

Comments:

Textile Help