Humanoid RTCs (C++) for Ubuntu
研究やホビーで使用される小型ヒューマノイドロボット(以下、ヒューマノイド)を動作させるRTCを作成する方法を解説します。
このページでは以下の知識を説明なく使用しますので、各解説ページで使用方法を学んでから読み進めてください。
1. はじめに
今回は、以下のRTCを作成し、ヒューマノイドを動作させる方法を学びます。
ヒューマノイドには、HPIジャパン製のGR-001を用います。
作成するRTCは以下のようになります。
-
GR-001
GR-001を動作させます。
GR-001のローカル座標系に対して、位置・姿勢を指示して制御します。
-
TimedPose2DConsoleIn
GR-001の位置・姿勢をコンソールから指令します。
本解説で想定するシステムは、以下の様になります。
シリアルケーブルを通して、PCとGR-001のコントローラ(RPU-11)を接続します。
5. おわりに
小型ヒューマノイドを動作させるRTCを作成しました。
任意の位置に移動できるよう、逆運動学を解き、歩行パターンを生成しました。
このように、リアルタイムに歩行パターンを生成できれるようになればさまざまなシステムにヒューマノイドを組み込むことができます。
今回示した手法はGR-001以外にも応用可能です。
ぜひ、自分のロボット用に応用してRTCを作ってみてください。
参考文献
-
「RPU-10取扱説明書」<http://www.futaba.co.jp/robot/download/manuals>(2014/04/22アクセス)
2. GR-001 RTC
GR-001 RTCを作成します。
このRTCの構成や、アルゴリズムの細かい点についてはロボコンマガジン 2014年5月号をご覧ください。
GR-001 RTCはTimedPose2D型のデータを受け取れるよう作成します。
TimedPose2Dは二次元平面の位置(x, y)と姿勢(heading)を格納できるデータ型です。
指定された位置と姿勢に、以下のような手順で移動させます。
(a) 目標位置までの距離と角度を計算します。
(b) 目標方向に旋回します。
(c) 目標位置まで直進します。
(d) 目標姿勢に移動させます。
それではソースコードを解説します。
-
1, 9行目
数値計算のためにmath.hをインクルードします。
また、mah.hでπ(M_PI)を使用するために_USE_MATH_DEFINESをデファインします。
-
5-7行目
シリアル通信のためにインクルードします。
-
11行目
シリアル通信用のファイルディスクリプタをグローバルで定義します。
-
13-16行目
逆運動学の計算に必要なボディーパラメータを格納する変数をグローバルで定義します。
shin_length: すねリンクの長さ mmを格納します。
thigh_length: ももリンクの長さ mmを格納します。
foot_length: 脚リンクの長さ(すねリンクの長さ + ももリンクの長さ) mmを格納します。
ready_z: 一般的なヒューマノイドロボットは膝を伸ばしたまま歩くことは出来ないので、適度に膝を曲げる必要があります。
この値は足裏の高さ mmに相当し、両足を上げることで結果的に膝を曲げることができます。
-
18-39行目
シリアルポートをオープンする関数です。
シリアル通信に関してはSerial Servo RTCsで解説していますので、そちらをご覧ください。
-
41-43行目
シリアルポートをクローズする関数です。
この関数はLinux環境とWindows環境で、コード中のシリアルポートの操作を揃えるために定義しています。
シリアル通信に関してはSerial Servo RTCsで解説していますので、そちらをご覧ください。
-
46-51行目
シリアルサーボのデータを格納する構造体を定義します。
この構造体は以下のようなメンバを持っています。
id: シリアルサーボのIDを格納する変数です。
angle: シリアルサーボの位置(角度)を格納する変数です。
duration: シリアルサーボの移動時間を格納する変数です。
-
53-57行目
複数のシリアルサーボのデータを格納する構造体を定義します。
先ほど定義したServo構造体をstd::vectorクラスに格納します。
この構造体は以下のようなメンバを持っています。
num: 指令を与えるシリアルサーボの個数を格納する変数です。
servo: 複数のシリアルサーボに対するデータを格納するstd::vectorクラスです。
-
59-71行目
逆運動学を用いて計算した足関節の角度を格納する構造体を定義します。
この構造体は以下のようなメンバを持っています。
th_1: 股ピッチ関節のシリアルサーボの位置(角度)を格納します。
th_2: 膝ピッチ関節のシリアルサーボの位置(角度)を格納します。
th_3: 足首ピッチ関節のシリアルサーボの位置(角度)を格納します。
また、この構造体はRSシリーズのサーボ用に角度を変換するconver_rsメソッドを持っています。
逆運動学を用いて計算した関節角度はラジアンですので度数に変換します。
さらに、RSシリーズのサーボは0.1 deg単位で指示するので、角度を10倍します。
-
73-107行目
ロボットの動作を行う関数です。
void move_robot(Pose型のデータのリファレンス)
複数のRSサーボにロングパケットモードを用いてGR-001のコントローラRPU-11を通して送信します。
RSシリーズのロングパケットモードは以下の様なパケットを送信します。
[Header] [ID] [Flags] [Address] [Length] [Count] [VID] [Data] [VID] [Data] ・・・ [Sum]
Header: パケットの先頭を表します。ここでは0xFA、0xAFとなります。
ID: 常に0x00を指定します。
Flags: 常に0x00を指定します。
Address: メモリーマップ上のアドレスを表します。Lengthに指定した長さのデータをメモリーマップに書き込みます。
Length: サーボ1つ分のデータの長さ(byte数)を指定します。Length = VID + Dataのバイト数
Count: サーボの数を表します。
Sum: チェックサムです。IDからDataまでの各バイトの排他的論理和(XOR)を計算した値を設定します。
また、RPU-11を通してシリアルサーボに命令を送る場合は、以下のようにHeaderとLengthを頭に付けます。
[RPU header] [Length] [RSシリーズの通常パケット]
RPU header: RS485通信のためのパケットを表します。送信のみの場合は0x53Hを使用します。
Length: RSシリーズの通常のパケットの長さを指定します。
-
75-84行目
std::vectorのインスタンスにパケットを格納します。
各パケットは、push_backメソッドを使用して追加していきます。
2番目のLengthにはパケットの長さを格納します。この時点では長さがわからないので仮の値を格納しています。
-
86-93行目
各シリアルサーボのIDや位置、移動時間を順番に格納します。
std::vectorの値にアクセスするときはatメソッドを使用します。
-
95-100行目
チェックサムを計算し、追加します。
-
102行目
パケットの長さを計算します。
sendbufの長さはsizeメソッドでわかります。
この長さにはRPU headerとLengthも含まれるので、全パケットの長さ - 2の値を格納します。
-
104行目
パケットを送信する前に、送信ポートをクリアします。
-
106行目
コマンドを出力します。
frontメソッドはデータの最初の要素を返します。
先頭要素のアドレスを渡し、sizeメソッドで書き込む長さを指定しています。
-
109-112行目
余弦定理を用いて、3つの辺の長さから内角の値を返します。
内角の大きさ calculation_law_cosines(l_1 mm, l_2 mm, l_3 mm)
各辺と求める角度の関係は以下の様になっています。
l_3で指定した辺の対角αの値が戻り値として返されます。
4. 動作確認
作成したRTCを実行して下さい。
GR-001 RTCのポート名は必ず変更し、各環境に合うよう設定してください。
その後、各RTCを接続し、Activateします。
Activate後にTimedPose2DConsoleIn RTCから姿勢と位置を入力します。
するとGR-001が移動するのが確認できます。
なお、GR-001 RTCの実行時に「Permission denied」と表示された場合はsudoを付けて実行してください。
もしくは、以下のコマンドを入力して、dialoutにユーザーを追加してください。
3. TimedPose2DConsoleIn RTC
TimedPose2DConsoleIn RTCを作成します。
テンプレートを以下に従って設定てください。
-
モジュール名
TimedPose2DConsoleIn
-
ベンダ名
あなたの名前
-
モジュールカテゴリ
Console
-
実行周期
1000
-
アクションコールバック
onInitialize
onExecute
-
データポート
・pose2dポート
ポート名(OutPort) pose2d
データ型 RTC::TimedPose2D
変数名 pose2d
表示位置 RIGHT
-
言語
C++
プロジェクトの設定が終わったら、TimedPose2DConsoleIn.cppに以下のコードを記述します。
以下のコードは抜粋となります。生成したテンプレートに当てはめて下さい。
コードの解説をします。
-
3-9行目
RTC::TimedPose2D構造体のメンバを、順にユーザに入力させます。
-
11行目
タイムスタンプをセットします。
-
13行目
ポートから出力します。
以上で、TimedPose2DConsoleInのコーディングは終了です。
コードが入力し終わったら、ビルドを行ってください。

