Archive for August, 2011

Diffident robot

When I saw the “Mr. General” robot by oddbot (link), I loved it. I thought it is “expressive”, a skill difficult to find in a robot built at home.  So I built my version of  Mr.General with Arduino.
The robot is based on compound-eye sensor (link): an infrared sensor that, with a minimum of programming, knows if the object, moving ahead, is going up, down, right or left.

Here’s the robot:

For the building, in addition to the compound eye sensor, I used two brackets with two servo motors that allow the pan and tilt mouvement, like a head. Also these have been provided by the Dagu, that created the compound eye (link). Both these items are very cheap: the compound eye costs $ 9, pan and tilt costs 14.5 dollars, including the two servomotors.
The advantage of using pan and tilt sensor of the same company is easily understandable. They are perfectly integrated and perfectly complementing.

To complete the robot, in addition to the Arduino, I used two continuous rotation servos, a little wheel caster and two battery packs, one for the Arduino and one for the servo motors (I recommend always using separate power supplies with a common ground). The chassis is built using a PVC foam sheet of 5 mm which has an incredible ease of modeling (you cut it with a knife) and an acceptable mechanical strength and rigidity.

What the robot does? It is a diffident robot. It waits until an an object is in front. Then it starts, follows the object moving in front of him, looking like a good robot, trying to keep the right distance from the object. if the object goes too close, the robot runs away, when if the object goes away, the robot begins to chase.
Here the robot in action:

For the code, I modified the code made by Oddbot (here). This is my code:

#include <Servo.h>

#define IRleft 1 // Compound Eye Left - analog input A1
#define IRright 3 // Compound Eye Right - analog input A3 
#define IRup 0 // Compound Eye Up - analog input A0
#define IRdown 2 // Compound Eye Down - analog input A2
#define IRleds 2 // Compound Eye LEDs - digital output D2

#define panPin 3 // PAN Servo - digital output D3
#define tiltPin 5 // TILt Servo - digital output D5
#define sServoPin 9 // Left Motor Servo - digital output D9
#define dServoPin 6 // Right Motor Servo - digital output D6

// definisci le costanti - define constants
byte LRscalefactor=10;  //fattore di scala - calibrare sui propri servi - calibrate depending on servo
byte UDscalefactor=10;  //fattore di scala - calibrare sui propri motori - calibrate depending on servo
// distanza minima per far muovere la testa - minimum distance to move the head
int distancemax=250;    //valore da trovare sperimentalmente - experimental value - adc value for analaog input for mimu
// distanza che provoca uno spostamento dei servi desto e sinistro - distance for moving the servos left and right
int bestdistance=450;   // valore da trovare sperimentalmente - experimental value - acd 
int PanZero=78;         // stop servo di pan (destra - sinistra) - pan servo stop
int TiltZero=70;         //stop servo alto basso - tilt servo stop
int sMotorStop=90;       // stop servo sinistro - stop left servo
int dMotorStop=90;       // stop servo destro - stop right servo
int LRmax=170;           // max valore servo pan - pan servo max value
int LRmin=10;            // min valore servo pan - pan servo min value
int UDmax=170;           // max valore servo tilt - tilt servo max value
int UDmin=10;            // min valore servo tilt - tilt servo min value

// Definisci le variabili di appoggio - define global variables
int pan=PanZero;
int tilt=TiltZero;
int panscale;
int tiltscale;
int sSpeed=sMotorStop;
int dSpeed=dMotorStop;
int panOld;
int tiltOld;
int distance;
int temp;

// IR Sensors - sensori IR
int updown;
int leftright;
int leftIRvalue;
int rightIRvalue;
int upIRvalue;
int downIRvalue;

//Servomotors - Servomotori
Servo sMotor;
Servo dMotor;
Servo panLR;
Servo tiltUD;

void setup()
{
  // inizializza i servo e configura i pin
  // initialize servos and configure pins
  sMotor.attach(sServoPin);
  sMotor.write(sMotorStop);
  dMotor.attach(dServoPin);
  dMotor.write(dMotorStop);
  panLR.attach(panPin);
  panLR.write(PanZero);
  tiltUD.attach(tiltPin);
  tiltUD.write(TiltZero);

  pinMode (IRleds,OUTPUT);
}

void loop()
{
  // imposta velocità servi - speed servos set
  sMotor.write(sSpeed);
  dMotor.write(dSpeed);
  panLR.write(pan);
  tiltUD.write(tilt);

  IReye();   // leggi valori compound eye - read compound eye values
  IRfollow(); // imposta inseguimento - following set
}

void IReye()//===============================================================Read IR compound eye================================================
{
  digitalWrite(IRleds,HIGH);                                  // turn on IR LEDs to read TOTAL IR LIGHT (ambient + reflected) - attiva sensore IR (ambiente + riflessione)
  delay(5);                                                   // Allow time for phototransistors to respond - aspetta per permettere la risposta dei fototransistors
  leftIRvalue=analogRead(IRleft);                             // TOTAL IR = AMBIENT IR + LED IR REFLECTED FROM OBJECT
  rightIRvalue=analogRead(IRright);                           // TOTAL IR = AMBIENT IR + LED IR REFLECTED FROM OBJECT
  upIRvalue=analogRead(IRup);                                 // TOTAL IR = AMBIENT IR + LED IR REFLECTED FROM OBJECT
  downIRvalue=analogRead(IRdown);                             // TOTAL IR = AMBIENT IR + LED IR REFLECTED FROM OBJECT
  delay(2);
  digitalWrite(IRleds,LOW);                                   // turn off IR LEDs to read AMBIENT IR LIGHT (IR from indoor lighting and sunlight)- spegni IR per leggere luminosità ambientale (luce interna + sole)
  delay(5);                                                   // Allow time for phototransistors to respond- aspetta per permettere la rispsta dei fototransistors
  leftIRvalue=leftIRvalue-analogRead(IRleft);                 // REFLECTED IR = TOTAL IR - AMBIENT IR
  rightIRvalue=rightIRvalue-analogRead(IRright);              // REFLECTED IR = TOTAL IR - AMBIENT IR
  upIRvalue=upIRvalue-analogRead(IRup);                       // REFLECTED IR = TOTAL IR - AMBIENT IR
  downIRvalue=downIRvalue-analogRead(IRdown);                 // REFLECTED IR = TOTAL IR - AMBIENT IR

  distance=(leftIRvalue+rightIRvalue+upIRvalue+downIRvalue)/4;// distance of object is average of reflected IR - la distanza dell'oggetto è la media di quanto riflesso dal sensore IR
}

