It's a good idea to monitor the battery voltage, especially with LiPO chemistries. NOTE: Monitoring of batteries in
software is no replacement for hardware-based monitoring.
A simple check at startup can be implemented, which stops the robot from starting up. This piece of code could be
inserted into the beginning of the setup() function, and will stop the robot from starting up if the voltage is
less than 10V. It requires that an AX-12 servo is connected, and set to ID 1:
// wait, then check the voltage (LiPO safety)
delay (1000);
float voltage = (ax12GetRegister (1, AX_PRESENT_VOLTAGE, 1)) / 10.0;
Serial.print ("System Voltage: ");
Serial.print (voltage);
Serial.println (" volts.");
if (voltage < 10.0)
while(1);
A more complete example can be found in the repository, as
When using a Commander, you can easily change between gaits and speeds. This same code could be used on other robots:
// above setup/loop:
int multiplier;
#define RIPPLE_SPEED 1
#define AMBLE_SPEED 3
#define TRIPOD_SPEED 4
#define TOP_SPEED 9 //8-9 for ax-12, 10ish for ax-18F
// end of setup():
multiplier = RIPPLE_SPEED;
// loop() function:
void loop(){
// take commands
if(command.ReadMsgs() > 0){
digitalWrite(0,HIGH-digitalRead(0));
// select gaits
if(command.buttons&BUT_R1){ gaitSelect(RIPPLE_SMOOTH); multiplier=RIPPLE_SPEED;}
if(command.buttons&BUT_R2){ gaitSelect(AMBLE_SMOOTH); multiplier=AMBLE_SPEED;}
if(command.buttons&BUT_R3){ gaitSelect(RIPPLE); multiplier=RIPPLE_SPEED;}
if(command.buttons&BUT_L4){ gaitSelect(AMBLE); multiplier=AMBLE_SPEED;}
if(command.buttons&BUT_L5){ gaitSelect(TRIPOD); multiplier=TRIPOD_SPEED;}
if(command.buttons&BUT_L6){ gaitSelect(TRIPOD); multiplier=TOP_SPEED;}
// set movement speed
Xspeed = multiplier*(command.walkV);
if((command.buttons&BUT_LT) > 0)
Yspeed = multiplier*(command.walkH/2);
else
Rspeed = -(command.walkH)*multiplier/300.0;
bodyRotY = (((float)command.lookV))/250.0;
if((command.buttons&BUT_RT) > 0)
bodyRotX = ((float)command.lookH)/250.0;
else
bodyRotZ = ((float)command.lookH)/250.0;
}
// if our previous interpolation is complete, recompute the IK
if(bioloid.interpolating == 0){
doIK();
bioloid.interpolateSetup(tranTime);
}
// update joints
bioloid.interpolateStep();
}
Issy used only 3 poses for walking, one for normal standing position, and 2 for picking up each of our pairs of legs,
but could do an unlimited number of step sizes. This was accomplished by basically parameterizing the length of the step.
Here is a sample of Issy's code, showing only the walking part in the loop:
// servo numbers
#define RIGHT_FRONT 5
#define LEFT_FRONT 6
#define RIGHT_REAR 11
#define LEFT_REAR 12
#define TILT_SERVO 13
...
void loop(){
// we read in serial port data here, to control Issy via the XBEE radio....
// update walking gait parameters
if(bioloid.interpolating == 0){
if((rstep != 0) || (lstep != 0)){ // do nothing if we are stopped
if(walkMode == 0){
bioloid.loadPose(walk0);
bioloid.setNextPose(RIGHT_FRONT,bioloid.getNextPose(RIGHT_FRONT) - rstep);
bioloid.setNextPose(LEFT_FRONT,bioloid.getNextPose(LEFT_FRONT) - lstep);
bioloid.setNextPose(RIGHT_REAR,bioloid.getNextPose(RIGHT_REAR) + rstep);
bioloid.setNextPose(LEFT_REAR,bioloid.getNextPose(LEFT_REAR) + lstep);
walkMode = 1;
bioloid.interpolateSetup(75);
}else if(walkMode == 1){
bioloid.loadPose(walkend);
bioloid.setNextPose(RIGHT_FRONT,bioloid.getCurPose(RIGHT_FRONT));
bioloid.setNextPose(LEFT_FRONT,bioloid.getCurPose(LEFT_FRONT));
bioloid.setNextPose(RIGHT_REAR,bioloid.getCurPose(RIGHT_REAR));
bioloid.setNextPose(LEFT_REAR,bioloid.getCurPose(LEFT_REAR));
walkMode = 2;
bioloid.interpolateSetup(30);
}else if(walkMode == 2){
bioloid.loadPose(walk1);
bioloid.setNextPose(RIGHT_FRONT,bioloid.getNextPose(RIGHT_FRONT) + rstep);
bioloid.setNextPose(LEFT_FRONT,bioloid.getNextPose(LEFT_FRONT) + lstep);
bioloid.setNextPose(RIGHT_REAR,bioloid.getNextPose(RIGHT_REAR) - rstep);
bioloid.setNextPose(LEFT_REAR,bioloid.getNextPose(LEFT_REAR) - lstep);
walkMode = 3;
bioloid.interpolateSetup(75);
}else{
bioloid.loadPose(walkend);
bioloid.setNextPose(RIGHT_FRONT,bioloid.getCurPose(RIGHT_FRONT));
bioloid.setNextPose(LEFT_FRONT,bioloid.getCurPose(LEFT_FRONT));
bioloid.setNextPose(RIGHT_REAR,bioloid.getCurPose(RIGHT_REAR));
bioloid.setNextPose(LEFT_REAR,bioloid.getCurPose(LEFT_REAR));
walkMode = 0;
bioloid.interpolateSetup(30);
}
}else{
// stopped! put feet down!
bioloid.loadPose(walkend);
bioloid.setNextPose(RIGHT_FRONT,bioloid.getCurPose(RIGHT_FRONT));
bioloid.setNextPose(LEFT_FRONT,bioloid.getCurPose(LEFT_FRONT));
bioloid.setNextPose(RIGHT_REAR,bioloid.getCurPose(RIGHT_REAR));
bioloid.setNextPose(LEFT_REAR,bioloid.getCurPose(LEFT_REAR));
bioloid.interpolateSetup(30);
}
}
// update joints
bioloid.interpolateStep();
}
You can change the speed of the interpolation engine, by changing the BIOLOID_FRAME_LENGTH in BioloidController.h.
If using more than 18 servos, you will need to change AX12_MAX_SERVOS in ax12.h to increase the size of the pose buffers.
Alternatively, if memory is running tight, and you are using less than 18 servos, you can change this to reduce the size of
the pose buffers.
Pose header files are very simple. We are using the program memory storage utilities of AVR-Libc in order to store
the poses in the FLASH, rather than occupying RAM. Each pose is an array of integers, to put this array in program
memory it is of type PROGMEM prog_uint16_t. The first element in the array is the servo count. The servo count can be
1-255, in the example below, Issy has 12 servos:
PROGMEM prog_uint16_t stand[ ] = {12,512,512,482,542,662,362,512,512,542,482,362,662};
Each number after the servo count is the position of the servo, starting with ID number 1. Positions are 0-1023,
the same as what an AX-12 can actually take on.
Note: The upper byte of the servo count is reserved for special values that may be used later to define
special new types of poses.