想定するロボットシミュレータ

ロボットプログラミング

基本構造と設計手法

知能ロボットは常に環境の情報を受け取りながら適切な行動を決定し,実行します.そのため,常に

のループを短い周期で定期的に実行するプログラムを書く必要があります.

具体的には,while文などで無限ループを構成し,システムに用意されたsleep関数やwait関数を用いて次の処理を開始するまでプロセスを停止する方法があります.
ロボットプログラムは大規模となることが多く,また,ロボットが人や環境に危害を加えないことが条件となるため,見通しの良い,バグの少ないプログラム開発が望まれます.
そこで,よく使われる設計手法である構造化プログラミングと状態遷移を紹介します.

構造化プログラミングと状態遷移

構造化プログラミング:全体の処理を排他的なモジュールに分割し,組み合わせて構築することで,見通しの良いプログラムを目指す考え方
このモジュールを状態とみなし,それらの状態の遷移として課題を表現します.
例として,以下の状態遷移を考えます.

  1. 状態Aの処理が終了したら状態Bへ
  2. 状態Bの処理が終了したときにvalueが0より大きければ終了
  3. 状態Bの処理が終了したときにvalueが0以下ならば状態Cへ
  4. 状態Cの処理が終了したら状態Bへ

この状態遷移を図に表すことで,プログラムの整理ができ,共同での設計や議論がしやすくなります. 達成したい課題があるとき,まずは状態を定義して,状態遷移図を描いてから,プログラミングに取り掛かるように心がけましょう.

ロボットによる物体操作の場合,次のように状態を定義できます.

プログラムの基本構造

サンプルプログラムをダウンロードしてください.

コンパイルと実行

main1.cのあるフォルダに移動して,

gcc main1.c -o main1 -L. -lrobo -lm -lpthread -lGL -lglut -lGLU

でコンパイルします.
そして,

./main1

で実行します.

a:左,d:右,w:拡大,s:縮小
q:終了

ロボットライブラリ

コンパイルコマンドの「-l{ライブラリ名}」はプログラム中に使用するライブラリを指定しています.
例えば,mは算術演算ライブラリで,GLとglut,GLUは3Dグラフィックのライブラリです.

roboライブラリ(librobo.a)は独自に開発したロボットライブラリで,同じフォルダ内にあります.
L. は同じフォルダ内にライブラリがあることを示します.
このロボットライブラリはロボットの駆動やセンシングに関する関数や構造体を定義しています.
なお,このライブラリ自体を変更することはできません.

(ソースコードがあればライブラリの変更が可能ですが,この授業の範囲外とします.
ライブラリの構築については,四年生の授業「アドバンスドプログラミング演習(C言語)」で扱います.)

librobo2.aは第4回で使います.

ヘッダファイル

ロボットライブラリの関数と構造体,列挙型はヘッダファイル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増加します.
現在の実行時間ステップ数は関数

int get_update_robot_step();

で取得できます.

今回,ロボットが動き始める前に,100ステップ待つことにします.
その処理は,

if(get_update_robot_step() == 100) set_command_move_arm_to(pos);

の条件文に反映されています.
ここで,関数

bool set_command_move_arm_to(Position target_position);

は,アームを目標座標 (target_position) まで移動させるものです.

練習1

(x, y) = (-0.1, 0.2) と (x, y) = (0.6, -0.3) を往復し続けるプログラムを作成してください.
また,このときの状態遷移図を描いてください.

ヒント

アームの状態は関数

ArmState get_arm_state();

で取得でき,アームが停止していれば,この関数はARM_STATE_STOPを返します.

練習2

アームが長方形軌道を2周描いて,停止するプログラムを作成してください.
図中のAとCの座標を,プログラム実行後にキーボード入力できるようにしてください.
フィールド外の座標が指定された場合はエラーを表示して,実行を終了するようにしてください.

ヒント

範囲外の座標を指定されたとき,関数

bool set_command_move_arm_to(Position target_position);

はfalseを返します.


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS