RTコンポーネントにおけるトルク指令の入力

ここでは、Choreonoidを用いて、ロボットの動作パターンを作成する方法、そのパターンをロボットで実行できるように前節で作成したRTコンポーネントを拡張する方法について解説します。

RTコンポーネントの拡張

関節のPD制御を行うために、前の節で作成したRTコンポーネントRobotControllerRTCを拡張します。PD制御を行うためには、関節へのトルク指令を出力する必要があるため、トルク指令用のデータポートをRTCBuilderを用いて追加します。[RobotControllerRTC]の[データポート]タブから、追加([Add])するデータポートのプロファイルは次のようになります。

  • OutPort プロファイル:

    ポート名: u
    データ型: RTC::TimeDoubleSeq
    変数名: torque
    表示位置: RIGHT

データポートを追加し、[基本]タブから再度コード生成([コード生成]と[Generate])を行ったら、ビルド(buildディレクトリにてmake)ができることを確認しておきます。

コントローラのソースコード

コントローラのヘッダファイル($HOME/workspace/RobotControllerRTC/include/RobotControllerRTC.h)は以下のようになります。(編集済みのサンプルファイルはsamples/tutorials/rtc/RobotControllerRTC3/include/RobotControllerRTC.hに収録されています)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/*!
 * @file  RobotControllerRTC.h
 * @brief Robot Controller component
 * @date  $Date$
 *
 * $Id$
 */

#ifndef ROBOTCONTROLLERRTC_H
#define ROBOTCONTROLLERRTC_H

#include <rtm/idl/BasicDataTypeSkel.h>
#include <rtm/idl/ExtendedDataTypesSkel.h>
#include <rtm/idl/InterfaceDataTypesSkel.h>
#include <cnoid/MultiValueSeq>
#include <vector>

using namespace RTC;

#include <rtm/Manager.h>
#include <rtm/DataFlowComponentBase.h>
#include <rtm/CorbaPort.h>
#include <rtm/DataInPort.h>
#include <rtm/DataOutPort.h>

class RobotControllerRTC
  : public RTC::DataFlowComponentBase
{
  public:
    RobotControllerRTC(RTC::Manager* manager);
    ~RobotControllerRTC();
    virtual RTC::ReturnCode_t onInitialize();
    virtual RTC::ReturnCode_t onActivated(RTC::UniqueId ec_id);
    virtual RTC::ReturnCode_t onDeactivated(RTC::UniqueId ec_id);
    virtual RTC::ReturnCode_t onExecute(RTC::UniqueId ec_id);

 protected:
   RTC::TimedDoubleSeq m_angle;
   InPort<RTC::TimedDoubleSeq> m_angleIn;

   RTC::TimedCharSeq m_torque;
   OutPort<RTC::TimedCharSeq> m_torqueOut;
 private:
   cnoid::MultiValueSeqPtr qseq;
   std::vector<double> q0;
   cnoid::MultiValueSeq::Frame oldFrame;
   int currentFrame;
   double timeStep_;

};

extern "C"
{
  DLL_EXPORT void RobotControllerRTCInit(RTC::Manager* manager);
};

#endif // ROBOTCONTROLLERRTC_H

今回はトルクの出力をしなければならないので、出力ポートのための設定が増加しています。 RTC::OutPort<RTC::TimedDoubleSeq> はRTCの出力ポートを表す型であり、出力ポートを操作するにはこれを利用します。

コントローラのソースコード($HOME/workspace/RobotControllerRTC/src/RobotControllerRTC.cpp)は以下のようになります。(編集済みのファイルは samples/tutorials/rtc/RobotControllerRTC3/src/RobotControllerRTC.cppに収録されています)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/*!
 * @file  RobotControllerRTC.cpp
 * @brief Robot Controller component
 * @date $Date$
 *
 * $Id$
 */

#include "RobotControllerRTC.h"
#include <iostream>
#include <cnoid/BodyMotion>
#include <cnoid/ExecutablePath>
#include <cnoid/FileUtil>

using namespace std;
using namespace cnoid;

namespace {

static const double pgain[] = {
    50000.0, 30000.0, 30000.0, 30000.0, 30000.0,
    80000.0, 80000.0, 10000.0, 3000.0, 30000.0,
    30000.0, 80000.0, 3000.0, 30000.0, 10000.0,
    3000.0, 3000.0, 30000.0, 30000.0, 10000.0,
    3000.0, 30000.0, 3000.0, 3000.0, 3000.0,
    3000.0, 3000.0, 3000.0, 3000.0, 3000.0,
    3000.0, 3000.0, 10000.0, 3000.0, 3000.0,
    30000.0, 3000.0, 3000.0, 3000.0, 3000.0,
    3000.0, 3000.0, 3000.0, 3000.0,
    };

static const double dgain[] = {
    100.0, 100.0, 100.0, 100.0, 100.0,
    100.0, 100.0, 100.0, 100.0, 100.0,
    100.0, 100.0, 100.0, 100.0, 100.0,
    100.0, 100.0, 100.0, 100.0, 100.0,
    100.0, 100.0, 100.0, 100.0, 100.0,
    100.0, 100.0, 100.0, 100.0, 100.0,
    100.0, 100.0, 100.0, 100.0, 100.0,
    100.0, 100.0, 100.0, 100.0, 100.0,
    100.0, 100.0, 100.0, 100.0,
    };
};

