この記事が私が担当しているロボットプログラミングⅡの講義用です。
今回は2自由度のロボットアームモデルを作成し、キーボード操作により動かすサンプルです。
1. 作業ディレクトリの作成
端末を開き、以下のコマンドを実行する($は打ち込まない)。ロボットプログラミングの演習では~/prog2ディレクトリ(フォルダ)の中にいろいろなプログラムを作っていく。
$ mkdir -p ~/prog2/6arm
$ cd ~/prog2/6arm
$ gedit arm.cc
2. ソースコードの作成
プラグインとなるC++言語のソースコード(拡張子cc)を作成する。
以下のプログラムを「1.作業ディレクトリの作成」で開いたgedit(エディター)に次のソースコードをコピペしてarm.ccを保存する。
#include <boost/bind.hpp> #include <gazebo/gazebo.hh> #include <gazebo/physics/physics.hh> #include <gazebo/sensors/sensors.hh> #include <gazebo/common/common.hh> #include <gazebo/transport/TransportTypes.hh> #include <gazebo/msgs/MessageTypes.hh> #include <gazebo/common/Time.hh> #include <stdio.h> namespace gazebo { class MobileBasePlugin : public ModelPlugin { public: void Load(physics::ModelPtr _parent, sdf::ElementPtr _sdf) { physics::WorldPtr world = physics::get_world("default"); // modelへポインタを格納 this->model = _parent; hinge1 = this->model->GetJoint("hinge1"); hinge2 = this->model->GetJoint("hinge2"); sensor = this->model->GetLink("sensor"); for (int i=0; i< 3; i++) { THETA[i] = 0; } // このプラグイン用のパラメータをロード if (this->LoadParams(_sdf)) { // アップテートイベントを聞く。シミュレーションの繰り返し時に // このイベントはブロードキャストされる。 this->updateConnection = event::Events::ConnectWorldUpdateBegin( boost::bind(&MobileBasePlugin::OnUpdate, this)); } } bool LoadParams(sdf::ElementPtr _sdf) { // 制御用のgainパラメータを見つける if (!_sdf->HasElement("gain")) { gzerr << "param [gain] not found\n"; return false; } else { // gainの値を取得 this->gain = _sdf->Get<double>("gain"); } // 成功時 return true; } // getch関数は以下のウェブサイトの記事を使用 // http://answers.ros.org/question/63491/keyboard-key-pressed/ int getch() { static struct termios oldt, newt; tcgetattr(STDIN_FILENO, &oldt); // save old settings newt = oldt; newt.c_lflag &= ~(ICANON); // disable buffering tcsetattr(STDIN_FILENO, TCSANOW, &newt); // apply new settings int c = getchar(); // read character (non-blocking) tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // restore old settings return c; } /*** キー入力関数 ***/ void command() { int cmd = getch(); switch (cmd) { case 'j': THETA[1] += M_PI/180; break; // jキー case 'f': THETA[1] -= M_PI/180; break; // fキー case 'k': THETA[2] += M_PI/180; break; // kキー case 'd': THETA[2] -= M_PI/180; break; // dキー default: std::cout << "Input j,f,k,d" << std::endl; } } /*** センサー情報の表示 ***/ void printSensor() { // 関節角表示 math::Angle angle1 = hinge1->GetAngle(0); // 関節角の取得 math::Angle angle2 = hinge2->GetAngle(0); // 度表示 printf("angle1=%6.3f angle2=%6.3f ", angle1.Degree(), angle2.Degree()); // radian表示 //printf("angle1=%f angle2=%f", angle1.Radian(), angle2.Radian()); // 手先位置表示 math::Pose pose = sensor->GetWorldCoGPose(); printf("x=%6.3f y=%6.3f \n", pose.pos.x,pose.pos.y); } /*** 関節を動かす ***/ void moveJoint() { hinge1->SetAngle(0, THETA[1]); hinge2->SetAngle(0, THETA[2]); } // ワールド更新開始イベントから呼び出される void OnUpdate() { command(); // キー入力受付 moveJoint(); // 関節の動かす printSensor(); } private: // モデルへのポインタ physics::ModelPtr model; physics::WorldPtr world; // ワールド状態のサブスクライブ(講読) transport::NodePtr node; transport::SubscriberPtr statsSub; common::Time simTime; // 更新イベントコネクションへのポインタ event::ConnectionPtr updateConnection; physics::JointPtr hinge1, hinge2; sensors::RaySensorPtr laser; physics::LinkPtr sensor; double THETA[3]; // 関節目標角度 THETA[0]は未使用 double gain; // 比例ゲイン }; // シミュレータへのプラグイン登録 GZ_REGISTER_MODEL_PLUGIN(MobileBasePlugin) }
コピペしたらgeditの「保存」ボタンをクリックして~/prog2/6arm/の中に保存し、geditメニューバーの「ファイル(F)」→「終了(Q)」でgeditを終了する。
3. コンパイル設定ファイルの作成
gazeboではソースコードをコンパイル(ビルド)するのにcmakeというシステムを使う。先程から使っている端末で以下のコマンドを実行しgeditを起動する。
$ gedit CMakeLists.txt
CMakeLists.txtはcmakeの設定ファイル。次のソースコードをgeditにコピペし、CMakeLists.txtを保存する。保存場所は今までと同じ~/prog2/6armディレクトリ。保存したらgeditを終了する。
cmake_minimum_required(VERSION 2.8 FATAL_ERROR) find_package(Boost REQUIRED COMPONENTS system) include_directories(${Boost_INCLUDE_DIRS}) link_directories(${Boost_LIBRARY_DIRS}) include (FindPkgConfig) if (PKG_CONFIG_FOUND) pkg_check_modules(GAZEBO gazebo) endif() include_directories(${GAZEBO_INCLUDE_DIRS}) link_directories(${GAZEBO_LIBRARY_DIRS}) add_library(arm SHARED arm.cc) target_link_libraries(arm ${GAZEBO_LIBRARIES} ${Boost_LIBRARIES})
4.コンパイル
以下のコマンドを実行し、ソースコードをメイクする。
$ cd ~/prog2/6arm
$ mkdir build
$ cd build
$ cmake ../
$ make
5. モデルファイルの作成
以下のコマンドを実行し、モデルファイルを作成する。
$ cd ~/.gazebo/models
$ mkdir arm_robot1
以下のmodel.configファイルをgeditにコピペし、model.configファイルと名前を付けてarm_robot1ディレクトリに保存する。
<?xml version="1.0"?> <model> <name>Arm Robot 1</name> <version>1.0</version> <sdf version='1.4'>model.sdf</sdf> <author> <name>My Name</name> <email>My mail address</email> </author> <description> My arm robot. </description> </model>
同様にして、以下のmodel.sdfファイルを保存する。
<?xml version='1.0'?> <sdf version='1.4'> <!-- アームロボット1 --> <model name="arm_robot1"> <static>false</static> <!-- trueにすると静的モデル --> <link name='base'> <!-- リンク:土台 --> <pose>0 0 0.05 0 0 0 </pose> <!-- 位置(x,y,z)と姿勢(roll,pitch,yaw) --> <visual name='visual'> <!-- 表示用 --> <geometry> <!-- 形状情報 --> <cylinder> <!-- 円柱 --> <radius>0.04</radius> <!-- 半径 --> <length>0.1</length> <!-- 長さ --> </cylinder> </geometry> </visual> </link> <link name='link1'> <!-- リンク1 --> <self_collide>0</self_collide> <pose>0.250 0 0.04 0 1.5707 0</pose> <!-- 位置(x,y,z)と姿勢(roll,pitch,yaw) --> <inertial> <inertia> <ixx>0.01</ixx> <ixy>0</ixy> <ixz>0</ixz> <iyy>0.01</iyy> <iyz>0</iyz> <izz>0.01</izz> </inertia> <mass>2.0</mass> </inertial> <visual name='visual'> <!-- 表示用 --> <geometry> <!-- 形状情報 --> <cylinder> <!-- カプセル --> <radius>0.04</radius> <!-- 半径 --> <length>0.5</length> <!-- 長さ --> </cylinder> </geometry> </visual> </link> <link name='link2'> <!-- リンク2 --> <self_collide>0</self_collide> <pose>0.75 0 0.04 0 1.5707 0</pose> <!-- 位置(x,y,z)と姿勢(roll,pitch,yaw) --> <inertial> <inertia> <ixx>0.01</ixx> <ixy>0</ixy> <ixz>0</ixz> <iyy>0.01</iyy> <iyz>0</iyz> <izz>0.01</izz> </inertia> <mass>2.0</mass> </inertial> <visual name='visual'> <!-- 表示用 --> <geometry> <!-- 形状情報 --> <cylinder> <!-- カプセル --> <radius>0.04</radius> <!-- 半径 --> <length>0.5</length> <!-- 長さ --> </cylinder> </geometry> </visual> </link> <link name='sensor'> <!-- 位置センサ --> <pose>1.0 0 0.04 0 0 0 </pose> <!-- 位置(x,y,z)と姿勢(roll,pitch,yaw) --> <visual name='visual'> <!-- 表示用 --> <geometry> <!-- 形状情報 --> <sphere> <!-- 円柱 --> <radius>0.004</radius> <!-- 半径 --> </sphere> </geometry> </visual> </link> <!-- ジョイント(関節) revoluteは回転式(ヒンジ)--> <joint type="revolute" name="hinge0"> <!--土台を地面に固定 --> <child>base</child> <!-- 子(下位)リンク --> <parent>world</parent> <!-- 親(上位)リンク --> <axis> <limit> <lower>0</lower> <upper>0</upper> </limit> <xyz>0 0 1</xyz> <!-- 回転軸ベクトル(x,y,z) --> </axis> </joint> <joint type="revolute" name="hinge1"> <!--第1関節 --> <pose>0 0 -0.25 0 0 0</pose> <child>link1</child> <!-- 子(下位)リンク --> <parent>base</parent> <!-- 親(上位)リンク --> <axis> <xyz>0 0 1</xyz> <!-- 回転軸ベクトル(x,y,z) --> </axis> </joint> <joint type="revolute" name="hinge2"> <!--第2関節 --> <pose>0 0 -0.25 0 0 0</pose> <child>link2</child> <!-- 子(下位)リンク --> <parent>link1</parent> <!-- 親(上位)リンク --> <axis> <xyz>0 0 1</xyz> <!-- 回転軸ベクトル(x,y,z) --> </axis> </joint> <joint type="revolute" name="sensor_joint"> <!--センサの固定 --> <pose>0.02 -0.02 -0.2 0 0 0</pose> <child>sensor</child> <!-- 子(下位)リンク --> <parent>link2</parent> <!-- 親(上位)リンク --> <axis> <limit> <lower>0</lower> <upper>0</upper> </limit> <xyz>0 0 1</xyz> <!-- 回転軸ベクトル(x,y,z) --> </axis> </joint> <plugin name="arm" filename="libarm.so" > <hinge1>hinge1</hinge1> <hinge2>hinge2</hinge2> <gain>0.1 </gain> <sensor>sensor</sensor> <ray_sensor>laser</ray_sensor> </plugin> </model> </sdf>
6. 実行
以下のコマンドを実行し、gazeboを立ち上げよう! ここでは、gazeboを起動するのに–verboseのオプションをつけます。これは、標準出力に位置や姿勢を表示するためです。
gazeboが起動したら画面左側のinsert(挿入)タブから制作したarm robot 1を選択する。
d, f, j, kキーを入力するとロボットアームの関節が動いたら成功。今回はここまで。
$ export GAZEBO_PLUGIN_PATH=~/prog2/6arm/build:$GAZEBO_PLUGIN_PATH
$ gazebo --verbose
〇 Exercise
2自由度ロボットアームのサンプルプログラムに以下を実装しよう!
- 運動学
運動学の計算結果と位置センサの出力を比較し、プログラムが正しいか確認する。 - 逆運動学
逆運動学を実装しよう。
任意の位置に直方体を表示し、アーム先端がそれを指すようなプログラムを実装する。2自由度の場合は解が2つあるので、数字キー1を押すと解1、数字キー2を押すと解2を選択する。
コメント