MIRS2009 ソフトウェア製造仕様書

MIRS0901-SOFT-0002

 

 版数

最終更新日

作成

承認

改訂記事

A01

2009/3/1

坂代・戸塚

武藤

初版


1.目的

2.概要

3.動作の流れ

4.ヘッダ一覧

5.関数一覧

6.変数・構造体一覧

7.まとめ


1 目的

 本ドキュメントは、ソフトウェア製造仕様をまとめたものである。

 

2 概要

  

 競技1回目 通路通行動作から始まり、白線やDBの状態に応じて、各種仕掛け通過動作に切り替えていく。

 競技2回目 競技1回目と同様の動作を行う。

 

角度                     :電子コンパスを用いて取得する。

壁との距離           :超音波センサーを用いて取得する。また、タッチセンサーで衝突して測る場合もある。

              移動距離              :ロータリーエンコーダーのカウント数から移動距離を算出

 

3 動作の流れ

 

 競技プログラムは、スレッドで各種センサーの状態を常に監視し、その状況に応じてswitch-case文で関数を切り替える。

以下に、競技プログラムのフローチャートを、実際の関数名を用いて示す。

 

 

                                          fig.1 競技プログラムのフローチャート

4 ヘッダ一覧

 

ファイル名

概要

define.h

競技プログラムで使用するマクロの定義を行っている。

extern.h

extern宣言はすべてここの中で行っている。

GV.h

グローバル変数をここで宣言している。構造体の宣言もここで行っている。

struct.h

構造体の型定義をここで行っている。

midi_no.h

midiノートの番号をマクロで定義していで定義している。

 

画像処理系

 

ファイル名

概要

CVGV.h

グローバル変数の宣言を行っている。

CVextern.h

extern宣言を行っている。

CVdefine.h

数字認識に用いるマクロの定義を行っている。

 

 

5 関数一覧

 

競技アルゴリズム系

 

ファイル名

関数

概要

mirs_main.c

int main(int argc , char *argv[])

メイン関数

 

 

 

 

act.c

void act_normal()

通路通行動作を行う

void act_rota()

ロータリー通過動作を行う

void act_kobeya()

小部屋通過動作を行う

void act_hakusen()

白線動作を行う

void act_key()

鍵と扉通過動作を行う

void act_DB()

DBの読み取り(画像処理)を行う

void act_goal()

ゴール動作を行う

void act_ikidomari()

行き止まり回避動作を行う

print.c

void print_state(int state)

現在の状態を分かりやすくターミナルにプリントする(デバッグ用)

timer.c

double gettime()

経過時間(OSを起動してから)を出力

ini.c

void ini()

変数・構造体の初期化

void ini_onoff_thread()

on/off IOセンサー監視スレッドの起動

void ini_thread()

超音波センサー監視スレッド、電子コンパス監視スレッドの起動

select.c

void select_state()

白線センサーが反応した場合の条件判断

 

 

シリアル通信系

 

ファイル名

関数

概要

uss_get.c(標準)

int uss_get(int uss_num)                            (標準)

超音波センサーの値を読み取り、それを返す

 

 

serial_func.c(標準)

int set_serial_port(int fd, struct termios *oldtio)       (標準)

シリアル通信に使うファイルディスクリプタの初期設定

void close_serial_port(int fd, struct termios *oldtio)    (標準)

set_serial_portで開いたデバイスのクローズ

int put_serial_char(int fd, char c)                    (標準)

ファイルディスクリプタで指定されたデバイスに一文字転送

int put_serial_string(int fd, char *s)                  (標準)

ファイルディスクリプタで指定されたデバイスに文字列を転送

int get_serial_char(int fd)                           (標準)

ファイルディスクリプタで指定されたデバイスから一文字取得

int get_serial_char_motor(int fd)

ファイルディスクリプタで指定されたデバイスから一文字取得(実態はget_serial_charと同じ)

ave_uss.c

int ave_uss(int num, int loop)

超音波センサーの値を複数回取得し、平均値を出力する(現在使用していない)

void *uss_thread()

超音波センサーの値を一定時間毎にグローバル変数に代入するスレッド

