目次

路面センサーにカメラを利用

 MCRでは、床面の白線に従ってマシンを走行させます。

 路面センサーを携帯電話やスマートフォンで
 使われるカメラを利用して走行させます。

 床面の白線さえ認識できれば、センサーはカメラ
 であろうが、赤外線センサーであろうが構わない
 のです。

 カメラには、人工網膜チップを採用している
 Game Boy Cameraを使います。




 人工網膜チップは、光の感度を人間の視覚に近く
 してあるので、扱いやすいと言われています。

 Game Boy Camera(GBC)から、情報を取得するには
 Arduinoを使います。

 車にのせて走行しなければならないので、GBC
 Arduino、AMFORTHと並べて板に固定。
 マシンに載せると、以下。



 左の紫色ワイヤーがのびている基板がGBC。
 その右にArduino、AMFORTH基板を続けて配置。

 Arduinoのプログラムは、以下。

/*
  Game Boy Camera handling (GBCY)

  Pin assignment

  PORTB
    PB.B5
    PB.B4 VBout   <input>
    PB.B3 Sensor3 <output>
    PB.B2 Sensor2 <output>
    PB.B1 Sensor1 <output>
    PB.B0 Sensor0 <output>

  PORTC
    PC.B5 SIN (GBC) <output>
    PC.B4 LOAD(GBC) <output>
    PC.B3 XRST(GBC) <output>
    PC.B2 XCK (GBC) <output>
    PC.B1 Read(GBC) <input>
    PC.B0 Vout(GBC) <input>

  PORTD
    PD.B7
    PD.B6
    PD.B5
    PD.B4 START(GBC) <output>
    PD.B3
    PD.B2
    PD.B1 TxD
    PD.B0 RxD

*/
#include <MsTimer2.h>

#define OFF 0
#define ON  OFF+1

#define MASK0F  0x0f
#define MASKF0  0xf0
#define MASKFF  0xff
#define MASK400 0x400

#define START_BIT 4

#define SIN_BIT  5
#define LOAD_BIT 4
#define XRST_BIT 3
#define XCK_BIT  2
#define READ_BIT 1

#define LED_BIT 5
#define CMP_BIT 4

#define PIXLAST 8064

#define ALL_BLACK   0
#define ALL_WHITE   1
#define LEFT_WHITE  2
#define RIGHT_WHITE 3
#define CENTER      4
#define TINY_RIGHT  5
#define RIGHT       6
#define BIG_RIGHT   7
#define TINY_LEFT   8
#define LEFT        9
#define BIG_LEFT    10
#define BOTH_WHITE  11
#define ILLEAGAL    12

boolean uflag ;
boolean aflag ;
boolean tflag ;

byte tmp ;
byte xcnt ;
word scnt ;

char sbuf[8];
byte sindex ;
char cmd ;

byte params[8];
byte rnum ;
byte lnum ;

word gbc[64];  /* raw data */
word gbcs[64]; /* shifted raw data */
char bgdat[64];

char msg[6] ;

word rcounter ; /* raster counter */

word rx ; /* line begin pixel */
word ry ; /* line finish pixel */

byte sum_cnt   ; /* edge counter */
byte left_loc  ; /* left edge location */
byte right_loc ; /* right edge location */
  
byte bport ;

void rs_putchar(char x)
{
  Serial.write(x);
}

void rs_puts(char *x)
{
  while ( *x != 0 ) {
    rs_putchar( *x ) ;
    x++ ;
  }
}

void crlf()
{
  rs_putchar('\r');
  rs_putchar('\n');
}

void show_help()
{
  rs_puts("? help");              crlf();
  rs_puts("A enable auto");       crlf();
  rs_puts("a disable auto");      crlf();
  rs_puts("P set parameters");    crlf();
  rs_puts("p show parameters");   crlf();
  rs_puts("G get graphic data");  crlf();
  rs_puts("S show graphic data"); crlf();
}

byte get_hex(char x)
{
  byte result ;
  /* default */
  result = 0 ;
  /* convert */
  if ( '0' <= x && x <= '9' ) { result = x - '0' ; }
  if ( 'A' <= x && x <= 'F' ) { result = x - 'A' + 10 ; }
  if ( 'a' <= x && x <= 'f' ) { result = x - 'a' + 10 ; }

  return result ;
}

