How is a tiny braitenberg vehicle robot made? by Alexander Weber
It weighs 17 gramms, is driven by two pager motors, powered by a small lipo cell and controlled by an 8-pin ATtiny25V. Pretty isn’t it? After reading this post you can build this amazing robot on your own. First, i want to explain what exactly braitenberg robot is?
Braitenberg vehicle : A Braitenberg vehicle is an agent that can autonomously move around. It has primitive sensors (measuring some stimulus at a point) and wheels (each driven by its own motor) that function as actuators or effectors. A sensor, on its simpler form, is directly connected to an effector, so that a sensed signal immediately produces a movement of the wheel. Depending on how sensors and wheels are connected, the vehicle exhibits different behaviors (which can be goal-oriented). This means that it appears to strive to achieve certain situations and to avoid others, changing course when the situation changes.[1]
Schematic and parts
This tiny robot has a very low component count. At least for a robot, based on a microcontroller. That has, of course, some implications. It can handle only two sensors and the motors run only in one direction. A full H-bridge for both motors would need 8 transistors and more lines to control it.
So I decided to just use a single transistor to drive the motor. That means it can only run forward. Not a big deal for a Braitenberg vehicle.
Here is the parts list. Most parts are very common.
- ATtiny25V, 2 kB flash RAM, ATTINY25V-10PU-ND
- MPC1700 3.3 voltage regulator, MCP1700-3302E/TO-ND
- 2 * light dependent resistors (LDR)
- 2 * 10 kOhm resistor
- 2 * 470 Ohm resistor
- 2 * 2n3904 transistor
- 2 * 1n4148 diode
- 100n capacitor
- 100u capacitor
- Lithium-polymer battery, 3.7 V, 100mAh, HobbyCity
- 2 * fuse holder
- 2 * pager motor
- heat shrink tubes
- rubber tube
- custom PCB
- 6-pin ISP header
Software and programming
The software is straigt forward. Reading two inputs, the light sensors, and driving two ouput lines accordingly with a PWM signal.
But it turns out, that the software needs a lot of adjustments. First you have to figure out in what range the light sensors report values. Next, check at what PWM level the robot starts to move. Maybe even adjust the directional stability.
Programming the robot in circuit via the 6-pin ISP header didn’t work out in the first place. The programmer was not able to set the lines to high that were driving the transistors. So I soldered a socket in place and can now pull off the two 470 Ohm resistors. After programming I can re-insert the resistors. Can be seen on the left critter on the picture above. A bit awkward, but it works.
Demo
You should have a very clean surface for them to run on. If you put them on a table, as I did, be sure to have very good reflexes or put a kind of fence around them. Mine dropped off the table a couple of times. Mostly no dramatic damage, but the sensors got twistet and the motors sprung out of their holders. Depending on the ambient light, the light source itself and the nature of the surface you may have to adjust the light sensors. As an example, if the surface is white and diffuses the light, then you would have to bend the sensors away from the surface, because the surface looks bright, even if the robot turns away from the light source.Issues and conclusion
There are still a couple of issues to resolve.
- Software improvements, use ADC in free running mode and use hardware PWM to drive the motors
- Place the skid in the middle of the PCB.
- There is no protection of deep discharging the battery, no idea how to solve this.
- Add a small power switch.
Links and downloads
/* -----------------------------------------------------------------------
* Title: tiny braitenberg
* Author: Alexander Weber <alex@tinkerlog.com>
* http://tinkerlog.com
* Date: 24.07.2009
* Hardware: ATtiny25v, ATtiny45 or 85 will work as well.
*
* If using a lipo-cell, never get under 2.5 V.
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define LED_BIT PB0
#define LEFT_MOTOR_BIT PB1
#define RIGHT_MOTOR_BIT PB2
#define LEFT_SENSOR_BIT PB3
#define RIGHT_SENSOR_BIT PB4
#define LEFT_OFFSET 10
#define RIGHT_OFFSET 0
static int16_t left_sensor = 0;
static int16_t right_sensor = 0;
static volatile uint8_t left_motor = 0;
static volatile uint8_t right_motor = 0;
static volatile uint8_t act_left_motor = 0;
static volatile uint8_t act_right_motor = 0;
static uint8_t count = 0;
/* -----------------------------------------------------
* ADC interrupt
* TODO: use ADC in free running mode
*/
SIGNAL(ADC_vect) {
// act_light = ADCH; // read only 8-bit
}
/* -----------------------------------------------------
* Timer0 overflow interrupt
* F_CPU 8.000.000 Hz
* -> prescaler 0, overrun 256 -> 31.250 Hz
* -> 256 steps -> 122 Hz
*
*/
SIGNAL(TIM0_OVF_vect) {
// every 256th step take over new values
if (++count == 0) {
act_left_motor = left_motor + LEFT_OFFSET;
act_right_motor = right_motor + RIGHT_OFFSET;
if (act_left_motor > 40) {
PORTB |= (1 << LEFT_MOTOR_BIT);
}
if (act_right_motor > 40) {
PORTB |= (1 << RIGHT_MOTOR_BIT);
}
}
if (count == act_left_motor) {
PORTB &= ~(1 << LEFT_MOTOR_BIT);
}
if (count == act_right_motor) {
PORTB &= ~(1 << RIGHT_MOTOR_BIT);
}
}
/*
* get_adc
* Return the 8 bit value of the selected adc channel.
*/
uint16_t get_adc(uint8_t channel) {
// ADC setup
ADCSRA =
(1 << ADEN) | // enable ADC
(1 << ADPS1) | (1 << ADPS0); // set prescaler to 8
// select channel
ADMUX = channel;
// select reference voltage
// ADMUX |= (1 << REFS0); // use internal reference
// warm up the ADC, discard the first conversion
ADCSRA |= (1 << ADSC);
while (ADCSRA & (1 << ADSC));
ADCSRA |= (1 << ADSC); // start single conversion
while (ADCSRA & (1 << ADSC)); // wait until conversion is done
return ADCW;
}
uint16_t scale_down(uint16_t v) {
v = max(300, v);
v -= 300;
// v /= 6;
v >>= 2;
v = min(120, v);
return v;
}
int main(void) {
uint8_t i = 0;
// eneable pins as output
DDRB |=
(1 << LED_BIT) |
(1 << LEFT_MOTOR_BIT) |
(1 << RIGHT_MOTOR_BIT);
// timer 0 setup, prescaler none
TCCR0B |= (0 << CS02) | (0 << CS01) | (1 << CS00);
// enable timer 0 interrupt
TIMSK |= (1 << TOIE0);
// TIMSK0 |= (1 << TOIE0);
// enable all interrupts
sei();
// intro, blink red
for (i = 0; i < 5; i++) {
PORTB |= (1 << LED_BIT);
_delay_ms(100);
PORTB &= ~(1 << LED_BIT);
_delay_ms(100);
}
_delay_ms(5000);
while (1) {
/* motor starts with 5
for (i = 0; i < 15; i++) {
right_motor = i * 10;
left_motor = i * 10;
_delay_ms(5000);
right_motor = 0;
left_motor = 0;
PORTB |= (1 << LED_BIT);
_delay_ms(500);
PORTB &= ~(1 << LED_BIT);
}
*/
// sensor reads from ~100 to 800
left_sensor = get_adc(2);
right_sensor = get_adc(3);
// scale down
left_sensor = scale_down(left_sensor);
right_sensor = scale_down(right_sensor);
// give some visual feedback
if (abs(left_sensor - right_sensor) < 10) {
PORTB |= (1 << LED_BIT);
}
else {
PORTB &= ~(1 << LED_BIT);
}
// aggression
right_motor = left_sensor;
left_motor = right_sensor;
// love
// right_motor = 120 - right_sensor;
// left_motor = 120 - left_sensor;
// fear
// right_motor = right_sensor;
// left_motor = left_sensor;
_delay_ms(10);
}
return 0;}
Related posts:
- Stanford Students Claim Altitude Record For Tiny, Autonomous Airplanes
- How a Drawdio is made?
- The ROPID Robot
- Öğrenebilen robot kol.
- Cockroach-Inspired hexapod Robot



0%