onoff.c

void *onoff_thread()

on/off IOセンサーの値を一定時間毎にグローバル変数に代入するスレッド

dcom.c

void ini_ds()

電子コンパスの初期設定を行う

int ds_get()

電子コンパスから値を取得する

void *ds_thread()

電子コンパスの値を一定時間毎にグローバル変数に代入するスレッド

ds_conv.c

void ds_conv()

電子コンパスの値をあらかじめ決めた規則にしたがって修正

 

 

駆動系

 

ファイル名

関数

概要

send_byte.c

void send_byte(int16_t request)

モータコントロールボードに8bitの情報を送信する

 

dmotor.c

void read_byte()

モータコントロールボードから8bitの情報を受信する

void motor_right(char dir,int8_t speed)

右タイヤの制御を行う

void motor_left(char dir,int8_t speed)

左タイヤの制御を行う

int lolita(char dir)

ロータリーエンコーダーの値を返す

 

 

 

 

 

func.c

void motor_forward(int speed)

前進する

void motor_back(int speed)

後退する

void motor_stop2()

停止する

void motor_rotation(int speed,int muki)

その場回転動作をする

int motor_circle(int speed,int rad,int muki,int r,int l)

旋回動作をする

int turn_right(int speed,int rotang,int err)

右に指定角度だけその場回転する

int turn_left(int speed,int rotang,int err)

左に指定角度だけその場回転する

int motor_forward_dis(int speed,int rotdis,int err)

指定距離だけ前進する

int motor_back_dis(int speed,int rotdis,int err)

指定距離だけ後退する

int turn_collect(int speed,int err)

角度補正動作を行う

int touch_collect(int speed,int muki,int err)

タッチセンサー反応時の角度補正・位置補正動作を行う

 

 

画像処理系

 

ファイル名

関数

概要

cv_main.c

int input_DB(int *num1,char *ps1,int *num2,char *ps2,char *col_dir,int otsuflag,int smoothflag)

opencvによる数字認識を実行する

img_update.c(標準)

void img_update(char *str, int width, int height)            (標準)

画像のキャプチャ

threshold.c

void threshold(IplImage *src,IplImage *dst,int otsuflag)

画像の2値化

 

labeling.c

int labeling(IplImage *src,IplImage *dst1,IplImage *dst2,char *collect,int *centerpos)

画像のラベリング

void gettrim(IplImage *src,CvRect *rect)

数字部を内包する最小矩形の座標情報を取得

void triming(IplImage *src,IplImage *dst,CvRect rect)

指定された画像の指定された座標から指定された大きさの矩形を抽出

noisecut.c

void noisecut(IplImage *src,IplImage *dst)

画像上部の余分な部分を白で塗りつぶす

select_number.c

void select_number(IplImage *src1,IplImage *src2,int *num1,int *num2)

トリムした画像から数字を決定する

 

 

デバッグ・パフォーマンス系

 

ファイル名

関数

概要

beep.c

void beep(float midi_no,int lengh)

指定された音階・長さの音をビープ音として出す

void music_start(char fname[100],int beep_s)

ビープを用いて拡張子 .bp のファイルからメロディを流す

 

bgm_func.c

void ini_bgm()

BGM再生プログラムの初期化

void bgm_change(int num)

BGMの変更

void *bgm_thread(int num)

BGM再生のスレッド化

bgmtest.c

int main(int argc,char *argv[])

BGM再生プログラム

 

 

              関数詳細概要

 

競技アルゴリズム系

 

int main(int argc , char *argv[])

               メイン関数である。なお、実行の際に、引数をつけることにより、小部屋からはじめる、ロータリーからはじめるなどといった操作も可能としている。

競技1回目と2回目で同じ動作を行うようになっている。switch-case文で各種動作関数を使い分けることで競技をクリアする。

 

void act_normal()

               通路通行動作を行う関数である。

              超音波センサーによる壁の検知と曲り角の通行、また、タッチセンサー衝突時の回避動作などを行いながら進む。

              mirs.DBflag0ならば通常どおりの動作を行い、1ならば各種仕掛け内に進入したか否かを常に監視しながら通路通行動作を行う。

 

