New sensor for RoboBall
From RoboWiki
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)
- 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)
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).
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.