知能ロボットは常に環境の情報を受け取りながら適切な行動を決定し,実行します.そのため,常に
具体的には,while文などで無限ループを構成し,システムに用意されたsleep関数やwait関数を用いて次の処理を開始するまでプロセスを停止する方法があります.
ロボットプログラムは大規模となることが多く,また,ロボットが人や環境に危害を加えないことが条件となるため,見通しの良い,バグの少ないプログラム開発が望まれます.
そこで,よく使われる設計手法である構造化プログラミングと状態遷移を紹介します.
構造化プログラミング:全体の処理を排他的なモジュールに分割し,組み合わせて構築することで,見通しの良いプログラムを目指す考え方
このモジュールを状態とみなし,それらの状態の遷移として課題を表現します.
例として,以下の状態遷移を考えます.
この状態遷移を図に表すことで,プログラムの整理ができ,共同での設計や議論がしやすくなります. 達成したい課題があるとき,まずは状態を定義して,状態遷移図を描いてから,プログラミングに取り掛かるように心がけましょう.
ロボットによる物体操作の場合,次のように状態を定義できます.
サンプルプログラムをダウンロードしてください.
main1.cのあるフォルダに移動して,
gcc main1.c -o main1 -L. -lrobo -lm -lpthread -lGL -lglut
でコンパイルします. (glu**関数関連のエラーが出るときは,コマンドの後ろに -lGLU を追加してみてください)
そして,
./main1
で実行します.
a:左,d:右,w:拡大,s:縮小
端末上でCtrl + Cで実行終了.
コンパイルコマンドの「-l{ライブラリ名}」はプログラム中に使用するライブラリを指定しています.
例えば,mは算術演算ライブラリで,GLとglutは3Dグラフィックのライブラリです.
roboライブラリ(librobo.a)は独自に開発したロボットライブラリで,同じフォルダ内にあります.
このロボットライブラリはロボットの駆動やセンシングに関する関数や構造体を定義しています.
なお,このライブラリ自体を変更することはできません.
(ソースコードがあればライブラリの変更が可能ですが,この授業の範囲外とします.ライブラリの構築については,四年生の授業「アドバンスドプログラミング演習(C言語)」で扱います.)
ロボットライブラリの関数と構造体,列挙型はヘッダファイルrobot_simulator.hで説明されています.
例えば,位置を表すPosition構造体にはxとyの座標が定義されています.
struct Position_{ double x; double y; }; typedef struct Position_ Position;
また,色を表すColorRGB構造体にはr(赤),g(緑),b(青)の各色の変数が定義されています.
struct ColorRGB_{ int r; int g; int b; }; typedef struct ColorRGB_ ColorRGB;
例えば,関数
bool set_command_move_arm_to( Position target_position );
へPosition構造体を渡すことで,アームがその座標まで移動します.
bool型(論理型)関数はtrue(真)かfalse(偽)を返します. この関数の場合,ARM_STATE_HANDLING_OBJECTのとき,あるいは,指定した座標が可動範囲外であるときにfalseを返し,それ以外だとtrueを返します.
このARM_STATE_HANDLING_OBJECTはenum型(列挙型)で定義されています.
enum ARM_STATE{ ARM_STATE_STOP, ARM_STATE_MOVING, ARM_STATE_HANDLING_OBJECT, }; typedef enum ARM_STATE ArmState;
列挙型は一般に,選択肢を表す整数の定数を定義するときに用いられます.
enum タグ名{ 列挙変数1, 列挙変数2, 列挙変数3, ... };
で定義すると,列挙変数には自動的に先頭から0, 1, 2, ...と整数が割り当てられます.
Arm_State列挙型はアームの状態を表し,各列挙変数はそれぞれ,アームが止まっている,動いている,物体を把持してるを意味しています.
今アームがどの状態にあるかは,関数
ArmState get_arm_state_str ( );
で取得できます.この関数はアームの状態に応じたArmState?型を返します.すなわち,ARM_STATE_STOP,ARM_STATE_MOVING,および,ARM_STATE_HANDLING_OBJECTのいずれかを返します.
この関数がARM_STATE_HANDLING_OBJECT(つまり,2)を返せば,アームが物体を把持していることになります.
set_command_move_arm_to関数では,このアーム状態の情報を用いて,返り値を決めています. ただし,上記の処理はライブラリのソースコードに書かれており,ライブラリユーザには見えません.
このヘッダファイルには,この他にも課題の遂行に必要な関数・列挙型が説明されています. プログラミングを始める前に,各自で読んでおいてください.
main1.cを解説します.ヘッダファイル中のコメントと併せて見てください.
まず,
#include "robot_simulator.h"
で,ヘッダファイルを読み込みます.
#define OBJECT_NUM 10
で,物体の数を指定しています.
main関数の中ですが,まず,
initialize_robot(OBJECT_NUM);
でOBJECT_NUM個の物体のあるシミュレーション環境を初期化しています. この関数は初期化に失敗するとfalseを返すので,以降で,その場合のエラー処理をしています.
もし,エラー(!is_initialized)なら,
finalize_robot();
でシミュレーションを終了します.
次に,
Position pos = {0.2, 0.1};
で,Position構造体にアームの目標座標を代入しています.
ここから,ロボットシミュレーションの実行になります.
while(update_robot()){***}
によって,update_robot関数がfalseを返すまで処理***をループさせます.
このupdate_robot関数により,シミュレーションを実行します.1回の関数呼び出し(1回のwhileループ)で,シミュレーションの実行時間ステップが1増加します. 現在の実行時間ステップ数はget_update_robot_step関数で取得でき,
update_step = get_update_robot_step();
で格納しています.
今回,ロボットが動き始める前に,100ステップ待つことにします. その処理は,
if(update_step == 100) set_command_move_arm_to(pos);
の条件文に反映されています.
set_command_move_arm_to(pos)関数で,アームを目標座標まで移動させます.
(x, y) = (0.2, 0.1) と (x, y) = (0.3, 0.6) を往復し続けるプログラムを作成してください. また,このときの状態遷移図を描いてください.
アームの状態はget_arm_state関数で取得でき,アームが停止していれば,その関数はARM_STATE_STOPを返します.
アームが長方形軌道を3周描いて,停止するプログラムを作成してください.
図中のAとCの座標を,プログラム実行後にキーボード入力できるようにしてください. フィールド外の座標が指定された場合はエラーを表示して,実行を終了するようにしてください.
また,このときのアームの駆動の状態遷移図を描いてください.上記の座標指定や指定時のエラー処理の状態遷移を考慮する必要はありません.
状態と状態遷移を明示的に定義して図にし,それに基づいてプログラムを作成するといった手順を踏んでください.
適宜コメントを入れたり,関数化したりして,他人にもわかりやすいプログラムになるように心がけてください.全くコメントのないものは減点します.
他人のプログラムを写したことが判明した場合は,両者とも0点になります.課題内容に対応していないプログラムにも点をつけません.
範囲外の座標を指定されたとき,set_command_move_arm_to関数はfalseを返します.
提出物一式を入れるフォルダの名前は学生番号にしてください.
次回の授業中に,提出プログラムが正しく実行できていることをTAが直接確認し,状態遷移図を回収します.
その授業日24:00までにCLEでプログラムファイルを提出してください.