char get_asc(byte x)
{
  char result ;
  /* default */
  result = '0' ;
  /* convert */
  if ( x < 10 ) { result = x + '0' ; }
  else          { result = x - 10 + 'A' ; }

  return result ;
}

void put_sin(boolean x)
{
  if ( x ) { PORTC |=  (1 << SIN_BIT); }
  else     { PORTC &= ~(1 << SIN_BIT); }
  /* delay */
  delayMicroseconds(1);
}

void put_load(boolean x)
{
  if ( x ) { PORTC |=  (1 << LOAD_BIT); }
  else     { PORTC &= ~(1 << LOAD_BIT); }
}

void put_rst(boolean x)
{
  if ( x ) { PORTC |=  (1 << XRST_BIT); }
  else     { PORTC &= ~(1 << XRST_BIT); }
  /* delay */
  delayMicroseconds(1);
}

void put_xck(boolean x)
{
  if ( x ) { PORTC |=  (1 << XCK_BIT); }
  else     { PORTC &= ~(1 << XCK_BIT); }
  /* delay */
  delayMicroseconds(1);
}

void put_start(boolean x)
{
  if ( x ) { PORTD |=  (1 << START_BIT); }
  else     { PORTD &= ~(1 << START_BIT); }
  /* delay */
  delayMicroseconds(1);
}

void rst_gbc(void)
{
  /* LOW */
  put_rst(OFF);
  /* XCK : H */
  put_xck(ON);
  /* XCK : L */
  put_xck(OFF);
  /* HIGH */
  put_rst(ON);
}

void start_gbc(void)
{
  /* HIGH */
  put_start(ON);
  /* XCK : H */
  put_xck(ON);
  /* XCK : L */
  put_xck(OFF);
  /* LOW */
  put_start(OFF);
}

void send_params(word x)
{
  word xtmp ;
  byte i ;
  /* copy */
  xtmp = x ;
  /* default */
  put_load(OFF);
  /* loop */
  for ( i = 0 ; i < 11 ; i++ ) {
    /* impress data */
    put_sin(OFF);
    if ( xtmp & MASK400 ) { put_sin(ON); }
    /* load */
    if ( i == 10 ) { put_load(ON); }
    /* XCK : H */
    put_xck(ON);
    /* shift */
    xtmp <<= 1;
    /* XCK : L */
    put_xck(OFF);
  }
  /* default */
  put_sin(OFF);
  put_load(OFF);
}

void init_gbc(void)
{
  byte i ;
  /* reset */
  rst_gbc();
  /* send parameters */
  for ( i = 0 ; i < 8 ; i++ ) { send_params( (i << 8) | *(params+i) ) ; }
}

boolean get_read()
{
  boolean result ;
  /* default */
  result = OFF ;
  /* judge */
  if ( PINC & (1 << READ_BIT) ) { result = ON ; }

  return result ;
}

boolean is_range(word x)
{
  boolean result ;
  /* default */
  result = OFF ;
  /* judge */
  if ( rx <= x && x < ry ) { result = ON ; }

  return result ;
}

void clear_gdat()
{
  byte i ;
  for ( i = 0 ; i < 64 ; i++ ) { *(gbc+i) = 0  ; }
}