void act_rota()

               ロータリー通過動作を行う関数である。ロータリーの半径はあらかじめ決まっているので、右と左のモーターの速度はあらかじめ設定したものを使う。

              左回りに旋回動作を行い、右の超音波センサーを常に監視し、道を発見したら位置補正を行い脱出を試みる。

 

void act_kobeya()

             小部屋通過動作を行う関数である。小部屋へ侵入した場合、右端・中央・左端の3つの侵入方法があるため、どこから入ってきたのかを超音波センサーで判別する。

              他にもタッチセンサー衝突時の補正動作も組み込んでいる。

 

void act_hakusen()

               白線動作を行う関数である。DBMG3との距離を一定に保ち、DBを正面に捉えられるように各種補正をかける。

              また、途中で壁に引っかかってしまった場合の回避動作も組み込んでいる。

 

void act_key()

               鍵と扉通過動作を行う関数である。超音波センサーで鍵の直前まで近づき、正面タッチセンサーが反応するまで前進するといった単純なものである。

              電子コンパスの電源をドータボードから取ると、何故か赤外線センサーが反応しないので、今回は赤外線センサーは用いていない。

 

void act_DB()

               DB読み取り動作を行う関数である。内部でinput_DB関数を実行し、画像処理を行った後、かえってきた値から最も優先度の高い番号を選びそれに対応する仕掛け通過プログラムに切り替える。

              また、DB読み取りミスのたびに、少しずつ角度をずらすなどといったことも行っている。

5回失敗が続いたら数字認識の手法を少し変えて、10回失敗が続いたらさらに別の手法を用いるなどといったことも行っている。

 

void act_goal()

               ゴール動作を行う関数である。前方の白線センサー反応後、しばらく前進してすべての白線センサーが反応した場合に行う。少し前進したり少しその場回転したりなど、多少の確認動作を行った後、

              ゴールアピール動作を行い、プログラムを終了する。なお、ゴール動作は星のカー○ィのステージクリア曲に合わせて踊るといったものである。

 

void act_ikidomari()

               行き止まり回避動作を行う関数である。全ての超音波センサーが壁を検知した場合に行う。180度回転した後、分かれ道(前方と右もしくは左が道)を探し、見つかったらそこを曲がり、180度回転して

              行き止まりと判別する前の動作に戻る。

 

void print_state(int state)

               linuxのターミナルに出力する文字の色を変えられるので、それを用いて現在の状態(通路通行・ロータリー通行など)を見やすく表示するといったことを行う。

              競技会本番では無線環境が使えないため、デバッグ時のみ重宝する関数である。

 

double gettime()

               OSが起動してからのクロック数をマイクロ秒単位まで取得する関数である。これをメインプログラムのwhile文の最初と最後につけ、その差を取ることにより、プログラムの1周に要する時間が分かる。

 

void ini()

               各種変数・構造体の初期化を行う。構造体の初期化はmemset関数を用いている。

 

void ini_onoff_thread()

               on/off IOセンサー監視スレッドの起動を行う。

 

void ini_thread()

               超音波センサー監視スレッドと電子コンパス監視スレッドの起動を行う。

 

void select_state()

               白線センサーの監視を行う。

 

 

シリアル通信系

 

int uss_get(int uss_num)                           (標準)

               uss_numで指定された番号の超音波センサーの値を返す。詳しくは標準プログラムのドキュメントを参照されたし

 

int set_serial_port(int fd, struct termios *oldtio)      (標準)

               シリアル通信の初期設定を行う。ボーレート、非カノニカル通信、パリティエラーを無視、キャラクタブロック数の設定などを行う。

 

void close_serial_port(int fd, struct termios *oldtio)   (標準)

               set_serial_portで開いたポートを閉じる

 

int put_serial_char(int fd, char c)                    (標準)

               ファイルディスクリプタで指定されたデバイスに一文字転送

 

int put_serial_string(int fd, char *s)                  (標準)

               ファイルディスクリプタで指定されたデバイスに文字列を転送

 