// Module specification
static const char* robotcontrollerrtc_spec[] =
  {
    "implementation_id", "RobotControllerRTC",
    "type_name",         "RobotControllerRTC",
    "description",       "Robot Controller component",
    "version",           "1.0.0",
    "vendor",            "AIST",
    "category",          "Generic",
    "activity_type",     "PERIODIC",
    "kind",              "DataFlowComponent",
    "max_instance",      "1",
    "language",          "C++",
    "lang_type",         "compile",
    ""
  };

RobotControllerRTC::RobotControllerRTC(RTC::Manager* manager)
  : RTC::DataFlowComponentBase(manager),
    m_angleIn("q", m_angle),
    m_torqueOut("u", m_torque)
{
}

RobotControllerRTC::~RobotControllerRTC()
{

}


RTC::ReturnCode_t RobotControllerRTC::onInitialize()
{
  addInPort("q", m_angleIn);
  addOutPort("u", m_torqueOut);

  return RTC::RTC_OK;
}

RTC::ReturnCode_t RobotControllerRTC::onActivated(RTC::UniqueId ec_id)
{
  if(!qseq){
    string filename = getNativePathString(
      boost::filesystem::path(shareDirectory())
      / "motion" / "RobotPattern.yaml");

    BodyMotion motion;

    if(!motion.loadStandardYAMLformat(filename)){
      cout << motion.seqMessage() << endl;
      return RTC::RTC_ERROR;
    }
    qseq = motion.jointPosSeq();
    if(qseq->numFrames() == 0){
      cout << "Empty motion data." << endl;
      return RTC::RTC_ERROR;
    }
    q0.resize(qseq->numParts());
    timeStep_ = qseq->getTimeStep();
  }

  m_torque.data.length(qseq->numParts());

  if(m_angleIn.isNew()){
    m_angleIn.read();
  }
  for(int i=0; i < qseq->numParts(); ++i){
    q0[i] = m_angle.data[i];
  }
  oldFrame = qseq->frame(0);
  currentFrame = 0;

  return RTC::RTC_OK;
}


RTC::ReturnCode_t RobotControllerRTC::onDeactivated(RTC::UniqueId ec_id)
{
  return RTC::RTC_OK;
}

RTC::ReturnCode_t RobotControllerRTC::onExecute(RTC::UniqueId ec_id)
{
  if(m_angleIn.isNew()){
    m_angleIn.read();
  }

  MultiValueSeq::Frame frame;

  if(currentFrame > qseq->numFrames()){
    frame = oldFrame;
  }else{
    frame = qseq->frame(currentFrame++);
  }

  for(int i=0; i < frame.size(); i++){
    double q_ref = frame[i];
    double q = m_angle.data[i];
    double dq_ref = (q_ref - oldFrame[i]) / timeStep_;
    double dq = (q - q0[i]) / timeStep_;
    m_torque.data[i] = (q_ref - q) * pgain[i]/100.0 + (dq_ref - dq) * dgain[i]/100.0;
    q0[i] = q;
  }
  oldFrame = frame;

  m_torqueOut.write();

  return RTC::RTC_OK;
}


extern "C"
{
  DLL_EXPORT void RobotControllerRTCInit(RTC::Manager* manager)
  {
    coil::Properties profile(robotcontrollerrtc_spec);
    manager->registerFactory(profile,
                             RTC::Create<RobotControllerRTC>,
                             RTC::Delete<RobotControllerRTC>);
  }
};

出力ポートに関する設定は、入力ポートの場合と関数名が異なるだけでよく似ています。

onActivated() のときの処理に注目しましょう。この関数はRTCが有効化された際に一度だけ呼ばれます。 ここで、Choreonoidの共有ディレクトリからRobotPattern.yamlを読み出しています。 これはロボットの全関節角度の軌道を記述した動作パターンファイルです。 motion.loadStandardYAMLformat() によりモーションデータに変換します。 onActivated()では初期値の設定も行っています。

onExecute()ではトルクの計算と出力の処理が追加されました。 関節角度を読み込む部分のコードはこれまでと同じですが、 m_torque.data[i] に計算したトルクの値を代入しています。 ここでは簡単なPD制御によりトルクの値を求めています。 各関節毎のPgainとDgainはソースコードの先頭付近に固定値で定義されています。 ロボットがうまく制御できない場合はこの値を調整する必要があります。 m_torque.data にセットした値は m_torqueOut.write() により実際のロボットの制御トルクとして出力されます。 出力ポートは値をセットするだけなので入力ポートよりも簡単です。

CMakeLists.txtの編集

このコントローラはChoreonoidのライブラリが提供している機能を使用しているため、Choreonoidのライブラリをリンクする必要があります。そこで$HOME/workspace/RobotControllerRTC/src/CMakeLists.txtを次のように編集します。(編集済みのファイルはsamples/tutorials/rtc/RobotControllerRTC3/src/CMakeLists.txtに収録されています)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
set(comp_srcs RobotControllerRTC.cpp )
set(standalone_srcs RobotControllerRTCComp.cpp)

if (DEFINED OPENRTM_INCLUDE_DIRS)
  string(REGEX REPLACE "-I" ";"
    OPENRTM_INCLUDE_DIRS "${OPENRTM_INCLUDE_DIRS}")
  string(REGEX REPLACE " ;" ";"
    OPENRTM_INCLUDE_DIRS "${OPENRTM_INCLUDE_DIRS}")
endif (DEFINED OPENRTM_INCLUDE_DIRS)

if (DEFINED OPENRTM_LIBRARY_DIRS)
  string(REGEX REPLACE "-L" ";"
    OPENRTM_LIBRARY_DIRS "${OPENRTM_LIBRARY_DIRS}")
  string(REGEX REPLACE " ;" ";"
    OPENRTM_LIBRARY_DIRS "${OPENRTM_LIBRARY_DIRS}")
endif (DEFINED OPENRTM_LIBRARY_DIRS)

if (DEFINED OPENRTM_LIBRARIES)
  string(REGEX REPLACE "-l" ";"
    OPENRTM_LIBRARIES "${OPENRTM_LIBRARIES}")
  string(REGEX REPLACE " ;" ";"
    OPENRTM_LIBRARIES "${OPENRTM_LIBRARIES}")
endif (DEFINED OPENRTM_LIBRARIES)

include_directories(${PROJECT_SOURCE_DIR}/include)
include_directories(${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME})
include_directories(${PROJECT_BINARY_DIR})
include_directories(${PROJECT_BINARY_DIR}/idl)
include_directories(${OPENRTM_INCLUDE_DIRS})
include_directories(${OMNIORB_INCLUDE_DIRS})
add_definitions(${OPENRTM_CFLAGS})
add_definitions(${OMNIORB_CFLAGS})

MAP_ADD_STR(comp_hdrs "../" comp_headers)

link_directories(${OPENRTM_LIBRARY_DIRS})
link_directories(${OMNIORB_LIBRARY_DIRS})

include(FindPkgConfig)
pkg_check_modules(CNOID choreonoid-body-plugin)
include_directories(${CNOID_INCLUDE_DIRS})
link_directories(${CNOID_LIBRARY_DIRS})

add_library(${PROJECT_NAME} ${LIB_TYPE} ${comp_srcs}
  ${comp_headers} ${ALL_IDL_SRCS})
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
set_source_files_properties(${ALL_IDL_SRCS} PROPERTIES GENERATED 1)
if(NOT TARGET ALL_IDL_TGT)
  add_custom_target(ALL_IDL_TGT)
endif(NOT TARGET ALL_IDL_TGT)
add_dependencies(${PROJECT_NAME} ALL_IDL_TGT)
target_link_libraries(${PROJECT_NAME} ${OPENRTM_LIBRARIES} ${CNOID_LIBRARIES} boost_system boost_filesystem)

add_executable(${PROJECT_NAME}Comp ${standalone_srcs}
  ${comp_srcs} ${comp_headers} ${ALL_IDL_SRCS})
target_link_libraries(${PROJECT_NAME}Comp ${OPENRTM_LIBRARIES} ${CNOID_LIBRARIES} boost_system boost_filesystem)

install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}Comp
    EXPORT ${PROJECT_NAME}
    RUNTIME DESTINATION ${BIN_INSTALL_DIR} COMPONENT component
    LIBRARY DESTINATION ${LIB_INSTALL_DIR} COMPONENT component
    ARCHIVE DESTINATION ${LIB_INSTALL_DIR} COMPONENT component)
install(EXPORT ${PROJECT_NAME}
    DESTINATION ${LIB_INSTALL_DIR}/${PROJECT_NAME}
    FILE ${PROJECT_NAME}Depends.cmake)

コントローラのビルド

buildディレクトリ内にて再度makeをし、前節と同じ場所にRTCをインストールします。

$ make
$ sudo cp -p src/RobotControllerRTC.so /usr/lib/choreonoid-1.5/rtc