void calc_location()
{
  byte i ;
  byte j ;
  word r0 ;
  word r1 ;
  byte xsum ;
  byte rl ;
  byte ll ;
  boolean sflag ;
  /* store shift data */
  *(gbcs+0) = 0 ; 
  /* calculate difference */
  for ( i = 0 ; i < 64 ; i++ ) {
    /* default */
    *(bgdat+i) = -1 ;
    /* temporary */
    r0 = *(gbc+i) ;
    r1 = *(gbcs+i) ;
    /* judge */
    if ( r0 > r1 )  { *(bgdat+i) = 1 ; }
    if ( r0 == r1 ) { *(bgdat+i) = 0 ; }
  }
  /* count (negative) */
  xsum = 0 ;
  for ( i = 0 ; i < 64 ; i++ ) {
    if ( *(bgdat+i) < 0 ) { xsum++ ; }
  }
  /* no change point */
  rl = 0 ; /* right */
  ll = 0 ; /* left  */
  /* one change point */
  if ( xsum == 1 ) {
    sflag = OFF ;
    for ( i = 0 ; i < 64 ; i++ ) {
      /* found */
      if ( *(bgdat+i) == -1 ) {
        sflag = ON ;
        ll = i ;
      }
      /* exit */
      if ( sflag == ON ) break ;
    }
  }
  /* multi change point */
  if ( xsum > 1 ) {
    /* set fixed value */
    xsum = 2 ;
    /* left */
    sflag = OFF ;
    for ( i = 0 ; i < 64 ; i++ ) {
      /* found */
      if ( *(bgdat+i) == -1 ) {
        sflag = ON ;
        ll = i ;
      }
      /* exit */
      if ( sflag == ON ) break ;
    }
    /* right */
    sflag = OFF ;
    for ( i = 0 ; i < 64 ; i++ ) {
      /* revese */
      j = 63 - i ;
      /* found */
      if ( *(bgdat+j) == -1 ) {
        sflag = ON ;
        rl = j ;
      }
      /* exit */
      if ( sflag == ON ) break ;
    }
  }
  /* result */
  sum_cnt   = xsum ;
  left_loc  = ll ;
  right_loc = rl ;
}

void get_gbc(byte x)
{
  word ii ;
  byte j ;
  word rawx ;
  /* clear raw data and binary data */
  clear_gdat();
  /* initialize GBC */
  init_gbc();
  /* start */
  start_gbc();
  rcounter = 0 ;
  /* send dummy clock (10 clocks) */
  for ( ii = 0 ; ii < 10 ; ii++ ) {
    /* XCK : H */
    put_xck(ON);
    /* XCK : L */
    put_xck(OFF);
    /* judge */
    if ( get_read() == ON ) {
      rcounter = ii ;
      break ;
    }
  }
  /* show READ location */
  show_value( rcounter );
  crlf();
  /* skip fist line */
  for ( ii = 0 ; ii < 127 ; ii++ ) {
    /* XCK : H */
    put_xck(ON);
    /* XCK : L */
    put_xck(OFF);
  }
  /* line handling */
  j = 0 ;
  for ( ii = 0 ; ii < PIXLAST ; ii++ ) {
    /* XCK : H */
    put_xck(ON);
    /* XCK : L */
    put_xck(OFF);
    /* get raw data */
    rawx = analogRead(0) ;
    /* judge target line */
    if ( is_range(ii+128) == ON ) {
      /* debug */
      show_value( rawx );
      putchar(' ');
      if ( (ii % 8) == 7 ) { crlf() ; }
      /* store */
      if ( (ii & ON) == ON ) {
        /* store raw data */
        *(gbc+j) = rawx ;
        /* store shifted raw data */
        if ( j ) { *(gbcs+j) = *(gbc+j-1) ; }
        /* increment */
        j++ ;
      }
    }
    /* judge */
    if ( j > 127 ) break ;
  }
  /* get change point location */
  calc_location();
}

void show_value(word x)
{
  /* delimiter */
  *(msg+5) = '\0' ;
  /* separate */
  for ( int jj = 4 ; jj > -1 ; jj-- ) {
    *(msg+jj) = get_asc( x % 10 ) ;
    x /= 10 ;
  }
  /* zero surpress */
  if ( *(msg+0) == '0' ) {
    *(msg+0) = ' ' ;
    if ( *(msg+1) == '0' ) {
      *(msg+1) = ' ' ;
      if ( *(msg+2) == '0' ) { *(msg+2) = ' ' ; }
    }
  }
  /* show */
  rs_puts( msg );
}

/* MtTimer2 interrupt handler */
void  update_trigger(void)
{
  send_led( xcnt & ON );
  xcnt++ ;
  if ( (xcnt % 4) == 3 ) { tflag = ON ; }
}

void  send_led(byte x)
{
  if ( x ) { PORTB |=  (1 << LED_BIT); }
  else     { PORTB &= ~(1 << LED_BIT); }
}

void show_gdat()
{
  byte i ;
  for ( i = 0 ; i < 64 ; i++ ) {
    show_value( *(gbc+i) );
    if ( (i % 8) == 7 ) { crlf(); }
  }
  crlf();
}