ここで説明するRTCは以下の様な構成で動作を確認しています。
-
ヒューマノイドロボット
HPIジャパン製 GR-001 RS301,302サーボ仕様
テンプレートを以下に従って設定てください。
-
モジュール名
GR001
-
ベンダ名
あなたの名前
-
モジュールカテゴリ
Humanoid
-
実行周期
1000
-
アクションコールバック
onInitialize
onActivated
onDeactivated
onAborting
onExecute
-
データポート
・pose2dポート
ポート名(InPort) pose2d
データ型 RTC::TimedPose2D
変数名 pose2d
表示位置 LEFT
-
コンフィギュレーション
・portコンフィギュレーション: デバイスのポート番号
名称 conf_port
データ型 string
デフォルト値 COM1
変数名 conf_port
・baudrateコンフィギュレーション: 通信のボーレート bps
名称 conf_baudrate
データ型 long
デフォルト値 115200
変数名 conf_baudrate
・walk durationコンフィギュレーション: 歩行時のシリアルサーボの移動時間 10ms
名称 conf_walk_duration
データ型 long
デフォルト値 50
変数名 conf_walk_duration
・step xコンフィギュレーション: 1ステップの移動量 mm
名称 conf_step_x
データ型 double
デフォルト値 40
変数名 conf_step_x
・step thetaコンフィギュレーション: 1ステップの旋回量 deg
名称 conf_step_theta
データ型 double
デフォルト値 30
変数名 conf_step_theta
・step zコンフィギュレーション: 歩行時の足上げ量 mm
名称 conf_step_z
データ型 double
デフォルト値 20
変数名 conf_step_z
・ready zコンフィギュレーション: 初期姿勢 mm(足の高さから算出)
名称 conf_ready_z
データ型 double
デフォルト値 5
変数名 conf_ready_z
・ready durationコンフィギュレーション: 初期姿勢移動時の移動時間 10ms
名称 conf_ready_duration
データ型 short
デフォルト値 200
変数名 conf_ready_duration
・shin lengthコンフィギュレーション: すねリンクの長さ mm
名称 conf_shin_length
データ型 double
デフォルト値 60
変数名 conf_shin_length
・thigh lengthコンフィギュレーション: ももリンクの長さ mm
名称 conf_thigh_length
データ型 double
デフォルト値 50
変数名 conf_thigh_length
・rollコンフィギュレーション: 歩行時の重心移動量 0.1deg
名称 conf_roll
データ型 short
デフォルト値 150
変数名 conf_roll
-
言語
C++
プロジェクトの設定が終わったら、GR001.cppに以下のコードを記述します。
以下のコードは抜粋となります。生成したテンプレートに当てはめて下さい。