void IRfollow ()//==============================================Track object in range================================================================
{
  // ferma servi - stop servos
  sSpeed=sMotorStop;
  dSpeed=dMotorStop;

   // se il valore restituito è basso, significa che non ci sono oggetto vicini
   // if the value read is low, there aren't object in front of the sensor
    if (distance<distancemax)
  {
    // riporta il sensore in posizione di riposo - reset the sensor at zero position
    if (pan>PanZero)pan=pan-1;
    if (pan<PanZero)pan=pan+1;
    if (tilt>TiltZero)tilt=tilt-1;
    if (tilt<TiltZero)tilt=tilt+1;

  }
  else
  {
    //-------------------------------------------------------------Track object with head------------------------------------------------
    panscale=(leftIRvalue+rightIRvalue)*LRscalefactor/10; //fattore di scala - scale value
    tiltscale=(upIRvalue+downIRvalue)*UDscalefactor/10; // fattore di scala - scale value

    // se sinistra gira a sinistra la testa - if left, turn left the head
    if (leftIRvalue>rightIRvalue)
    {
      leftright=(leftIRvalue-rightIRvalue)*15/panscale;
      pan=pan-leftright;
    }
    // se destra gira a destra la testa - if right, turn right the head
    if (leftIRvalue<rightIRvalue)
    {
      leftright=(rightIRvalue-leftIRvalue)*15/panscale;
      pan=pan+leftright;
    }

    // se alto, gira in alto la testa - if up, turn up the head
    if (upIRvalue>downIRvalue)
    {
      updown=(upIRvalue-downIRvalue)*15/tiltscale;
      tilt=tilt+updown;
    }

    // se basso, gira in basso la testa - if up, turn up the head
    if (downIRvalue>upIRvalue)
    {
      updown=(downIRvalue-upIRvalue)*15/tiltscale;
      tilt=tilt-updown;
    }

    panOld=pan;
    tiltOld=tilt;
    if (pan<LRmin) pan=LRmin;
    if (pan>LRmax) pan=LRmax;
    if (tilt<UDmin)tilt=UDmin;
    if (tilt>UDmax)tilt=UDmax;

    //-------------------------------------------------------------Turn body to follow object--------------------------------------------
    // se la testa si gira più di 60 gradi, gira anche il corpo - if the head turn more than 60 degrees, turn the body
    temp=LRmax-panOld;
    if (temp<60)
    {
      sSpeed=sMotorStop-50+temp/2;
      dSpeed=dMotorStop-50+temp/2;
    }
    temp=panOld-LRmin;
    if (temp<60)
    {
      dSpeed=dMotorStop+50-temp/2;
      sSpeed=sMotorStop+50-temp/2;
    }

    //------------------------------------------------------Move forward or backward to follow object------------------------------------
    // se l'oggetto è a distanza inferiore a quella bestdistance, muovi per tornare a quella distanza
    // if the object distance is less than bestdistance, move the robot to go again to the same distance
    temp=distance-bestdistance;
    temp=abs(temp);

    if (temp>10)
    {
      temp=temp-10;
      if (distance>bestdistance)
      {
        dSpeed=dSpeed-temp/3;
        sSpeed=sSpeed+temp/3;
      }
      else
      {
        dSpeed=dSpeed+temp/3;
        sSpeed=sSpeed-temp/3;
      }
    }
  }
}

 

Tags: ,

Robot shoe brush


In those days I was thinking about the mechanism of vibration in cell phones and wondered how it worked. I had no idea. So, I searched a little with Google. Basically, the mechanism that causes the vibration is a normal engine which is attached to an eccentric weight. When the motor spins, the eccentric weight causes a vibration.
Looking on the online robot shops I’ve seen that are sold the vibration motors. Then I said to myself: why not build a simple robot with a nice vibration motor? And so I tried to think of something original and super cheap.
I went into the shop of my friends Chinese to make me some inspiration, and I found something very very nice: a vibrating massager.

 

I thought that inside there was a vibration motor that I could reuse. I opened it and what I found? just a vibration motor:

To use the motor in a vibrating robot I needed to remove the motor from its housing, which is not possible for the insufficient length of wire (yellow in the figure). Then I added the wires to allow the motor to move freely outside the structure of the Chinese mini massager. Here are soldered wires (white):

To build the robot I used, as usually in this case, a brush. A shoe brush like this (sorry but I missed to take of photo before the robot building):

So what happen if I put together a vibrating motor and a shoe brush? First of all, I need something to hold the motor and the batteries on the brush. I used a generous splash of hot glue.
Here’s the result:

Basically what is it? There is a mini massager, properly disassembled and ‘turned’ upside down. There is the vibrating motor placed upon the brush and the brush itself. You can see the motor connected to the eccentric shaft which causes the vibration.
The idea is to make a little robot where the brush, vibrating, almost ‘suspended’ on the bristles, moving around the house. Of course it needs a smooth floor to slide well.
That’s it. Here is the link to the video:

As the bristles are not perfect (the brush was a bit deteriorated on one side) and since the motor always runs on the same side, the brush turns always on the same side, but with a new brush I am convinced that could be better.
The robot is cheaper than 10 euros all inclusive. In addition, it costs about 1 hour of work and it is affordable for everyone, absolutely everyone.

 

 

As the bristles are not perfect (the brush was a bit deteriorated on one side) and since the motor always runs on the same side, the brush turns always on the same side, but with a new brush I am convinced that could be better.
The robot is cheaper than 10 euros all inclusive. In addition, it costs about 1 hour of work and it is affordable for everyone, absolutely everyone.

12 servos controller low cost

 Building a robot with wheels or with legs needs a design completly different. One of the biggest difference is the type and number of motors.

While for a robot with wheels usually 2 or 4 servos or gear motors are enough, when we speak about a robot with legs, the number of motors increases dramatically. It is not uncommon to see a hexapod robot with 18 servos.
A quadrapod robot has usually 8 or 12 servos, depending on the number of joints. Luckily, compared to robot with wheels, usually are not required servomotors with continuous rotation or gearmotors. Standard servos (0-180 degrees) are enough.
Servomotors are driven with PWM and Arduino 2009 or Arduino Uno have 6 PWM pin. And this is a problem. And if you want to use also other sensors using digital pins, the situation is not good, because the Arduino’s digital pins are only 14, and 2 of wich are used for the serial port communication (pin 0 and 1).
It is possible to use the Arduino Mega, with 14 PWM pin, but it is very expensive (50 euros and more).
Searching in internet, I found a low cost chip, using I2C protocol that can drive 12 servos. First good news, the I2C protocol uses only the analog pins 4 and 5. Second good news is that you can use up to 8 chip, so it is possible to drive up to 8×12=96 servos with only 2 analog pins, the 4 and 5. Third good news is that the chip costs about 4 euros and half. A price that can be accepted by every robot builder.
This is the magic chip:

 

 

It is a DIP chip with 20 pins.The chip is made by Hobbytronics (here).  This is the pins schema:

 

This is the pins usage:

VDD = +5 V
Vss = GND
SDA, SLC are pins I2C that are connected to Arduino’s pins 4 and 5 and also connected to the Arduino +5 V using the pull-up resistors. The value is between 1K and 10K Ohm (I used the 3.3k Ohm).
Address1, 2, 3 are the pins that you can use to change the I2C address of the chip(usually not needed, unless you uses more than one chip, in this case it is necessary to differentiate the addresses).
SERVO01 … SERVO11 are pins connected  to the control pin of the servos (the others are connected to +5 V and GND).
I did a test  and I think it works fine.
Here is a short video I made:

 

 

This is the datasheet.

This is the code used in the video.

Below my code:

// I2C servo controller by www.hobbytronics.co.uk
// written by Alegiaco - July 2011

#include <Wire.h>

const int  servoslave_address=40;   // I2C Address of ADC Chip 
int gradServo[12];

void setup()
{

servo12Setup();

}

void loop()
{
moveServo(5,0); // move servo 5 to 0 degrees
delay(1000);
moveServo(5,90); //move servo 5 to 90 degrees
delay(1000);
moveServo(5,180);  //move servo 5 to 180 degrees
delay(1000);

// move servo 4 and 5 to 100 degrees
gradServo[4]=100;
gradServo[5]=100;
moveServoSet(4,5,gradServo);
delay(1000);

// move servo 4 and 5 to 180 degrees
gradServo[4]=180;
gradServo[5]=180;
moveServoSet(4,5,gradServo);

delay(1000);
}

void servo12Setup(){
 Wire.begin();              // join i2c bus (address optional for master)
  // Optionally set mode to standard 
  // – comment out this section if extended mode required
  Wire.beginTransmission(servoslave_address); // transmit to device
  Wire.send(20);             // servo register address 20
  Wire.send(1);              // 1 = extended mode (0-180 degrees)
  Wire.endTransmission();    // stop transmitting 
  delay(1);
}

void moveServo(int numServo, int deg) {
    Wire.beginTransmission(servoslave_address); // transmit to device
    Wire.send(numServo);               // servo register to start from
    Serial.println(numServo);
    float degre=((deg/180.0)*255.0);    // transform in format compatible with servo controller
    int degree=int(degre);
    Wire.send(degree);           // send 12 bytes of data
    Wire.endTransmission();    // stop transmitting 
    delay(1);                  // waits
  } 

void moveServoSet(int numServoStart, int numServoEnd, int valServo[12]) {
    Wire.beginTransmission(servoslave_address); // transmit to device
    Wire.send(numServoStart);               // servo register to start from
    for (int j=numServoStart;j<=numServoEnd;j++) {
      float degre=valServo[j]/180.0*255.0;
      int degree=int(degre);
      Wire.send(degree);           // send 12 bytes of data
      }
    Wire.endTransmission();    // stop transmitting 
    delay(1);                  // waits 

    }

Tags: