この記事が私が担当しているロボットプログラミングⅡの講義用です。
今回は前回作成した車輪型ロボットモデルに北陽電機のLIDAR(レーザ式測域センサ)モデルを搭載して動かしましょう。
なお、本記事は以下のGAZEBO.orgのチュートリアルを改変しています。
http://wiki.gazebosim.org/wiki/Tutorials/1.2/control_robot/mobile_base_laser
コードが古くgazebo4.0に対応していない部分を変更し、ワールド座標系での位置、姿勢の取得に関するコードを追加し、コメントを日本語化しています。それ以外のソースコードはほとんど同じものです。本ソースコードはgazebo4.0用なので、それ以前のバージョンでは動かないかもしれません。
1. 作業ディレクトリの作成
端末を開き、以下のコマンドを実行する($は打ち込まない)。ロボットプログラミングの演習では~/prog2ディレクトリ(フォルダ)の中にいろいろなプログラムを作っていく。
$ mkdir -p ~/prog2/5sensor
$ cd ~/prog2/5sensor
$ gedit sensor.cc
2. ソースコードの作成
プラグインとなるC++言語のソースコード(拡張子cc)を作成する。
以下のプログラムを「1.作業ディレクトリの作成」で開いたgedit(エディター)に次のソースコードをコピペしてwheel.ccを保存する。
注:コピペするときは、下プログラムの右上にあるプリンタアイコンの左にあるView Sourceアイコンをクリックして、ソースを表示して、それをコピペすること。そうしないと、コンパイルと時のエラーになやまされることになる。
//http://wiki.gazebosim.org/wiki/Tutorials/1.2/control_robot/mobile_base_laserを改変
#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)
{
// modelへポインタを格納
this->model = _parent;
// このプラグイン用のパラメータをロード
if (this->LoadParams(_sdf))
{
// エラーチェック
gzerr << this->leftWheelJoint->GetAngle(0) << "\n";
gzerr << this->rightWheelJoint->GetAngle(0) << "\n";
// アップテートイベントを聞く。シミュレーションの繰り返し時に
// このイベントはブロードキャストされる。
this->updateConnection
= event::Events::ConnectWorldUpdateBegin(
boost::bind(&MobileBasePlugin::OnUpdate, this));
}
}
public:
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");
}
// プラグインパラメータからセンサ名を見つける
if (!_sdf->HasElement("ray_sensor"))
{
gzerr << "param [ray_sensor] not found\n";
return false;
}
else
{
// センサ名の取得
std::string sensorName
= _sdf->Get<std::string>("ray_sensor");
// SensorMangaerを使い、センサのポインタを取得
sensors::SensorPtr sensor
= sensors::SensorManager::Instance()->GetSensor(sensorName);
if (!sensor)
{
gzerr<< "sensor by name [" << sensorName
<< "] not found in model\n";
return false;
}
this->laser = boost::shared_dynamic_cast<sensors::RaySensor>(sensor);
if (!this->laser)
{
gzerr << "laser by name ["
<< sensorName
<< "] not found in model\n";
return false;
}
}
// プラグインパラメーターからジョイントをロード
if (!this->FindJointByParam(_sdf, this->leftWheelJoint,
"left_wheel_hinge") ||
!this->FindJointByParam(_sdf, this->rightWheelJoint,
"right_wheel_hinge"))
return false;
// 成功時
return true;
}
public:
bool FindJointByParam(sdf::ElementPtr _sdf,
physics::JointPtr &_joint,std::string _param)
{
if (!_sdf->HasElement(_param))
{
gzerr << "param [" << _param << "] not found\n";
return false;
}
else
{
_joint
= this->model->GetJoint(_sdf->Get<std::string>(_param));
if (!_joint)
{
gzerr << "joint by name ["
<< _sdf->Get<std::string>(_param)
<< "] not found in model\n";
return false;
}
}
return true;
}
// ワールド更新開始イベントから呼び出される
public:
void OnUpdate()
{
// ワールド座標系での位置と姿勢を取得
math::Pose pose = this->model->GetWorldPose();
printf("x=%.1f y=%.1f z=%.1f\n", pose.pos.x,pose.pos.y,pose.pos.z);
printf("roll=%f pitch=%f yaw=%f\n",pose.rot.GetRoll(),pose.rot.GetPitch(),pose.rot.GetYaw());
unsigned int n = this->laser->GetRangeCount();
double min_dist = 1e6;
for (unsigned int i = 0; i < n; ++i)
{
if (this->laser->GetRange(i) < min_dist)
min_dist = this->laser->GetRange(i);
}
double target_dist = 2.0;
if (min_dist < this->laser->GetRangeMax())
{
// 比例制御
double torque = this->gain*(min_dist - target_dist );
this->leftWheelJoint->SetForce(0, torque);
this->rightWheelJoint->SetForce(0, torque);
}
else
{
this->leftWheelJoint->SetForce(0, 0);
this->rightWheelJoint->SetForce(0, 0);
}
}
// モデルへのポインタ
private:
physics::ModelPtr model;
private:
physics::WorldPtr world;
// ワールド状態のサブスクライブ(講読)
private:
transport::NodePtr node;
private:
transport::SubscriberPtr statsSub;
private:
common::Time simTime;
// 更新イベントコネクションへのポインタ
private:
event::ConnectionPtr updateConnection;
private:
physics::JointPtr leftWheelJoint;
private:
physics::JointPtr rightWheelJoint;
private:
sensors::RaySensorPtr laser;
private:
double gain;
};
// シミュレータへのプラグイン登録
GZ_REGISTER_MODEL_PLUGIN(MobileBasePlugin)
}
コピペしたらgeditの「保存」ボタンをクリックして~/prog2/5hello/の中に保存し、geditメニューバーの「ファイル(F)」→「終了(Q)」でgeditを終了する。
3. コンパイル設定ファイルの作成
gazeboではソースコードをコンパイル(ビルド)するのにcmakeというシステムを使う。先程から使っている端末で以下のコマンドを実行しgeditを起動する。
$ gedit CMakeLists.txt
CMakeLists.txtはcmakeの設定ファイル。次のソースコード(2番の注参照)をgeditにコピペし、CMakeLists.txtを保存する。保存場所は今までと同じ~/prog2/4wheelディレクトリ。保存したら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(sensor SHARED sensor.cc)
target_link_libraries(sensor ${GAZEBO_LIBRARIES} ${Boost_LIBRARIES})
4.コンパイル
以下のコマンドを実行し、ソースコードをメイクする。
$ cd ~/prog2/5sensor
$ mkdir build
$ cd build
$ cmake ../
$ make
5. モデルファイルのコピー
(1) 以下のコマンドを実行し、モデルファイルをコピーする。
$ cd ~/.gazebo/models
$ cp -r wheel_robot1 wheel_robot3
(2) model.configファイルの変更
model.configの3行目
<name>Wheel Robot 1 </name>
を
<name>Wheel Robot 3 </name>
に変更する。
(3) model.sdfファイルへのプラグイン追加
model.sdfの下から2行目にある</model>の上に以下のLIDARセンサとプラグインに関するコードを追加する。追加しないとロボットが動きません。
<include>
<uri>model://hokuyo</uri>
<pose>0.175 0 0.3 0 0 0</pose>
</include>
<joint name="hokuyo_joint" type="revolute">
<child>hokuyo::link</child>
<parent>chassis</parent>
<axis>
<xyz>0 0 1</xyz>
<limit>
<upper>0</upper>
<lower>0</lower>
</limit>
</axis>
</joint>
<plugin name="sensor" filename="libsensor.so" >
<left_wheel_hinge>left_wheel_hinge</left_wheel_hinge>
<right_wheel_hinge>right_wheel_hinge</right_wheel_hinge>
<gain>0.1</gain>
<ray_sensor>laser</ray_sensor>
</plugin>
6. 実行
以下のコマンドを実行し、gazeboを立ち上げよう! ここでは、gazeboを起動するのに–verboseのオプションをつけます。これは、標準出力に位置や姿勢を表示するためです。
gazeboが起動したら画面左側のinsert(挿入)タブから制作したwheel robot 3を選択する。ロボットの前に直方体と置くとロボットが動き出したら成功。今回はここまで。
$ export GAZEBO_PLUGIN_PATH=~/prog2/5sensor/build:$GAZEBO_PLUGIN_PATH
$ gazebo --verbose
○ 演習
LIDARを搭載した家政婦ロボットを動かしてみましょう。今回はロボットの後ろ5mに球を置き、それを見つけ、ドリブル(押し続ける)するプログラムを作ろう!ロボットサッカーのもっとも基本的なスキルです。

コメント