-
114-133行目
足先の逆運動学を計算します。
足関節の角度 calculation_ik(足先の踏み出し量 mm, 足先の高さ mm)
各リンクや角度の表記は以下のようになっています。
右脚と左脚の動作にはmove_right_foot関数と、move_left_foot関数を使用します。
-
246-263行目
ロボットの歩行を制御する関数です。
指定された移動量とステップの幅から歩行回数を決定し、最後に初期姿勢をとるように帳尻をあわせます。
void walk(移動量 mm, 1ステップの幅 mm, 足上げ量 mm, ロール軸の移動量 0.1deg, 移動時間 10ms)
-
248-249行目
歩行回数を移動量と1ステップの幅から求めます。
また、ステップ幅以下のあまり量を求めます。
-
251-254行目
求めた歩行回数分歩行させます。
-
256-259行目
あまり量がある場合は、あまり分だけ歩行させます。
-
261-262行目
初期姿勢に移動するよう、最後に帳尻をあわせます。
-
265-289行目
ロボットを右旋回させる関数です。
void step_right_turn(1ステップの回転角 deg , 足上げ量 mm, ロール軸の移動量 0.1deg, 移動時間 10ms)
脚を上げている間に、遊脚している脚のヨー軸を回転させることで旋回します。
-
267-270行目
右脚ヨー軸のシリアルサーボ用のServo型の構造体を定義し、Pose型の構造体に格納します。
-
273行目
右脚を遊脚させます。
-
275-277行目
右脚ヨー軸に指定された1ステップの旋回角度をRSシリーズのサーボ用に10倍します。
その値をPose型の構造体内に格納されたServo型の構造体のメンバであるangleに格納します。
次に、シリアルサーボを動作させ、その間スリープさせます。
-
279行目
右脚を着地させます。
-
282行目
左脚を遊脚させます。
-
254-286行目
右脚ヨー軸を初期位置に移動させ、その間スリープさせます。
-
288行目
左脚を着地させます。
-
291-315行目
ロボットを左旋回させる関数です。
step_left_turn関数と左右が入れ替わっただけで、あとは同様の処理を行います。
-
317-360行目
ロボットの旋回を制御する関数です。
指定された旋回角度 degだけ、1ステップの回転角から旋回回数を決定し、最後に初期姿勢をとるように帳尻をあわせます。
-
319-320行目
旋回回数を旋回角度と1ステップの回転角から求めます。
また、ステップ幅以下のあまり量を求めます。
-
322-331行目
旋回時に腕と脚が干渉するため、左右の脇を開いて干渉を回避します。
脇を適当な角度開くようPose型の構造体を定義し、ロボットを動作させます。
-
333-343行目
旋回角が負の場合は右旋回を行い、0より大きい場合は左旋回を行います。
-
345-355行目
あまりがある場合は、あまり分だけ旋回させます。
-
357-359行目
開いた脇を元に戻します。
-
363-385行目
Activate時にシリアルポートのオープンや、コンフィギュレーションの読み込みを行います。
-
365行目
コンフィギュレーションに設定されたポートと、ボーレートでシリアルポートをオープンします。
-
369-372行目
脛リンク、腿リンクの長さと、初期位置をコンフィギュレーションから読み込みます。
次に、脚リンクの長さを計算します。
-
378行目
ポートがオープンするまで十分な時間スリープします。
-
380行目
ロボットを初期姿勢に移動させます。
-
390行目
ポートをクローズします。
-
398-443行目
onExecute時には、pose2dポートから入力があった場合、指定の位置・姿勢に移動します。
-
402-408行目
pose2dの値を読み込み、読み込んだ値を変数に格納します。
-
410行目
目標位置(x,y)から、移動距離lを求めます。
-
412-424行目
旋回角thetaを求めます。
-
413-424行目
xが0ではないとき旋回角thetaをatan2関数で求めます。
-
417-423行目
もしthetaが0でないとき、thetaだけ旋回させます。
thetaはラジアンですので、度数に変換してturn関数に与えます。
turn関数のその他の引数には、コンフィギュレーションで指定した値を渡します。
最後に、適度にスリープします。
-
426-431行目
もしlが0より大きい場合は、walk関数を使って歩行させます。
walk関数のその他の引数には、コンフィギュレーションで指定した値を渡します。
最後に、適度にスリープします。
-
433-439行目
もしheadingが0ではない場合は、先ほどと同様にturn関数を使って旋回させます。
-
446-453行目
エラー時にポートをクローズします。
以上で、GR-001のコーディングは終了です。
コードが入力し終わったら、ビルドを行ってください。
今回はコードが長く、大変でしょうが頑張って最後までコーディングしてみて下さい。
GR-001以外のヒューマノイドでも、同様の構造でRTC化できると思います。
ぜひ、お手元のヒューマノイドでお試し下さい。

