音声認識によるロボットの制御

パソコンで音声認識を行い、認識結果を赤外線送信機に送ります。そして赤外線送信機からはテレビのリモコンと同一の赤外線コードを送信することで、リモコンロボットを音声で動作させることができるようになります。

ロボット本体


簡易版

通常版と形状が異なりますが、アームを外して簡略化しただけで、基本的な回路は同じものです。

電気回路

回路図

赤外線送信機

パソコンと接続し任意のコマンドを送信できるように、送信機を用意します。


※ ICソケットだけとなっている部分は、本来マイコンが取り付けられます。

電気回路

回路図
送信機 回路図 (CE3形式 - 回路図エディタ BSchファイル)

部品
品名   個数
PICマイコン PIC16F88 1
インターフェイスIC ADM3202 (MAX232) 1
セラミック発振子 20MHz 1
3端子レギュレーター 78L05 1
赤外線LED TLN115A 1
LED   2
トランジスタ 2SC3751 1
カーボン抵抗 330Ω 2
カーボン抵抗 200Ω 1
カーボン抵抗 10Ω 1
セラミック コンデンサ 1.5μF 1
セラミック コンデンサ 0.1μF 6
電解コンデンサ 22μF 1

プログラム

TV_SignalSender.h

#case

// --------------------------------------------------------------
#include <16f88.h>
#fuses HS, NOWDT, NOPROTECT, PUT, NOBROWNOUT, NOLVP, NOCPD, MCLR, CCPB3

#device *=16

#use delay( Clock = 20000000 )
#use rs232( BAUD = 115200, XMIT=PIN_B5, RCV=PIN_B2 )

#use fast_io( A )
#use fast_io( B )

typedef short bool;

// マクロ定義
#define IR_LED     PIN_B6  // 受信確認LED
#define STATUS_LED PIN_B4  // ステータスLED
#define PWM_DUTY   0x0109L // PWM Duty (13.2usec = 1/10周期)

#define DATA_SIZE  5       // データサイズ[ byte ]

#define STATUS_REGISTER 0x03 // STATUSレジスタ
#define FSR_REGISTER    0x04 // FSR
#define INDF_REGISTER   0x00 // INDF

#bit INTERRUPT_FLAG_RECEIVE = 0x0C.5 // 受信 割り込みフラグ(16F88)


// 変数宣言
struct BitLength
{
    int high;
    int low;
};

struct CodeFormat
{
    struct BitLength startBit;
    struct BitLength oneBit;
    struct BitLength zeroBit;
};


// プロトタイプ宣言
void Init();
void BlinkStatusLed( int num );

void ReceiveSendDataOfInfraredRays();

void SendCode( int bitSum, struct CodeFormat* format, int* data );
void FlashLed( struct BitLength* length );

void ShowValue( long value );

void ShiftBit( int* data, int dataLength, int numberToShift );

TV_SignalSender.c

#include "TV_SignalSender.h"

// =====================================================================
// メイン関数
void main()
{
    // 初期化する
    Init();

    while( 1 )
    {
    }
}


// =====================================================================
// 初期化する
void Init()
{
    set_tris_a( 0b00000000 );
    set_tris_b( 0b00000101 );

    // ラッチをクリアする
    output_a( 0x00 );
    output_b( 0x00 );

    // A/D コンバーターを設定する
    setup_adc_ports( NO_ANALOGS );
    setup_adc( ADC_OFF );

    // PWMを設定する
    set_pwm1_duty( 0x0000L );
    setup_timer_2( T2_DIV_BY_1, 0x83, 1 ); // PWM周期 設定( 26.4us = 37.9KHz )
    setup_ccp1( CCP_PWM );                 // CCP動作モード設定(PWM)

    // タイマ1を設定する
    setup_timer_0( RTCC_INTERNAL | RTCC_DIV_256 );

    // 外部INTを設定する
    ext_int_edge( H_TO_L );

    // USARTの割り込みを許可する
    enable_interrupts( INT_RDA );


    // 受信確認LEDを消灯させる
    output_high( IR_LED );

    // ステータスLEDを点滅させる
    BlinkStatusLed( 2 );

    // ... ステータスLEDを点灯させる
    output_low( STATUS_LED );


    // 割り込みを許可する
    enable_interrupts( GLOBAL );
}


// =====================================================================
// USARTから 受信をした
#int_rda
void ReceiveFromUsart()
{
    // 受信データチェック
    switch( getc() )
    {
        case 0xF0: // 送信要求
        {
            // 赤外線送信データを受信する
            ReceiveSendDataOfInfraredRays();
            break;
        }

        case 0xF1: // 受信要求
        {
            // 外部INT 割り込みを許可する
            enable_interrupts( INT_EXT );
            break;
        }
    }

    // 多重割り込みに対処するため、受信フラグをクリアする
    INTERRUPT_FLAG_RECEIVE = 0;
}

// 信号を検出した
#INT_EXT
void DetectSignal()
{
    // カウンタ値を取得する
    long widthOfSignal;
    widthOfSignal = get_timer0();

    // タイマをリセットする
    set_timer0( 0x00 );


    // 受信確認LEDを点灯させる
    output_low( IR_LED );

    // ... 信号の幅を表示する
    ShowValue( widthOfSignal );

    // ... 受信確認LEDを消灯させる
    output_high( IR_LED );
}