Choreonoidの起動

前節で作成したプロジェクトファイル、sample2.cnoidを開きます。もしくは、samples/tutorials/cnoid/sample2.cnoidでも構いません。

$ choreonoid sample2.cnoid

ポーズ列の追加

まずアイテムビューで「JVRC」を選択します。 次に、「メニュー」の「ファイル」「新規」より「ポーズ列」を選択し「SampleMotion」という名前で追加します。

../_images/motion.png

次に、[表示]-[ビューの表示]-[ポーズロール]を選択します。次の画面が表示されるはずです。

../_images/pose_role.png

基準の姿勢を作るため、アイテムビューで「JVRC」を選択し、ツールバーにある「選択ボディを初期姿勢に」のボタンを押します。

../_images/pose_toolbar.png

ポーズロールにおいて、1.0 を選択して「挿入」を押します。

../_images/poserole_neck_01.png

今回は首を左右に振る動作パターンを作成します。以下の手順でポーズを作成してください。

  1. ポーズロールにおいて 2.0 を選択し、関節スライダにおいて首関節のヨー軸「NECK_Y」を 70.0 にセットし、ポーズロールの「挿入」を押す。

    ../_images/poserole_neck_02.png
  2. ポーズロールにおいて 3.0 を選択し、関節スライダにおいて首関節のヨー軸「NECK_Y」を 0.0 にセットし、ポーズロールの「挿入」を押す。

    ../_images/poserole_neck_03.png
  3. ポーズロールにおいて 4.0 を選択し、関節スライダにおいて首関節のヨー軸「NECK_Y」を -70.0 にセットし、ポーズロールの「挿入」を押す。

    ../_images/poserole_neck_04.png
  4. ポーズロールにおいて 5.0 を選択し、関節スライダにおいて首関節のヨー軸「NECK_Y」を 0.0 にセットし、ポーズロールの「挿入」を押す。

    ../_images/poserole_neck_05.png
  5. ポーズロールにおいて 6.0 を選択し、関節スライダにおいて首関節のヨー軸「NECK_R」を -50.0 にセットし、ポーズロールの「挿入」を押す。

    ../_images/poserole_neck_06.png

ポーズロールで作成したのはキーフレームと呼びます。これより、プログラムで使用するモーションを生成させます。 ツールバーから「ボディモーションの生成」ボタンを押します。

../_images/motion_toolbar.png

モーションはツールバーのボタンで手動で生成しなくても、キーフレームの更新時に自動生成することができます。 これを有効にするにはツールバーの「自動更新モード」のボタンをオンにしてください。

../_images/motion_toolbar2.png

SampleMotion の子供に motion があるので、これを選択し[ファイル]-[名前を付けて選択アイテムを保存]します。

../_images/item_motion.png

ファイル名はRobotPattern.yamlとし、適当なディレクトリ(例えば/tmp)に保存します。その後でコントローラが参照するChoreonoidの共有ディレクトリに次のコマンドで移動します。

$ sudo mv RobotPattern.yaml /usr/share/choreonoid-1.5/motion

コントローラの設定

Choreonoidには1体のロボットに複数のコントローラを割り当てる機能があります。今JVRCの子アイテムとしてBodyRTCとSampleMotionの2つがありますが、これら2つのうちどちらをシミュレーション時に使用するかを指定する必要があります。ここではBodyRTCを使いますのでBodyRTCの方にチェックを入れておきます。

../_images/choose_bodyrtc.png

シミュレーションを実行する

シミュレーションを実行すると今度はロボットが崩れ落ちることなく、先ほど作成した首振りのモーションを実行しているはずです。この時RTSystemEditorでRTCの接続を確認すると、下図のようになっており、フィードバックループができていることが確認できます。

../_images/openrtp_sample3.png

注釈

別のシーンビューを生成して、同時にカメラの映像を表示してみましょう。

メインメニュー「表示」の「ビューの生成」から「シーン」を選択します。次のウィンドウが表示されるので OK をクリックします。

../_images/make_sceneview.png

新しいシーンビュー「シーン2」が生成されます。 「シーン2」タブをクリックして選択し、カメラの選択ボタンでロボットのカメラ「JVRC - rcamera」などに表示を切り替えます。

../_images/simulation_torque_2.png

「シーン2」タブをドラッグして二つのシーンを同時に表示することもできます。

../_images/simulation_torque_3.png

プロジェクトの保存

シミュレーションの実行が終わったら、プロジェクトを保存しておきましょう。[ファイル]-[名前を付けてプロジェクトを保存]を選択し、ファイル名をsample3.cnoidとして保存します。

サンプルファイルについて

このチュートリアルで作成されるプロジェクトファイルはcnoid/sample3.cnoidに、コントローラのソースコードはrtc/RobotControllerRTC3に収録されています。