なお、ロータリエンコーダそのものの解説は、例えばエム・システム技研のこのサイトを参考にして下さい。
ロータリーエンコーダはモーターに組み込まれているので、ロボットの改造は必要ありません。
NXTのモータに組み込まれているロータリエンコーダは、光学式(光電式)で1周で360カウントする(すわなち1カウント=1度)エンコーダです。
ロータリエンコーダは、本来の分解能(パルス数)の最大4倍までカウントが可能ですが、ここで使用しているエンコーダは1周180パルスを2倍してカウントしています。
このプログラムはBポートとCポートのモータが720度(=2回転)と回転し、1秒間停止後に、逆方向に720度回転するプログラムです。走行体を床におけば、タイヤ2回転分の直進と後退を行います。
#include "kernel.h" #include "kernel_id.h" #include "ecrobot_interface.h" #define STOP_TIME 1000 DeclareTask(Task1); /* Task1を宣言 */ void ecrobot_device_initialize(){ /* OSEK起動時の処理(モータ停止)*/ nxt_motor_set_speed(NXT_PORT_B,0,1); nxt_motor_set_speed(NXT_PORT_C,0,1); } void ecrobot_device_terminate(){ /* OSEK終了時の処理(モータ停止)*/ nxt_motor_set_speed(NXT_PORT_B,0,1); nxt_motor_set_speed(NXT_PORT_C,0,1); } void user_1ms_isr_type2(void){} /* 直進動作の関数定義 str_enc() 左右のモータの回転角の平均値が ref_angle になるまで、 左右のモータがそれぞれ duty_l, duty_r のduty比で回転する */ void str_enc(int ref_angle, int duty_l, int duty_r){ int angle_b, angle_c, angle; // ロータリエンコーダリセット nxt_motor_set_count(NXT_PORT_B,0); nxt_motor_set_count(NXT_PORT_C,0); nxt_motor_set_speed(NXT_PORT_B,duty_r,1); nxt_motor_set_speed(NXT_PORT_C,duty_l,1); while(1){ angle_b = nxt_motor_get_count(NXT_PORT_B); angle_c = nxt_motor_get_count(NXT_PORT_C); angle = (angle_b + angle_c ) /2; //モータの平均回転角度が指定角度(前進時:以上、後退時:以下)になったらループを出る if( ref_angle >= 0){ if( angle >= ref_angle ) break; //前進時 } else { if( angle <= ref_angle ) break; //後退時 } systick_wait_ms(10); } } TASK(Task1) { float ref_angle; int duty_l, duty_r; //前進動作 ref_angle = 720; //duty比は前項の「モータの使用」で調整した値を用いる duty_l = 60; duty_r = 60; str_enc((int)ref_angle, duty_l, duty_r); //モータ停止 nxt_motor_set_speed(NXT_PORT_B,0,1); nxt_motor_set_speed(NXT_PORT_C,0,1); systick_wait_ms(STOP_TIME); //後退動作 ref_angle = -720; duty_l = -60, duty_r = -60; str_enc((int)ref_angle, duty_l, duty_r); //モータ停止 nxt_motor_set_speed(NXT_PORT_B,0,1); nxt_motor_set_speed(NXT_PORT_C,0,1); systick_wait_ms(STOP_TIME); display_string("THAT'S ALL !"); display_update(); TerminateTask(); /* 処理終了 */ } |
このプログラムの解説をします。
nxt_motor_get_count(NXT_PORT_B)
なお、モータの回転速度は前回の読み取り値との差を、読み取り間隔(systic_wait_ms()の引数で指定した値)で割ることにより得られます。
- サンプルプログラム torary1.c の str_enc() をベースに(プログラム内でコピーして)、左右のモータの回転角度の差が指定角度になるまで左右のモータが反対方向に回転する関数 rot_enc() を作成(定義)せよ。ただし、この関数の引数は str_enc()と同じとせよ。
なお、「回転角度の差」は左モータの回転角から右モータの回転角を引いたものとし、機体を接地させた場合に時計回りとなることを前提としてよい。
- ロータリエンコーダを用いて 45cm 直進して1秒停止後に、時計回りで90度回転するプログラムを作成せよ。(以下の【ヒント】を参考にせよ)
- 2で作成したプログラムを改良して、一辺が45cmの正方形を描く走行(45cm直進、90度回転)を実現せよ。(前回のモータ動作の課題のプログラムを参考にせよ)
【ヒント】
直進やターンに必要な回転量は以下の式から導くことができる。
直進動作: L = R ( θl + θr ) / 2 ( L: 走行距離、R:タイヤの半径、
θl:左タイヤの回転角)、
θr:右タイヤの回転角)
前節で組み立てた標準走行体の場合、R=27.5[mm], D=102[mm]です。
ただし、この値は車体重量等により微妙に変化するため、実際に走行させたときの直進距離や回転角度に応じて調整する必要があります。 なお、今回作成した関数では直進時は ( θl + θr ) / 2 を、回転時は θl - θr を ref_angle として与えていることに注意して下さい。
また、直進動作の場合、この関係式のθはラジアン単位であるため、プログラム内の角度単位(度、degree)との間で単位換算を行う必要があります(1ラジアン=57.3度)。 一方、回転動作の場合は、Φもθも同じ単位を使えばよいので、ともに度(degree)として扱えば単位換算は不要です。 |
【ロータリエンコーダを用いたフィードバック制御(比例制御)による直進の実現】
毎ループ、左右のエンコーダ値を取得して、その差をなくすようにモータのduty比の指定値を変更する。
具体的には、左右モータの回転角の差に比例した値を、最初のduty比の指定値に加算(または減算)することにより、モータの回転速度調整して回転角の差をなくす。比例係数(比例ゲインKp)は走行スピードにもよるが 1~3 程度とすればよい。 |
ロータリエンコーダを使ったフィードバック制御による直進走行が実現できれば、左右のモータに多少の個体差があったとしても、左右のモータのPWM値を事前調整する必要がなくなる。
1. モータの使用 | TOPへ | 3.タッチセンサの使用 |