- ロボットモデルに不具合があったので修正しました。これlegged_robot_model150115.tgzを使用してください(2015-01-15)。
- 授業時のエラーを訂正しました。変更はlegged.ccだけです。エラーの理由はGazeboバージョンによりAPIの仕様が変更になったためです(15年1月10日)。
この記事が私が担当しているロボットプログラミングⅡの講義用です。
今回は4脚ロボットモデルを作成し、クロール歩行のシミュレーションを行います。これは拙著「簡単!実践!ロボットシミュレーション Open Dynamics Engineによるロボットプログラミング」のプログラム8.1 legged.cpp (P210)をGAZEBOに移植したものです。理論的な説明や式などは拙著をご覧頂ければ幸いです。
1. 作業ディレクトリの作成
端末を開き、以下のコマンドを実行する($は打ち込まない)。ロボットプログラミングの演習では~/prog2ディレクトリ(フォルダ)の中にいろいろなプログラムを作っていく。
$ mkdir -p ~/prog2/7legged
$ cd ~/prog2/7legged
$ gedit legged.cc
2. 4脚ロボットのソースコード
プラグインとなるC++言語のソースコード(拡張子cc)を作成する。以下のプログラムを「1.作業ディレクトリの作成」で開いたgedit(エディター)に次のソースコードをコピペしてlegged.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>
#define LINK_NUM 3 // 全リンク数 (total number of links)
#define JT_NUM 3 // 全ジョイント数 (total number of joints)
#define LEG_NUM 4 // 全脚数 (total number of legs)
namespace gazebo
{
class MobileBasePlugin : public ModelPlugin
{
public:
MobileBasePlugin()
{
l1 = 1.5*0.05, l2 = 0.3, l3 = 0.3; // leg length
r1 = 0.02, r2 = 0.02, r3 = 0.02 ; // leg radius
for (int i=0; i< 4; i++) {
for (int j=0; j < 3; j++) {
THETA[i][j] = 0;
}
}
}
void Load(physics::ModelPtr _parent, sdf::ElementPtr _sdf)
{
physics::WorldPtr world = physics::get_world("default"); // modelへポインタを格納
this->model = _parent;
hinge_lf1 = this->model->GetJoint("hinge_lf1");
hinge_lf2 = this->model->GetJoint("hinge_lf2");
hinge_lf3 = this->model->GetJoint("hinge_lf3");
hinge_lr1 = this->model->GetJoint("hinge_lr1");
hinge_lr2 = this->model->GetJoint("hinge_lr2");
hinge_lr3 = this->model->GetJoint("hinge_lr3");
hinge_rr1 = this->model->GetJoint("hinge_rr1");
hinge_rr2 = this->model->GetJoint("hinge_rr2");
hinge_rr3 = this->model->GetJoint("hinge_rr3");
hinge_rf1 = this->model->GetJoint("hinge_rf1");
hinge_rf2 = this->model->GetJoint("hinge_rf2");
hinge_rf3 = this->model->GetJoint("hinge_rf3");
// このプラグイン用のパラメータをロード
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;
}
/*** 逆運動学の計算 (calculate inverse kinematics ***/
void inverseKinematics(double x, double y, double z,
double *ang1, double *ang2, double *ang3,int posture)
{
double l1a = 0, l3a = l3 + r3/2;
double c3 = (x*x + z*z + (y-l1a)*(y-l1a) - (l2*l2+l3a*l3a))/(2*l2*l3a);
double s2 = (y-l1a) / (l2 + l3a*c3);
double c2 = sqrt(1 - s2 * s2);
double c1 = (l2 + l3a*c3)*c2/sqrt(x*x+z*z);
// printf("c3=%f s2=%f c2=%f c1=%f \n", c3,s2,c2,c1);
if (sqrt(x*x+y*y+z*z) > l2 + l3) {
printf(" Target point is out of range \n");
}
switch (posture) {
case 1: // 姿勢1 (posture 1)
*ang1 = atan2(x,-z) - atan2(sqrt(1 - c1*c1),c1);
*ang2 = - atan2(s2,c2);
*ang3 = atan2(sqrt(1-c3*c3),c3); break;
case 2: // 姿勢2 (posture 2)
*ang1= atan2(x,-z) + atan2(sqrt(1 - c1*c1),c1);
*ang2= - atan2(s2,c2);
*ang3= - atan2(sqrt(1-c3*c3),c3); break;
case 3: // 姿勢3 (posture 3)
*ang1 = M_PI + (atan2(x,-z) - atan2(sqrt(1 - c1*c1),c1));
*ang2 = - M_PI + atan2(s2,c2);
*ang3 = - atan2(sqrt(1-c3*c3),c3); break;
case 4: // 姿勢4 (posture 4)
*ang1 = M_PI + atan2(x,-z) + atan2(sqrt(1 - c1*c1),c1);
*ang2 = -M_PI + atan2(s2,c2);
*ang3 = atan2(sqrt(1-c3*c3),c3); break;
}
}
/*** 関節を動かす ***/
void moveJoint()
{
hinge_lf1->SetAngle(0, THETA[0][0]);
hinge_lr1->SetAngle(0, THETA[1][0]);
hinge_rr1->SetAngle(0, THETA[2][0]);
hinge_rf1->SetAngle(0, THETA[3][0]);
hinge_lf2->SetAngle(0, THETA[0][1]);
hinge_lr2->SetAngle(0, THETA[1][1]);
hinge_rr2->SetAngle(0, THETA[2][1]);
hinge_rf2->SetAngle(0, THETA[3][1]);
hinge_lf3->SetAngle(0, THETA[0][2]);
hinge_lr3->SetAngle(0, THETA[1][2]);
hinge_rr3->SetAngle(0, THETA[2][2]);
hinge_rf3->SetAngle(0, THETA[3][2]);
}
/*** 歩行制御 (gait control) ***/
void walk()
{
static int t = 0, steps = 0;
int interval = 1000;
if ((steps++ % interval)==0){
t++;
}
else { // 目標関節角度の設定 (set target gait angles)
for (int leg_no = 0; leg_no < LEG_NUM; leg_no++) {
for (int joint_no = 0; joint_no < JT_NUM; joint_no++) {
THETA[leg_no][joint_no] = gait[t%12][leg_no][joint_no];
}
}
}
moveJoint();
}
void calcAngle() /*** 目標角度の計算 (Calculate target angles) ***/
{
double z0 = -0.4,z1 = -0.37; // z0:地面までの高さ(height to the ground),z1:最高到達点 (highest point)
double y1 = 0.05, fs = 0.2; // y1:左右の変位(defference between right and left),fs:歩幅 (foot step)
double f1 = fs/4, f2 = fs/2, f3 = 3 * fs/4, f4 = fs; // 一時変数 (temporal variables)
double traj[12][LEG_NUM][3] = { // 目標軌道点 (trajectory points)
// leg0: left fore leg, leg1: left rear leg
// leg2: right rear leg, leg3: right fore leg
// 左前leg0遊脚 左後leg1 右後 leg2 右前leg3
{{ 0, y1,z0},{ 0, y1,z0},{ 0, y1,z0},{ 0, y1,z0}},// 0 重心移動(move COG)
{{f2, y1,z1},{ 0, y1,z0},{ 0, y1,z0},{ 0, y1,z0}},// 1 離地(takeoff)
{{f4, y1,z0},{ 0, y1,z0},{ 0, y1,z0},{ 0, y1,z0}},// 2 着地(touchdown)
{{f3,-y1,z0},{-f1,-y1,z0},{-f1,-y1,z0},{-f1,-y1,z0}},// 3 重心移動(move COG)
// leg0 leg1 leg2 遊脚(swing) leg3
{{f3,-y1,z0},{-f1,-y1,z0},{ f1,-y1,z1},{-f1,-y1,z0}},// 4 離地(takeoff)
{{f3,-y1,z0},{-f1,-y1,z0},{ f3,-y1,z0},{-f1,-y1,z0}},// 5 着地(touchdown)
{{f2,-y1,z0},{-f2,-y1,z0},{ f2,-y1,z0},{-f2,-y1,z0}},// 6 重心移動(move COG)
// leg0 leg1 leg2 leg3 遊脚(swing)
{{f2,-y1,z0},{-f2,-y1,z0},{ f2,-y1,z0},{ 0,-y1,z1}},// 7 離地(takeoff)
{{f2,-y1,z0},{-f2,-y1,z0},{ f2,-y1,z0},{ f2,-y1,z0}},// 8 着地(touchdown)
{{f1, y1,z0},{-f3, y1,z0},{ f1, y1,z0},{ f1, y1,z0}},// 9 重心移動(move COG)
// leg0 leg1 遊脚(swing) leg2 leg3
{{f1,y1, z0},{-f1, y1,z1},{ f1, y1,z0},{ f1, y1,z0}},// 10 離地(takeoff)
{{f1,y1, z0},{ f1, y1,z0},{ f1, y1,z0},{ f1, y1,z0}} // 11 着地(touchdown)
};
double angle1, angle2, angle3;
int posture = 2; // 姿勢(posture)
for (int i = 0; i < 12; i++) {
for (int k = 0; k < LEG_NUM; k++) {
// 逆運動学
inverseKinematics(traj[i][k][0],traj[i][k][1],
traj[i][k][2],&angle1, &angle2, &angle3,posture);
gait[i][k][0] = angle1;
gait[i][k][1] = angle2;
gait[i][k][2] = angle3;
}
}
}
// ワールド更新開始イベントから呼び出される
void OnUpdate()
{
static int epoch = 0;
//std::cout << "epoch=" << epoch << std::endl;
if (epoch++ == 0) calcAngle();
walk();
}
private:
// モデルへのポインタ
physics::ModelPtr model;
physics::WorldPtr world;
// ワールド状態のサブスクライブ(講読)
transport::NodePtr node;
transport::SubscriberPtr statsSub;
common::Time simTime;
// 更新イベントコネクションへのポインタ
event::ConnectionPtr updateConnection;
physics::JointPtr hinge_lf1, hinge_lf2, hinge_lf3; // 左前脚の関節
physics::JointPtr hinge_lr1, hinge_lr2, hinge_lr3; // 左後
physics::JointPtr hinge_rr1, hinge_rr2, hinge_rr3; // 右後
physics::JointPtr hinge_rf1, hinge_rf2, hinge_rf3; // 右前
//sensors::RaySensorPtr laser;
//physics::LinkPtr sensor;
double THETA[LEG_NUM][LINK_NUM]; // 関節目標角度
double gait[12][LEG_NUM][JT_NUM] ; // 目標角度(target angle of gait)
double l1, l2, l3; // 脚長 (lenth of links)
double r1, r2, r3; // 脚半径(leg radius)
double gain; // 比例ゲイン
};
// シミュレータへのプラグイン登録
GZ_REGISTER_MODEL_PLUGIN(MobileBasePlugin)
コピペしたらgeditの「保存」ボタンをクリックして~/prog2/7legged/の中に保存し、geditメニューバーの「ファイル(F)」→「終了(Q)」でgeditを終了する。
3. コンパイル設定ファイルの作成
gazeboではソースコードをコンパイル(ビルド)するのにcmakeというシステムを使う。先程から使っている端末で以下のコマンドを実行しgeditを起動する。
$ gedit CMakeLists.txt
CMakeLists.txtはcmakeの設定ファイル。次のソースコードをgeditにコピペし、CMakeLists.txtを保存する。保存場所は今までと同じ~/prog2/7leggedディレクトリ。保存したら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(legged SHARED legged.cc)
target_link_libraries(legged ${GAZEBO_LIBRARIES} ${Boost_LIBRARIES})
4.コンパイル
以下のコマンドを実行し、ソースコードをメイクする。なお、以下を行ってうまくいかない場合は、7legged以下をまとめたこのファイル(7legged150110.tgz)をダウンロード、展開する。
$ cd ~/prog2/7legged
$ mkdir build
$ cd build
$ cmake ../
$ make
5. 4脚ロボットのモデルファイル
- legged_robot_model150115.tgzを~/.gazebo/modelsにダウンロードして展開する。
6. 実行
以下のコマンドを実行し、gazeboを立ち上げよう! gazeboが起動したら画面左側のinsert(挿入)タブから制作したlegged robotを選択する。4脚ロボットが歩行(クロール歩容)をしたら成功。今回はここまで。
$ export GAZEBO_PLUGIN_PATH=~/prog2/7legged/build:$GAZEBO_PLUGIN_PATH
$ gazebo --verbose
コメント