At about 1500L today the insectoid walked for its first time. It took several hours to get there. I started off by spending a while in vain trying to use my board programmer with my Mac with the parallel-to-USB adapter I had recently acquired. No such luck. I thought about just calling it a day with the accomplishment being to order a USB board programmer, but I decided that would be lame, so it was back to the ancient beast of a machine that lacks wireless… After much staring at the micro-controller’s data sheet, tinkering with the components, experimental programming, and furrowing of brow, the machine finally showed signs of life. The first walk was sort of a drunk walk and more than a little disappointing. Several iterations later, both in code and hardware, I had something that I was happy to file under A Good Day’s Work.
It had some traction issues on the tile floor.
The servo controlling the center legs doesn’t seem to have enough power to fully tilt the chassis.
And some combination of calibration issues and traction issues are causing drift to one side in the gait.
Switching to sharper movements will not by itself save the day. An aerial view illustrates that the legs are not swinging symmetrically.
A little bit of servo calibration later and things are more symmetrical. This is also using a simultaneous swinging of the leg pairs, with unclear consequences.
Now we’re back to non-simultenous leg movement with no apparent degradation in performance.
Some surround-shots of the action…
And finally I struck on the idea of removing the rubber feet from the center legs to counter the problem of them dragging due to inadequate lift power coming from the servo…
The code is not that complex once you understand the relevant bits of configuring the micro-controller. The system has one 16-bit timer, one 8-bit timer, and two output channels hung off of each. See earlier posts for more detail. One of my main disappointments by the end of the exercise was the realization that if you’re stuck using an 8-bit timer to generate a PWM signal for a servo then even the best possible configuration of pre-scaler and TOP value does not give you much granularity which leaves you with jerky motion as your only option. I also learned the hard way that the registers used for specifying the TOP value for counters are used constantly, not latched at configuration time, which when you consider that the only way to specify a custom value of TOP for the 8-bit output channels is to put it in one of the output-compare registers, you find that you lose one of your channels which seems a bit miserly to me.
I am relieved that everything seems to work, at least to a degree. I feel lucky that the micro-controller I chose gave me enough leeway to do what I wanted, if not entirely beautifully. All these years later I could probably get a much nicer one than I initially bought. I wonder if more powerful servos would do the trick for the tilting problem. Or maybe I need to run more power to them because the strain is overloading the batteries and causing a voltage drop? I suppose I could test this by hooking up to a wall power supply. In any case, there is still an infinite amount of playing to be done, but today marks a major milestone and is cause for celebration.
[sourcecode language=”cpp”]
#include <avr/io.h>
#define F_CPU 8000000
#include <util/delay.h>
int configure_pwm() {
DDRB = (1 << PB4) | (1 << PB3);
DDRD = (1 << PD5);
OCR0B = 10;
OCR1B = 1500;
OCR1A = 1500;
// 16-bit timer configuration
ICR1 = 20000;
TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
// 8-bit timer configuration
OCR0A = 164;
TCCR0A = (1 << COM0A1) | (1 << COM0B1) | (1 << WGM01) | (1 << WGM00);
TCCR0B = (1 << WGM02) | (1 << CS02) | (1 << CS00);
return 1;
}
int left_pair_forward() {
OCR1B = 1800;
return 1;
}
int left_pair_back() {
OCR1B = 1000;
return 1;
}
int right_pair_forward() {
OCR1A = 1000;
return 1;
}
int right_pair_back() {
OCR1A = 1800;
return 1;
}
int tilt_left() {
OCR0B = 8;
return 1;
}
int tilt_right() {
OCR0B = 12;
return 1;
}
int pause(int duration) {
int i = 0;
for (i = 0; i < duration; ++i) _delay_ms(20);
}
int walk() {
while (1) {
tilt_right();
pause(10);
left_pair_forward();
pause(50);
right_pair_back();
pause(50);
tilt_left();
pause(10);
right_pair_forward();
pause(50);
left_pair_back();
pause(50);
}
return 1;
}
int main(void)
{
int i = 0;
configure_pwm();
walk();
}
[/sourcecode]