New sensor for RoboBall

From RoboWiki
Revision as of 01:00, 10 April 2009 by Robot (talk | contribs) (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 ...)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

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.