沼津高専 電子制御工学科 | ||||||
| ||||||
改訂記録 | ||||||
---|---|---|---|---|---|---|
版数 | 作成日 | 作成者 | 承認 | 改訂内容 | ||
A01 | 2004.04.28 | 瀬川 | 牛丸 | 初版 |
目次
本ドキュメントは、MIRSSTNDの行動制御プログラム中で使用するPWM・ロータリーエンコーダ制御用関数の機能と、行動制御プログラム/RT-task間のInterfaceについて記したものである。
本モジュールをRT-カーネルに組み込む方法を下に示す。
both.c :タスク制御用 enc_test.c :エンコーダのテストプログラム encoder.c :エンコーダのカウントを読み込み、速度と距離の算出を行う。 encoder2.c :encoder.cに同じ(100msごとの回転角度を返す) encoder3.c :encoder.cに同じ(回転角度の合計を返す) pwm.c :pwm制御用 pwm_enc_module2.c:RTタスクモジュール pwm_stop.c :タイヤの停止プログラム system_check.c :pwm.cのAPIのチェックプログラム pwm_enc.h :タイヤやタスクのパラメータなどを定義 pwm_module.h :RTタスクモジュールの制御用パラメータの定義 rtl.mk :コンパイルオプションの定義 Makefile :メイクファイル
モジュールの削除は下記のコマンドを使用して行う。
>rmmod pwm_enc_module2
コンパイルを行って作成したファイルの削除は下記のコマンドを使用して行う。
>make clean
Makefileについて
必要があれば下記のようにMakefileを書き換える。
例えば、both.cで定義されている関数を中に含むプログラムtest.cをコンパイルして、testという名前の実行ファイルを作成したい場合は、
both.o: both.c $(CC) -O2 -Wall -c both.c test: test.c both.o $(CC) ${INCLUDE} -O2 -Wall both.o test.c -o test
また、一番上のallのところにもコンパイルしてできるファイル名を書き加えておく。(上の例の場合はboth.oとtestを書き加える。)
本モジュールと行動制御プログラムとのインターフェースには、下記の3つのFIFOを使用する。
FIFO3(ロータリーエンコーダのカウント値用)
左右のロータリーエンコーダのカウント値が書き込まれる。
データの送受の向きは、RT-タスク ---> 行動制御プログラム
FIFO4(PWM指定用)
左右のPWMデータ,実行モードが書き込まれる。
データの送受の向きは、行動制御プログラム ---> RT-タスク
FIFO5(RT-タスク制御用)
START、STOP、RESTARTの指令とRT-タスクの実行周期が書き込まれる。
データの送受の向きは、行動制御プログラム ---> RT-タスク
FIFOの使用例
int fd4; if((fd4 = open("/dev/rtf4", O_WRONLY)) < 0){ fprintf(stderr, "Error opening /dev/rtf4\n"); return 0; }また、読み込みだけならばopen()の第二引数をO_RDONLYとすればよい。
int left,right; struct pwm_speed_data pwm; pwm.l_speed = left; pwm.r_speed = right; if(write(fd4, &pwm, sizeof(pwm)) < 0){ fprintf(stderr, "Can't send a data to RT-task\n"); return 0; } close(fd4);write()で書き込んだ後はclose()で必ず閉じるようにする。
fd_set rfds; struct timeval tv; struct count_data count; int retval1, retval2; FD_ZERO(&rfds); FD_SET(fd3, &rfds); tv.tv_sec = 0; tv.tv_usec = 100000; retval1 = select(FD_SETSIZE, &rfds, NULL, NULL, &tv); if(retval1 > 0){ while(1){ read(fd1, &count, sizeof(count)); retval2 = select(FD_SETSIZE, &rfds, NULL, NULL, &tv); if(retval2 == 0) break; } }
int fifo4=4; int pwm_data_l1,pwm_data_r1; struct pwm_speed_data pwm; while((err = rtf_get(fifo4, &pwm, sizeof(pwm))) == sizeof(pwm)){ pwm_data_l1 = pwm.l_speed; pwm_data_r1 = pwm.r_speed; }これで、先ほどwrite()で書き込まれたpwm.l_sppedとpwm.r_speedをRTタスク側の変数に代入することができる。
int fifo3=3; struct count_data count; rtf_put(fifo3, &count, sizeof(count));これでcountという構造体のデータがFIFOに書き込まれる。
DUTY_MODE | 指定したDUTY比でモータを回す。 |
PID_MODE | 指定した角度または距離だけ、モータを回し、その値に達したら止める。 |
LQR_MODE | 指定した角度または距離を、指定した時間で移動する。 |
void *thread_code(void *t)
メインスレッド。コマンドハンドラから渡された実行周期でPWMの制御(主にpwm.cからのデータによるモードごとの制御と、エンコーダのカウント値をFIFOに書き込む)を行う。
int my_handler(unsigned int fifo)
コマンドハンドラ。both.cでFIFOに書き込まれたスレッドの実行周期やスレッドのSTART,STOP,RESTARTの状態遷移を行う。
int init_module(void)
モジュールのロード時に最初に実行される初期化メソッド。FIFOの生成や、スレッドの生成を行う。
void cleanup_module(void)
FIFOの削除などの終了時の処理を行う。
void pwm_enco_start(int period)
ロータリーエンコーダ・PWM制御用のRT-タスクをスタートさせるための関数。実行周期を[msec]単位で引数として与える。
void pwm_enco_stop(void)
ロータリーエンコーダ・PWM制御用のRT-タスクを停止させるための関数。
void pwm_enco_restart(void)
pwm_enco_stopで停止させたRT-タスクを再スタートさせるための関数。
void encoder_data(float *l_distance, float *r_distance, float *l_speed, float *r_speed, int *br)
FIFO3から左右のロータリーエンコーダのカウント値とフラグの状態を100[ms]の周期で読み込んで、左右のタイヤの移動距離/速度とフラグの状態をポインタを使って値を返す関数。
brという変数は、PID_MODE,LQR_MODEの制御の状態を示すものであり、0なら制御の途中であり、1なら制御が完了したことを示す(Duty_Modeは常に0である)。また、distanceとspeedの単位はそれぞれ[mm]と[mm/sec]である。
void encoder_data2(float *l_angle, float *r_angle, int *br)
FIFO3から左右のロータリーエンコーダのカウント値とフラグの状態100[ms]の周期で読み込み、100msごとの左右のタイヤの回転角度[deg]をポインタを使って値を返す関数。
brという変数は、PID_MODE,LQR_MODEの制御の状態を示すものであり、0なら制御の途中であり、1なら制御が完了したことを示す(Duty_Modeは常に0である)。
void encoder_data3(float *l_angle, float *r_angle, int *br)
FIFO3から左右のロータリーエンコーダのカウント値とフラグの状態100[ms]の周期で読み込み、左右のタイヤの回転角度[deg]の合計をポインタを使って値を返す関数。
brという変数は、PID_MODE,LQR_MODEの制御の状態を示すものであり、0なら制御の途中であり、1なら制御が完了したことを示す(Duty_Modeは常に0である)。
void pwm_both_data(int l_duty, int r_duty)
左右のDutyを直接[-127〜+127]で与える関数である。l_dutyが左のタイヤ、r_dutyが右のタイヤへのdutyである。単位は共に[-]であり、前進が+、後進が−となっている。
MODEはDUTY_MODEである。
void pwm_straight(int l_distance, int r_distance)
P制御を行って左右のタイヤをそれぞれ指定した距離だけ進める関数である。l_distanceが左のタイヤの目標距離,r_distanceが右のタイヤの目標距離である。単位は共に[mm]であり、前進が+、後進が−となっている。
void pwm_rotate(int angle)
P制御を行ってその場回転を行う関数である。回転角度をangle[deg]で与え、左回転が+、右回転が-となっている。
void pwm_lqr_straight(int distance, int time)
LQRモードの直進を行う関数である。移動距離をdistance[mm]、移動時間をtime[msec]で与える。前進が+、後進が-である。
void pwm_lqr_rotate(int angle, int time)
LQRモードでその場回転を行う関数である。回転角度をangle[deg]、回転時間をtime[msec]で与える。左回転が+、右回転が-である。
void pwm_lqr_circle(int radius, int angle, int time)
LQRモードで、半径rの円を描くように移動する関数である。回転半径をradius[mm]、回転角度をangle[deg]、移動時間をtime[msec]で指定する。左周りが+、右回りが-である。
pwm.cの中で定義されている関数を使用する際は、以下のようにする必要がある。これはDUTY_MODEで0,0を書き込んだときにに、目標値やカウントをリセットする仕様になっているからである。また、これらの関数はusleep()などで間をおいて次を呼ぶ必要がある。その一例を以下に示す。
int lqr_rotate(int ang,int time){ pwm_enco_start(10); usleep(40000); pwm_lqr_rotate(ang,time); usleep(40000); count.br=0; while(1){ if(count.br==1) break; usleep(20000); } pwm_both_data(0,0); usleep(40000); pwm_enco_stop(); return 0; }このようにpwm_lqr_rotate()という関数を使うために、pwm_enco_start(10)でスレッドを10msecで動かし、時間をおいてpwm_lqr_rotae()を呼び、count.brが1だったら制御が終わったということなので、DUTY_MODEで0,0を書き込み、変数を初期化し、スレッドを止めるといった手順を踏む。
Please Input Mode Number 1:pwm_both_data check --> (no control) duty only (-127 to 127) 2:pwm_straight check --> (pid control) go straight as far as reserved distance 3:pwm_rotate check --> (pid control) rotate on the spot 4:pwm_lqr_straight check --> (lqr control) go straight as far as reserved distance 5:pwm_lqr_rotate check --> (lqr control) rotate on the spot 6:pwm_lqr_circle check --> (lqr control) rotate around a something 7:Exit This Menu Select Number =と表示されるので、1番から順にチェック行う。
タイヤが意図しない方向に回る場合は、モータの±の取りつけが逆である可能性が高い。
また、ロータリーエンコーダも逆に結線されている場合、制御が終わらないという事態に陥る場合があるので注意する。
関連文書 |
---|