int get_serial_char(int fd)                           (標準)

               ファイルディスクリプタで指定されたデバイスから一文字取得

 

int get_serial_char_motor(int fd)

               ファイルディスクリプタで指定されたデバイスから一文字取得

 

int ave_uss(int num, int loop)

               numで指定された番号の超音波センサーの値をloopで指定された数だけ取得し、その平均を取る。

              現在は処理速度の関係で使用していない。

 

void *uss_thread()

               超音波センサー監視スレッド

              超音波センサー13の値を一定周期でグローバル変数に代入する。sensor.ussflag1 sensor.ussflag2 sensor.ussflag3の値が

              0の場合はそれに対応した超音波センサーは監視しない。そうすることにより、必要なセンサーの監視速度を向上させることができる。

 

void *onoff_thread()

               on/off IOセンサーの値を一定周期でグローバル変数に代入する。なお、白線センサーに関しては、一瞬の反応でも見逃さないように、一回でも白線センサーが反応したら

              リセットしない限りその値を保持するようにしている。

 

void ini_ds()

               電子コンパスの初期設定を行う。 実際に送っているコマンドは  * DM FB @21 w47_74_51_41  であるこのコマンドについては別ドキュメントにて説明してある。

              他にも、シリアル通信の初期設定を行っている。

int ds_get()

               電子コンパスに現在角度を読み取るための、コマンドを送り、read関数でその値を読み取る。その値(016E0F16)を実際の角度(01-12735910)に変換してその値を返す。

              なお、電子コンパスから送られてくる値は16進表記の文字であるので、atoi関数を用いれば容易に変換できるが、処理を重くしたくないので変換条件をつくり変換した。

 

void *ds_thread()

               電子コンパスの値を一定周期でグローバル変数に代入する。そのままの値は-127359であるため、359度の次に0度となってしまい、条件式を作る際に非常に都合が悪い。

              そこで、1つ前の角度と現在の角度を比較し、その差が180度以上あった場合、0359をまたいだと考え、カウントを±1(03593590の場合)し、

 

                                          現在角度(-127359)+360×カウント数(-∞〜∞の整数)

 

              という風にし、現在の周回数も数えられるような仕様にした。1ループの間に角度が物理的に180度変化した場合にはこの方法では誤認識をしてしまうが、1ループが高速なので、

              物理的にあり得ないと想定し、この方法を用いることにした。

 

void ds_conv()

               電子コンパスの値をある規則に従い修正する。

              電子コンパスの値は、自動補正機能を用いても、若干実際の角度と違ってしまう。そこで、45度刻みに実測値と理論値を比較し、Excelを用いて45度の間を補間する。

              そうすることにより、かなり正確な角度を取得することができる。

 

 

駆動系

 

void send_byte(int16_t request)

              モータコントロールボードに8bitの情報を送信する。

 

void read_byte()

              モータコントロールボードから8bitの情報を受信する。

 

void motor_right(char dir,int8_t speed)

              右タイヤの速度と方向を指定する。正転の場合はdir=’f’ 逆転の場合はdir=’r’停止の場合はdir=’b’である。

速度はspeed(-127127)の範囲で設定できる。

 

void motor_left(char dir,int8_t speed)

              左タイヤの速度と方向を指定する。パラメータはmotor_rightと同じ。

 

int lolita(char dir)

              左右タイヤのロータリエンコーダの値を返り値として返す。

 

void motor_forward(int speed)

               speed(-127127)で指定された速度で前進をする。電子コンパスの値から、なるべく直進できるように補正も行っている。

 

void motor_back(int speed)

               speed(-127127)で指定された速度で後退する。こちらは直進補正は行っていない。

 

void motor_stop2()

               モーターを停止させる。それ以上でもそれ以下でもない

 

void motor_rotation(int speed,int muki)

               speed(-127127)で指定された速度で、mukiRIGHT(マクロで定義)なら右その場回転、LEFTなら左その場回転する。

 

int motor_circle(int speed,int rad,int muki,int r,int l)

 

int turn_right(int speed,int rotang,int err)

               speed(-127127)で指定された速度で、rotangで指定された角度(deg)だけ右その場回転する。なお、誤差は±err度である。

 

int turn_left(int speed,int rotang,int err)

               speed(-127127)で指定された速度で、rotangで指定された角度(deg)だけ左その場回転する。なお、誤差は±err度である。

 

int motor_forward_dis(int speed,int rotdis,int err)

               speed(-127127)で指定された速度で、rotdisで指定された距離(mm)だけ前進する。なお、誤差は±err度である。

int motor_back_dis(int speed,int rotdis,int err)

               speed(-127127)で指定された速度で、rotdisで指定された距離(mm)だけ後退する。なお、誤差は±err度である。

 

int turn_collect(int speed,int err)

               mirs.directionで指定されている角度と、現在の角度を比較し、最短ルートでmirs.directionの角度になるようにその場回転する。

              mirs.directionの中は、現在進むべき角度を代入することになっているので、つまりこれは進むべき角度からズレた場合の補正動作になる。

 

int touch_collect(int speed,int muki,int err)

               主に、左右のタッチセンサー反応時に用いる。壁との角度をあえて一定にし、そこから一定距離さがり、元の角度に戻ることにより、どの角度でタッチセンサーが反応しても、

同様に道幅の中央付近まで下がることができ、その後、再び左右のタッチセンサーが押されることが少なくなる。これにより、少しでも競技クリア速度を上げようという考えである。

 

 

画像処理系

 

int input_DB(int *num1,char *ps1,int *num2,char *ps2,char *col_dir,int otsuflag,int smoothflag)

               num1,num2にそれぞれ認識した数字を、ps1,ps2num1,num2の数字の相対位置(どちらが右でどちらが左か)を代入する。

              col_dirには現在DBから左右どちらに角度補正をかければいいかを代入する。

               otsuflag(0 or 1)1にすると大津の手法を用いた数字認識を行い、smoothflag(0 or 1)1にすると画像の平滑化を行うようになる。

              この2つのオプションの使い分けで、より精度の高い数字認識を目指した。大津の手法、画像の平滑化については、画像処理のドキュメントにて詳しく説明しているので参照されたし。

 

void img_update(char *str, int width, int height)            (標準)

               標準プログラムをそのまま用いている。 strで指定されたファイル名でwidth×heightのサイズでキャプチャを行う。実際にはsystem関数でuvccaptureを実行している。

              opencvにもキャプチャ関数があるが、処理落ちがひどくて現在のスペックでは使い物にならないのでこちらを用いている。

 

void threshold(IplImage *src,IplImage *dst,int otsuflag)

               画像の2値化を行っている。srcに入力画像、dstに出力画像領域を指定し、otsuflagにはinput_DBで指定したotsuflagの値を入れる。

 

int labeling(IplImage *src,IplImage *dst1,IplImage *dst2,char *collect,int *centerpos)

               画像のラベリングを行っている。srcに入力画像、dst1に出力画像領域その1dst2に出力画像領域その2を指定し、collectには左右どちらに補正をかければいいかが代入される。

              centerposにはDB中央のしきりの座標が代入される。

 

void gettrim(IplImage *src,CvRect *rect)

               srcには入力画像を指定し、rectにはsrcの数字部を内包する最小矩形の矩形情報が代入される。

 

void triming(IplImage *src,IplImage *dst,CvRect rect)

               srcrectで指定された矩形領域を切り取り、dstに出力する。

 

void noisecut(IplImage *src,IplImage *dst)

               画像のDBより上の部分を真っ白な領域にする。観客などを消し去る効果がある。

 

void select_number(IplImage *src1,IplImage *src2,int *num1,int *num2)

               src1で指定された画像から、アルゴリズムに従い、-1279までの値を決定し、num1num2に代入される。

 

 

デバッグ・パフォーマンス系

 

void beep(float midi_no,int lengh)

               midi_nomidiの音階を指定し、lenghでその音の長さを指定してビープ音を鳴らす。

 

void music_start(char fname[100],int beep_s)

               fnameで指定されたパスのファイル(.bp)を用いて、beep_sのテンポでメロディを演奏する。

 