-
116-119行目
脚のリンクの長さと、角度を計算します。
-
121-125行目
角度のphiの値を求めます。
zが0出ないとき、atan2関数を使ってphiの値を求めます。
-
127-132行目
th_1、th_2、th_3の値を求めます。
これらの値は各脚のシリアルサーボの位置(角度)となります。
求めた値をFooAngles型の構造体に格納して返します。
-
135行目
初期姿勢時の各シリアルサーボの位置を格納するFootAngles型の構造体を定義します。
-
137-167行目
ロボットを初期姿勢に移動させます。
void set_ready_position(初期高さ mm, 移動時間 10ms)
初期高さ mmは初期姿勢を計算するための足先の高さです。
ここではready_zを指定することを想定しています。
-
140-141行目
初期姿勢時の各関節の角度を計算し、FooAngles型の構造体に格納します。
また、格納した角度をRSシリーズのサーボ用に角度を変換します。
-
143-144行目
複数のシリアルサーボのデータを格納するPose型の構造体を定義します。
GR-001の脚部のシリアルサーボは10個なので、numメンバには10を指定します。
-
146-158行目
シリアルサーボのデータを格納するServo型の構造体を定義し、各関節角度で初期化します。
RSシリーズのサーボは正の角度値で右方向に移動します。
意図したとおりに関節が動作するよう、関節の角度にマイナスを付けてリバースしています。
-
159-162行目
Pose型の構造体のservoメンバに、Servo型のデータを格納します。
-
164-166行目
Pose型のデータをmove_robot関数に与えて、GR-001を動作させます。
その後、動作時間の間スリープさせます。
-
169-200行目
ロボットの右脚を動作させる関数です。
void move_right_foot(足の踏み出し量 mm, 足のあげ量 mm, ロール軸の移動量 0.1deg, 移動時間 10ms)
足の踏み出し量と足のあげ量は、足先の目標位置(x, z)です。
ロール軸の移動量は、重心を移動するための股と足首関節の移動量です。
ロボットが脚を上げるには、反対の脚に重心を移動する必要があり、そのときのにロール軸を回転させて移動させます。
各ロール軸に体重移動量を指定する以外は、set_ready_position関数と同様の処理を行います。
-
202-233行目
ロボットの左脚を動作させる関数です。
move_right_foot関数と左右が入れ替わっただけで、あとは同様の処理を行います。
-
235-244行目
ロボットを歩行させる関数です。
void step_forward(1ステップの幅 mm, 足あげ量 mm, ロール軸の移動量 0.1deg, 移動時間 10ms)
ステップは1回の移動量です。右脚と左脚はstep / 2ずつ移動します。
1ステップの幅と足のあげ量の関係は下図の用になります。
今回はstep / 4の位置で脚をあげることで、三角形の足先軌道を描きます。

動作の様子は、以下の動画をご覧ください。
$ sudo gpasswd –a <ユーザ名> dialout