byte gen_sensor_nibble()
{
  byte result ;
  byte ws ;
  word sval ;
  /* default */
  result = ILLEAGAL ;
  sval = *(gbc+32) ;
  /* calculate wight center */
  ws = (left_loc + right_loc) >> 1 ;
  /* ALL_BLACK or ALL_WHITE */
  if ( sum_cnt == 0 ) {
    result = ALL_WHITE ;
    if ( sval < 90 ) { result = ALL_BLACK ; }
  }
  /* LEFT_WHITE or RIGHT_WHITE */
  if ( sum_cnt == 1 ) {
    result = LEFT_WHITE ;
    if ( ws > 49 ) { result = RIGHT_WHITE ; }
  }
  /* */
  if ( sum_cnt == 2 ) {
    /* judge CENTER */
    if ( 28 < ws && ws < 35 ) { result = CENTER ; }
    /* judge RIGHT side */
    if ( 34 < ws && ws < 38 ) { result = TINY_RIGHT ; }
    if ( 37 < ws && ws < 44 ) { result = RIGHT ; }
    if ( 43 < ws && ws < 50 ) { result = BIG_RIGHT ; }
    /* judge LEFT side */
    if ( 25 < ws && ws < 29 ) { result = TINY_LEFT ; }
    if ( 18 < ws && ws < 26 ) { result = LEFT ; }
    if ( 11 < ws && ws < 19 ) { result = BIG_LEFT ; }
    /* judge both end line */
    if ( left_loc < 15 && 45 < right_loc ) { result = BOTH_WHITE ; }
  }

  return result ;
}

void  impress_information()
{
  bport = PORTB & MASKF0 ;
  bport |= gen_sensor_nibble();
  PORTB = bport ;
}

void  show_info()
{
  byte info ;
  /* get information */
  info = gen_sensor_nibble();
  /* show */
  switch ( info ) {
    case ALL_BLACK   : rs_puts("B");   break;
    case ALL_WHITE   : rs_puts("W");   break;
    case LEFT_WHITE  : rs_puts("LW");  break;
    case RIGHT_WHITE : rs_puts("RW");  break;
    case CENTER      : rs_puts("C");   break;
    case TINY_RIGHT  : rs_puts("R");   break;
    case RIGHT       : rs_puts("RR");  break;
    case BIG_RIGHT   : rs_puts("RRR"); break;
    case TINY_LEFT   : rs_puts("L");   break;
    case LEFT        : rs_puts("LL");  break;
    case BIG_LEFT    : rs_puts("LLL"); break;
    case BOTH_WHITE  : rs_puts("WW");  break;
    default          : rs_puts("??");  break;
  }
  /* new line */
  crlf();
}

/* initialilze */
void setup()
{
  /* initialize serial port */
  Serial.begin(9600);
  sindex = 0 ;
  /* pin configuration */
  PORTB = 0b00010000 ; /* PB4 : pull up */
  PORTC = 0b00001000 ; /* PC3 reset */
  PORTD = 0b00000001 ;
  /* pin direction */
  DDRB  = 0b11101111 ; /* PB4 : input , others : outputs */
  DDRC  = 0b11111100 ; /* PC1 and PC0 are inputs , others : output*/
  DDRD  = 0b11111110 ; /* PD4 = GBC start , PD1 = TxD PD0 = RxD */
  /* set flags */
  uflag = OFF ;
  aflag = ON ;
  tflag = OFF ;
  /* others */
  scnt = 0 ;
  xcnt = 0 ;
  /* initialize */
  clear_gdat();
  *(params+0) = 0x80 ; *(params+1) = 0x03 ;
  *(params+2) = 0x00 ; *(params+3) = 0x0F ;
  *(params+4) = 0x01 ; *(params+5) = 0x00 ;
  *(params+6) = 0x01 ; *(params+7) = 0x21 ;
  /* target line number */
  lnum = 32 ;
  /* target pixel number */
  rx = (lnum << 7) ;
  ry = rx + 128 ;
  /* 500ms period */
  MsTimer2::set(500,update_trigger);
  /* enable */ 
  MsTimer2::start();
}

