Difference between revisions of "New sensor for RoboBall"
m (New page: This is a simple project you can build on your own. It allows you to detect RoboCup Junior Soccer RoboBall. You need: * 1 AVR ATtiny26L processor (unless you are an expert, use the PDIP ...) |
|||
(4 intermediate revisions by one other user not shown) | |||
Line 4: | Line 4: | ||
* 1 AVR ATtiny26L processor (unless you are an expert, use the PDIP package) | * 1 AVR ATtiny26L processor (unless you are an expert, use the PDIP package) | ||
− | * 11 phototransistors (we used L-53P3BT) | + | * 11 phototransistors (we used L-53P3BT, which is [http://home.comet.bg/datasheets/Infrared%20LED/L-53P3C(V4).pdf L-53P3C] with blue transparent lens) |
* 11 resistors (we used 10K Ohm) | * 11 resistors (we used 10K Ohm) | ||
* optionally: 1 LED and 1K Ohm resistor (to indicate the operation of the sensor) | * optionally: 1 LED and 1K Ohm resistor (to indicate the operation of the sensor) | ||
Line 10: | Line 10: | ||
* soldering and wiring | * soldering and wiring | ||
* 5V power supply (e.g. 4x rechargable AA batteries) | * 5V power supply (e.g. 4x rechargable AA batteries) | ||
+ | * programmer for AVR (for instance AVR Dragon) | ||
+ | |||
+ | [[Image:new_roboball_sensor.gif]] | ||
Connect each of the phototransistors to one ADC input of the AVR and to the ground, | Connect each of the phototransistors to one ADC input of the AVR and to the ground, | ||
Line 16: | Line 19: | ||
to the other CPU (for example ATmega128) through a short communicating cable (using shielded | to the other CPU (for example ATmega128) through a short communicating cable (using shielded | ||
cable is an advantage). | cable is an advantage). | ||
+ | |||
+ | The sensor is a low-cost solution, the price of one phototransistor is about 0.25 EUR, | ||
+ | ATtiny26L is for about 3.7 EUR, making the total price safely under 10 EUR. | ||
Then download the following code to the sensor CPU: | Then download the following code to the sensor CPU: | ||
Line 159: | Line 165: | ||
On the side of the master, use analogous program. | On the side of the master, use analogous program. | ||
+ | |||
+ | |||
+ | // in this case, the communication line is connected to pins 3,4,5 on PORTC | ||
+ | #define PDATALINE PINC | ||
+ | #define PDATAREAD PORTC | ||
+ | #define PDREADY PINC | ||
+ | |||
+ | #define BDREADY 8 | ||
+ | #define BDATALINE 16 | ||
+ | #define BDATAREAD 32 | ||
+ | |||
+ | /* read a single 16-bit unsigned integer from sensor CPU over the 3-wire communication line */ | ||
+ | uint16_t read_ir() | ||
+ | { | ||
+ | uint8_t i; | ||
+ | uint16_t x = 0; | ||
+ | uint32_t cnt; | ||
+ | |||
+ | // indicate that we are ready to read | ||
+ | PDATAREAD |= BDATAREAD; | ||
+ | // wait for the slave to get ready to send | ||
+ | cnt = 0; | ||
+ | while ((!(PDATALINE & BDATALINE)) && (cnt < 1000000)) { PDATAREAD |= BDATAREAD; cnt++; } //TODO: check for timeout | ||
+ | if (cnt == 1000000) return 65535; // when the connection is lost, receiving failed | ||
+ | |||
+ | // request the first bit of data | ||
+ | PDATAREAD &= ~BDATAREAD; | ||
+ | for (i = 0; i < 16; i++) | ||
+ | { | ||
+ | // wait until next bit is online | ||
+ | cnt = 0; | ||
+ | while ((!(PDREADY & BDREADY)) && (cnt < 1000000)) { PDATAREAD &= ~BDATAREAD; cnt++; } | ||
+ | if (cnt == 1000000) return 65535; | ||
+ | // read the next bit | ||
+ | x += ((PDATALINE & BDATALINE) / BDATALINE) << i; | ||
+ | // indicate that the data was read | ||
+ | PDATAREAD |= BDATAREAD; | ||
+ | // wait for the slave to learn that the data was read | ||
+ | cnt = 0; | ||
+ | while ((PDREADY & BDREADY) && (cnt < 1000000)) { PDATAREAD |= BDATAREAD; cnt++; } | ||
+ | // request next bit of data | ||
+ | PDATAREAD &= ~BDATAREAD; | ||
+ | } | ||
+ | |||
+ | // return the number read. | ||
+ | return x; | ||
+ | } | ||
+ | |||
+ | . . . | ||
+ | |||
+ | { | ||
+ | uint16_t n; | ||
+ | printf("rcv:"); | ||
+ | while (!terminate) | ||
+ | { | ||
+ | n = read_ir(); // read value from the sensor and print it out | ||
+ | if (n > 2000) printf("%2d:", n - 2000); // direction | ||
+ | else printf("%4d\n", n); // distance | ||
+ | } | ||
+ | } | ||
+ | |||
+ | The picture shows our prototyping board before the sensor was built: | ||
+ | |||
+ | [[Image:new_roboball_sensor.jpg]] | ||
+ | |||
+ | Remember to connect the GND of both CPUs. | ||
+ | |||
+ | [[Category:Sensors]] |
Latest revision as of 10:38, 24 January 2012
This is a simple project you can build on your own. It allows you to detect RoboCup Junior Soccer RoboBall. You need:
- 1 AVR ATtiny26L processor (unless you are an expert, use the PDIP package)
- 11 phototransistors (we used L-53P3BT, which is L-53P3C with blue transparent lens)
- 11 resistors (we used 10K Ohm)
- optionally: 1 LED and 1K Ohm resistor (to indicate the operation of the sensor)
- some testing board which you can use to build your sensor
- soldering and wiring
- 5V power supply (e.g. 4x rechargable AA batteries)
- programmer for AVR (for instance AVR Dragon)
Connect each of the phototransistors to one ADC input of the AVR and to the ground, connect each of the 10K resistors between VCC and the ADC input of the AVR. Connect LED to pin1 of CPU through 1K resistor and to ground. Connect the pins 2,3, and 4 to the other CPU (for example ATmega128) through a short communicating cable (using shielded cable is an advantage).
The sensor is a low-cost solution, the price of one phototransistor is about 0.25 EUR, ATtiny26L is for about 3.7 EUR, making the total price safely under 10 EUR.
Then download the following code to the sensor CPU:
/* Intelligent sensor for RoboSoccer Ball detection * * Implemented for AVR ATtiny26L - a low-cost CPU with 11 A2D converters * The sensor communicates using three digital lines with the main CPU. * The idea is that the 11 IR sensors are mounted around the robot. * On request, the sensor sends the location of the ball (0-22) based * on the readings from the 11 IR sensors (we used the L-53P3BT phototransistors * and 10K resistors in voltage divider configuration). * CPU is powered with 5V. * * Compiled with AVR Studio 4.16, WINAVR 20090313. You need to link this * together with a2d.c from AvrLib. * * Author: Palo, ppetrovic@acm.org, April 2009 */ #include <inttypes.h> #include <avr/io.h> #include <util/delay.h> // the following is included from Procyon AVRLib // (http://hubbard.engr.scu.edu/embedded/avr/avrlib/) #include <a2d.h> uint16_t val[33]; // three readings from the sensors uint16_t val2[14]; // averaged readings from the sensors uint16_t max, d0, d1, maxd1; // finding the maximum readings uint8_t dir, d; // direction index // three communication lines on the side of the slave (attiny26): PORTB 1-3 #define PDREADY PORTB #define PDATALINE PORTB #define PDATAREAD PINB #define BDREADY 2 #define BDATALINE 4 #define BDATAREAD 8 // sends a 16-bit number to the main CPU uint8_t send_ir(uint16_t x) { uint8_t i; uint32_t cnt; // if the master does not request data, do not send if (!(PDATAREAD & BDATAREAD)) return 0; // indicate that we are ready to send PDATALINE |= BDATALINE; cnt = 0; // wait for master to indicate that it is ready to receive while ((PDATAREAD & BDATAREAD) && (cnt < 1000000)) { PDATALINE |= BDATALINE; cnt++;} if (cnt == 1000000) return 0; // communication lost -> sending failed // bit-by-bit for (i = 0; i < 16; i++) { // send the next bit uint8_t tmp = PDATALINE & (~BDATALINE); PDATALINE = tmp | (x & 1) * BDATALINE; // indicate that the data is online PDREADY |= BDREADY; // wait for master to read the data cnt = 0; while ((!(PDATAREAD & BDATAREAD)) && (cnt < 1000000)) { PDREADY |= BDREADY; cnt++; } if (cnt == 1000000) return 0; // getting ready for more data PDREADY &= ~BDREADY; // wait until master gets ready as well cnt = 0; while ((PDATAREAD & BDATAREAD) && (cnt < 1000000)) { PDREADY &= ~BDREADY; cnt++; } if (cnt == 1000000) return 0; // prepare next data x >>= 1; } // clear the data line PDATALINE &= ~BDATALINE; return 1; } // sensor main function int main() { int i; uint8_t sent, trial; DDRB = 1 + BDREADY + BDATALINE; PORTB = 1; // there is a LED on pin0 of PORTB DDRA = 0; // clean the ADC port PORTA = 0; // disconnect pull-ups // setup A2D converter a2dInit(); a2dSetPrescaler(ADC_PRESCALE_DIV128); // given 8MHz CPU clock, this gives good sampling precision a2dSetReference(0); // OUCH! ATtiny26 has some of the ADMUX flags reveresed! // start-up LED flashing for (i = 0; i < 10; i++) _delay_ms(10); PORTB = 0; for (i = 0; i < 10; i++) _delay_ms(10); PORTB = 1; // sensors just sends the data forever while (1) { // three-times sample all the IR transistors for (trial = 0; trial < 3; trial++) for (dir = 0; dir < 11; dir++) val[trial * 11 + dir] = 1023 - a2dConvert10bit(dir); // compute average readings for (dir = 0; dir < 11; dir++) val2[dir + 1] = (val[dir] + val[dir + 11] + val[dir + 11]) / 3; // sensors are on a circle, so we wrap with the first and the last val2[0] = val2[11]; val2[12] = val2[1]; // consider 23 different directions: 11 in the line of sensors, and 12 between sensors max = 0; d = 0; maxd1 = 0; for (dir = 0; dir < 23; dir++) { // if the direction in the line of sensor scaled 90% is more than averege of two, it wins if (dir & 1) { d1 = val2[1 + (dir >> 1)]; d0 = (d1 * (uint16_t) 9) / (uint16_t) 10; } // otherwise take a direction between two neighboring sensors else { d0 = (val2[dir >> 1] + val2[1 + (dir >> 1)]) >> 1; d1 = d0;} // find the direction with maximum value if (d0 > max) { max = d0; maxd1 = d1; d = dir; } } // send the data (direction + value) to the master do { sent = send_ir(2000+d); } while (!sent); do { sent = send_ir(maxd1); } while (!sent); // LED flashes when master reads data PORTB ^= 1; } return 0; }
On the side of the master, use analogous program.
// in this case, the communication line is connected to pins 3,4,5 on PORTC #define PDATALINE PINC #define PDATAREAD PORTC #define PDREADY PINC #define BDREADY 8 #define BDATALINE 16 #define BDATAREAD 32 /* read a single 16-bit unsigned integer from sensor CPU over the 3-wire communication line */ uint16_t read_ir() { uint8_t i; uint16_t x = 0; uint32_t cnt; // indicate that we are ready to read PDATAREAD |= BDATAREAD; // wait for the slave to get ready to send cnt = 0; while ((!(PDATALINE & BDATALINE)) && (cnt < 1000000)) { PDATAREAD |= BDATAREAD; cnt++; } //TODO: check for timeout if (cnt == 1000000) return 65535; // when the connection is lost, receiving failed // request the first bit of data PDATAREAD &= ~BDATAREAD; for (i = 0; i < 16; i++) { // wait until next bit is online cnt = 0; while ((!(PDREADY & BDREADY)) && (cnt < 1000000)) { PDATAREAD &= ~BDATAREAD; cnt++; } if (cnt == 1000000) return 65535; // read the next bit x += ((PDATALINE & BDATALINE) / BDATALINE) << i; // indicate that the data was read PDATAREAD |= BDATAREAD; // wait for the slave to learn that the data was read cnt = 0; while ((PDREADY & BDREADY) && (cnt < 1000000)) { PDATAREAD |= BDATAREAD; cnt++; } // request next bit of data PDATAREAD &= ~BDATAREAD; } // return the number read. return x; } . . . { uint16_t n; printf("rcv:"); while (!terminate) { n = read_ir(); // read value from the sensor and print it out if (n > 2000) printf("%2d:", n - 2000); // direction else printf("%4d\n", n); // distance } }
The picture shows our prototyping board before the sensor was built:
Remember to connect the GND of both CPUs.