Sunday, January 27, 2013

CritterBot with a Little Sense of Balance

My goal to this point was to put a bot inside a critter crawl ball. I'm not sure why, but it just seemed like a fun thing to do.

So here we have it. I've attached the high torque gear motor axles to 3/4 inch wooden dowels and attached them to the sides of a 13 inch critter crawl that I bought at the pet store.

I specifically made the assembly heavy at one end so I could use gravity as a drive mechanism. Never mind steering at this point, in fact I'm not sure it can ever be steered, but I'll explore that later.

By adding the accelerometer, I'm able to detect the tilt of the assembly. So using this, I should be able to keep the motor from just flipping the assembly over randomly. In other words, I need some sense of balance as I want to keep the assembly horizontal to the floor to maximize the gravity imbalance, thus causing it to roll.

After mounting the accelerometer I created this graph. What I thought would be a difficult algorithm turned out to be quite simple. If I want to move forward, then I should keep the Z axis at zero, and I can simply use the sign of X to determine if I need to add or subtract power from the motor. So if the angle of the assembly is at 45 degrees in the Z axis, and the X axis is positive, then add more power. Simple. If I want to reverse direction, then just the opposite is true if I ignore the sign of the Z axis. The controller has subscribed to accelerometer events, so the forward code uses those events to adjust the speed:

case ADXL345_STATUS_SIG:
{
  int8_t x = Q_EVT_CAST(ADXL345EventArg)->x;
  int8_t z = Q_EVT_CAST(ADXL345EventArg)->z;

  if( moveState == MOVE_STATE_FORWARD )
  {
    if( z > 5 )
    {
      motorSpeed +=  x < 0 ? -10 : +10;

      if( motorSpeed < 0 )
        motorSpeed = 0;
      else if( motorSpeed > 100 )
        motorSpeed = 100;

      DCMotorControl_Set(DC_SPIN_MOTOR, motorSpeed, DCMD_MODE_FORWARD);
    }
  }
}

I've hard coded the values here but eventually I'll make them constants, or variables if the situation arises. So we add plus 10 or minus 10 percent to the motor speed if the z axis is not within 5 degrees of zero.

Backing up to the accelerometer process, it sends out an average angle update to all subscribers

case ADFTIMER_NOTICE:
{
  if( Q_EVT_CAST(ADFTimerEventArg)->processID == me->m_ProcessID )
   {
      ...
      ADXL345EventArg *ea  = Q_NEW(ADXL345EventArg, ADXL345_STATUS_SIG);
      ea->x = me->xAve;
      ea->y = me->yAve;
      ea->z= me->zAve;
      QF::PUBLISH(ea, ADXL345_STATUS_SIG);

at regular intervals. These intervals are based on the accelerometer's subscription to the timer process for an event every 200 milliseconds:

     ADFTimer_StartTimer(me->m_ProcessID, 0, 200);

So the accelerometer takes readings every 200 milliseconds, averages those readings, and sends out an accelerometer event every 5 events, or about one second. What surprised me was that with only a 1 second sample, the CritterBot appeard to behave fairly well. In the following video, you can see the bot climb to 90 degrees and stop because gravity didn't get the ball rolling. But as soon as it starts rolling the bot tries to stay at zero Z axis so you can see and hear it accelerate across the floor.


Cool.

I'll need to experiment more, which I will be able to do as soon as I can program it remotely. I've received my Adafruit parts and built the wireless XBee receiver part, but need my SeeeStudio order to get the transmitter part up and running.

No comments:

Post a Comment