void ini_bgm()

               bgmtestの実行 引数は0にしてあるので音はならない。

 

void bgm_change(int num)

               numで指定された番号を引数としてbgmtestをバックグラウンドで実行する。番号に対応したメロディが流れる。

              また、.bpファイルの登録は、csvファイルにパスとテンポを記述するだけでできるようにしてある。これにより、Excelを用いることができるので登録が簡単になった。

 

void *bgm_thread(int num)

               BGMを鳴らすスレッド

              どうしても音が途中で切れてしまうのであまり使えない。

 

6 変数・構造体一覧

 

変数名

概要

int ksmdis

小部屋通過時のみに用いる。小部屋内の直進距離が入る。小部屋内で曲がるとリセットされる

int ksmstddis

小部屋進入時のロータリーエンコーダーの値が入る。小部屋進入時の値を0にするための基準値 定数

int directDB

画像認識失敗後、左右どちらに角度補正すべきかが入る。

int stdrot_r

プログラム開始時の右のロータリーエンコーダーの値が入る。定数

int stdrot_l

プログラム開始時の左のロータリーエンコーダーの値が入る。定数

int motor_err_cnt

モーターとの通信エラーのカウント数が入る。

float stddis

各種パラメーターから計算された、ロータリーエンコーダー1カウントで進む距離(mm)が入る。定数

 

 

構造体名

メンバー名

概要

 

sensor_t

int uss1,uss2,uss3

超音波センサー1,2,3の値が代入される。

int ts1,ts2,ts3

タッチセンサー1,2,3の値が代入される。

int ws1,ws2,ws3

白線センサー1,2,3の値が代入される。

int ussflag1,ussflag2,ussflag3

これらを0にするとそれに対応した超音波センサーの起動を停止する。

 

 

 

 

 

 

 

 

mirs_t

int state

この値からact_normalなどといった関数のどれを用いるかが決まる

int next_state

例外処理が起きたとき、例外処理終了後にstateをどの値にするかを入れる。 退避用

int state_old

例外処理が起きたとき、例外処理終了後にstateをどの値にするかを入れる。 退避用

int next_dir

数字認識成功した場合に代入される。左右どちらに曲がればいいかが入る。

int process

act_ から始まる関数内の現在の状態(どの段階まで進んだか)が入る

int process_old

例外処理が起きたとき、例外処理終了後にprocessをどの値にするかを入れる。 退避用

int process_load

processと同じ用途だが、これはact_normal()専用

int process_load_old

act_normal()実行時に例外動作が起きたとき、例外処理終了後にprocess_loadをどの値にするかを入れる。退避用

int process_next

例外処理が起きたとき、例外処理終了後にprocessをどの値にするかを入れる。 退避用

int process_goal

processと同じ用途だが、これはact_goal()専用。

int process_white

processと同じ用途だが、これはact_hakusen()専用。

int DBflag

数字認識成功後、現在仕掛け前の通路を通行している場合は0 仕掛けに既に侵入している場合は1が入る。

int rota_deguti

ロータリー通過時のみ使う。ロータリーの出口が4つのうちどこにあるかの情報が入る

int angle_now

電子コンパスの値に360×周回数を足したものが入る。

int rot_r,rot_l

rot_rには右の、rot_lには左のロータリーエンコーダーから算出した移動距離が入る。

int rot_r_old,rot_l_old

rot_rrot_lの値を一時的に保持したい場合にrot_rrot_lの内容を代入する。

int direction

現在進むべき角度(90度刻み)が入る。

 

num_t

int num

数字認識で取得した数字(09)が入る。数字認識失敗時には -1 が入る

int pos

数字認識で取得した数字の相対位置('R' or 'L')が入る。

int sksr

取得した数字の競技での優先度の数字が入る

 

 

7 まとめ

 実際このようにドキュメントにまとめると、使っていない変数や関数があることに気づき、非常に無駄の多いコーディングであったと思った。

今度作るときはもう少し無駄を省いた綺麗なプログラムを作りたい。


 

関連文書