/* endless loop */
void loop()
{
  byte i ;
  char msgx[5];
  /* command interpreter */
  if ( uflag == ON ) {
    /* clear flag */
    uflag = OFF ;
    /* new line */
    crlf();
    /* get command */
    cmd = *(sbuf+0) ;
    /* help */
    if ( cmd == '?' ) { show_help() ; }
    /* enable auto */
    if ( cmd == 'A' ) { aflag = ON ; }
    /* disabel auto */
    if ( cmd == 'a' ) { aflag = OFF ; }
    /* set parameter */
    if ( cmd == 'P' ) {
      /* get register number */
      rnum = get_hex( *(sbuf+1) ) ;
      /* get parameter */
      tmp = get_hex( *(sbuf+2) ) ;
      tmp <<= 4 ;
      tmp |= get_hex( *(sbuf+3) ) ;
      /* store */
      *(params+rnum) = tmp ;
    }
    /* show parameter */
    if ( cmd == 'p' ) {
      /* fixed */
      *(msgx+4) = '\0' ;
      *(msgx+1) = ':' ;
      /* parameters */
      for ( i = 0 ; i < 8 ; i++ ) {
        /* register number */
        *(msgx+0) = get_asc( i ) ;
        /* parameters */
        *(msgx+2) = get_asc( (*(params+i) >> 4) & MASK0F ) ;
        *(msgx+3) = get_asc( *(params+i) & MASK0F ) ;
        /* show */
        rs_puts( msgx );
        /* hexadecimal */
        rs_putchar('h');
        /* space */
        rs_putchar(' ');
      }
      /* new line */
      crlf();
    }
    /* get graphic data code */
    if ( cmd == 'G' ) {
      /* message */
      rs_puts("Start "); crlf();
      /* perform */
      get_gbc(lnum);
      /* message */
      rs_puts("Complete !"); crlf();
    }
    /* show graphic data */
    if ( cmd == 'S' ) {
      /* raw data */
      show_gdat();
      /* show information */
      show_info();
      /* impress */
      impress_information();
    }
  }
  /* auto */
  if ( tflag == ON ) {
    /* clear flag */
    tflag = OFF ;
    /* perform */
    if ( aflag == ON ) {
      /* perform */
      get_gbc(lnum);
      /* impress */
      impress_information();
    }
  }
}

/* receive interrupt */
void serialEvent()
{
  char ch;

  if ( Serial.available() > 0 ) {
    /* get 1 character */
    ch = Serial.read();
    /* store */
    *(sbuf+sindex) = ch ;
    /* increment */
    sindex++ ;
    /* judge */
    if ( ch == '\r' ) {
      sindex = 0  ; 
      uflag  = ON ;
    }
  }
}

 GBCから取得した路面の情報は、次のコードに変換しています。

 0 ALL_BLACK
 1 ALL_WHITE
 2 LEFT_WHITE
 3 RIGHT_WHITE
 4 CENTER
 5 TINY_RIGHT
 6 RIGHT
 7 BIG_RIGHT
 8 TINY_LEFT
 9 LEFT
10 BIG_LEFT
11 BOTH_WHITE
12 ILLEAGAL

 4ビット情報を、AMFORTHに入力できるようにします。

 10ピンケーブルを利用し、接続は以下。

 1 Vcc   : Vcc
 2 
 3 
 4 
 5 
 6 GBC_3 : PORTC.B3
 7 GBC_2 : PORTC.B2
 8 GBC_1 : PORTC.B1
 9 GBC_0 : PORTC.B0
10 GND   : GND

 AMFORTHは、次の制御基板に接続。



 モータは12Vの産業用を使ってます。

 MCRでは、単3アルカリ電池あるいは2次
 電池8本との規定があるので、DCDC変換
 器で12Vまで昇圧。

 タイヤの後方にある基板に、DCDC変換器を
 配置してあります。この位置に配置すると
 走行中にショートなどの危険が減ります。

 モータ制御基板とカメラをまとめて配置する
 ことを考えて、ダイソーから部材を入手。



 カメラ用の穴をあけて、Arduino基板を配置して
 いきます。Arduino基板は、2枚使い、片方には
 AmForthを入れておきます。



 左からGBC、Arduino基板、AmForth基板と並べて
 ボックスの下に電池を置けば、安定します。


目次

inserted by FC2 system