// =====================================================================
// 赤外線送信データを受信する
void ReceiveSendDataOfInfraredRays()
{
    int i;
    int bitSum;

    struct CodeFormat format;

    int data[ DATA_SIZE ];

    // ビット数を取得する
    bitSum = getchar();

    // スタートビット幅を取得する
    format.startBit.high = getchar();
    format.startBit.low = getchar();

    // 1ビット幅を取得する
    format.oneBit.high = getchar();
    format.oneBit.low = getchar();

    // 0ビット幅を取得する
    format.zeroBit.high = getchar();
    format.zeroBit.low = getchar();


    for( i=0; i<DATA_SIZE; i++ )
    {
        // データを取得する
        data[ i ] = getchar();
    }

    // コードを送信する
    SendCode( bitSum, &format, data );
}

// =====================================================================
// コードを送信する
void SendCode( int bitSum, struct CodeFormat* format, int* data )
{
    int i;

    // 最初のデータまで ビットを動かす
    ShiftBit( data, DATA_SIZE, ( DATA_SIZE * 8 ) - bitSum );


    // スタートビットを発光する
    FlashLed( &format->startBit );

    for( i=0; i<bitSum; i++ )
    {
        // 最上位ビットが1ならば Highを送信する
        if( 0x80 & *data )
        {
            // High を発光する
            FlashLed( &format->oneBit );
        }
        else
        {
            // Low を発光する
            FlashLed( &format->zeroBit );
        }

        // 1ビット左シフトする
        ShiftBit( data, DATA_SIZE, 1 );
    }

    // ストップビットを発光する
    FlashLed( &format->startBit );
}

// LED発光 (38kHz)
void FlashLed( struct BitLength* length )
{
    int i;

    set_pwm1_duty( PWM_DUTY );

    for( i=0; i<length->high; i++ )
    {
        // プリスケーラ 256 x 0.2usec = 51.2usec
        delay_us( 51 );
    }

    //
    set_pwm1_duty( 0x0000L );

    for( i=0; i<length->low; i++ )
    {
        // プリスケーラ 256 x 0.2usec = 51.2usec
        delay_us( 51 );
    }
}


// =====================================================================
// 数値を表示する
void ShowValue( long value )
{
    const int countMax = 10;
    static int count;

    printf( "%2X ", value );

    if(countMax <= ++count )
    {
        printf( "\n\r" );
        count = 0;
    }
}


// =====================================================================

// ステータスLEDを点滅させる
void BlinkStatusLed( int num )
{
    // num は点滅回数のため2倍にする
    num <<= 1;

    for( ; num; num-- )
    {
        output_bit( STATUS_LED, num & 0x01 );
        delay_ms( 200 );
    }
}


// ビットをシフトする
void ShiftBit( int* data, int dataLength, int numberToShift )
{
    int k;
    int loop;
    int* address;

    address = data + ( DATA_SIZE - 1 );

    for( k=0; k<numberToShift; k++ )
    {
        loop = dataLength;

        // 処理速度を向上させるため、以降はインライン アセンブラ
        #asm

            // バンク0を指定する
            bcf    STATUS_REGISTER, 7

            // ... FSRレジスタに データのアドレスを設定する
            movf   address, w
            movwf  FSR_REGISTER

            // キャリーをクリアする
            bcf    STATUS_REGISTER, 0

        LOOP:

            // キャリーを含めて左にシフトする
            rlf    INDF_REGISTER, f

            // ... アドレスを左に動かす
            decf   FSR_REGISTER, f

            // 指定回数だけ 処理をくり返す
            decfsz loop, f

            goto   LOOP

        #endasm
    }
}

音声認識アプリケーション

音声認識を行い、その結果を赤外線送信機に送るためのアプリケーションです。

次のリンクからダウンロードできます。ソースコードも公開していますので、興味のある方はそちらも参照してください。

ダウンロード ≫VoiceCommander

voice-commander-src.zip ソースコード (Visual Studio 2008)

動作環境

  • Windows 98/Me/2000/XP/Vista
  • Microsoft SAPI 5.1

注意
音声認識には Microsoft SAPIを使用しているため、事前にSpeech SDKがインストールされている必要があります。

認識用の辞書定義

音声の認識に使用する語句の辞書を編集することで、任意の言葉で操作できるようになります。その辞書は、インストール先のフォルダにgrammar.xmlの名前であります。

<?xml version="1.0" encoding="UTF-8"?>
<GRAMMAR>
<RULE name="MOTION_NAME" toplevel="ACTIVE">
  <L>
    <P>/前へ進め/まえへすすめ;</P>
    <P>/止まれ/とまれ;</P>
    <P>/後ろへ下がれ/うしろへさがれ;</P>
    <P>/左へ回れ/ひだりへまわれ;</P>
    <P>/右へ回れ/みぎへまわれ;</P>
  </L>
</RULE>
</GRAMMAR>

※ このファイルを変更しても、VoiceCommanderの画面上の語句は変化しません。