Skip to content

Commit fe52522

Browse files
Shota Aokiknjinki
andauthored
逆運動学ライブラリとX7のサンプルを追加 (#10)
* FKのテストをlink1まで追加 * FKのテストを実装 * cherry-pick find_routeを実装 * cherry-pick find_routeに例外処理を追加 * cherry-pick 型エイリアスを作成 * get_q_listを実装 * calc_errorを実装 * calc_basic_jacobianを実装(テストは未実施) * cherry-pick set_q_listを実装 * calc_errorをcalc_error_Rにリネーム * calc_error_pを実装 * calc_errorを実装 * Inverse Kinematics LMを実装。テスト方法について検討中 * CRANE-X7のリンクパラメータを用いたテストを追加 * 失敗するテストをコメントアウト(後ほど修正する) * linkクラスにdxl_idを追加 * linkクラスにset_q_within_limitを追加 * set_q_listにwithin_limitオプションを追加 * IK演算時に関節位置制限を設ける。IKのテストに失敗しているため要調査 * rotation_to_omegaを実装 * calc_error_Rの実装を修正。 * IKを修正。後面のIKに失敗する、上面の特異姿勢IKに失敗する問題が残っている * 関節可動範囲を取得できるようにhardwareを更新 * x7_inverse_kinematicsサンプルを追加 * set_q_listのwithin_limitオプションのテストを追加 * サンプルとREADMEの更新 * READMEのlinkクラスを更新 * READMEの目次を更新 * fix typo * Update rt_manipulators_lib/src/kinematics_utils.cpp Co-authored-by: kenjiinukai <36922159+kenjiinukai@users.noreply.github.com> * Update rt_manipulators_lib/src/kinematics_utils.cpp Co-authored-by: kenjiinukai <36922159+kenjiinukai@users.noreply.github.com> * rotation_to_omegaのコメントを修正 * rotation_to_omegaをrotation_to_axis_angle_representationに変更 * Update rt_manipulators_lib/src/kinematics_utils.cpp Co-authored-by: kenjiinukai <36922159+kenjiinukai@users.noreply.github.com> * Update rt_manipulators_lib/src/kinematics_utils.cpp Co-authored-by: kenjiinukai <36922159+kenjiinukai@users.noreply.github.com> * inverse_kinematics_LMのパラメータを定数にした * IKのリファクタリング * IKのomegaを0.01に変更 * IKについてREADMEの文言を更新 Co-authored-by: kenjiinukai <36922159+kenjiinukai@users.noreply.github.com>
1 parent a482ba0 commit fe52522

File tree

16 files changed

+1047
-2
lines changed

16 files changed

+1047
-2
lines changed

rt_manipulators_lib/include/hardware.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ class Hardware {
6060
bool get_temperature(const uint8_t id, int8_t& temperature);
6161
bool get_temperature(const std::string& joint_name, int8_t& temperature);
6262
bool get_temperatures(const std::string& group_name, std::vector<int8_t>& temperatures);
63+
bool get_max_position_limit(const uint8_t & id, double & max_position_limit);
64+
bool get_min_position_limit(const uint8_t & id, double & min_position_limit);
6365
bool set_position(const uint8_t id, const double position);
6466
bool set_position(const std::string& joint_name, const double position);
6567
bool set_positions(const std::string& group_name, std::vector<double>& positions);

rt_manipulators_lib/include/hardware_joints.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ class Joints{
6464
bool get_temperature(const dxl_id_t & id, temperature_t & temperature);
6565
bool get_temperature(const joint_name_t & joint_name, temperature_t & temperature);
6666
bool get_temperatures(const group_name_t & group_name, std::vector<temperature_t>& temperatures);
67+
bool get_max_position_limit(const dxl_id_t & id, position_t & max_position_limit);
68+
bool get_min_position_limit(const dxl_id_t & id, position_t & min_position_limit);
6769
bool set_position(const dxl_id_t & id, const position_t & position);
6870
bool set_position(const joint_name_t & joint_name, const position_t & position);
6971
bool set_positions(const group_name_t & group_name, const std::vector<position_t> & positions);

rt_manipulators_lib/include/kinematics.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,19 @@
1515
#ifndef RT_MANIPULATORS_LIB_INCLUDE_KINEMATICS_HPP_
1616
#define RT_MANIPULATORS_LIB_INCLUDE_KINEMATICS_HPP_
1717

18+
#include <eigen3/Eigen/Dense>
1819
#include <vector>
1920

21+
#include "kinematics_utils.hpp"
2022
#include "link.hpp"
2123

2224
namespace kinematics {
2325

2426
void forward_kinematics(std::vector<manipulators_link::Link> & links, const int & start_id);
25-
27+
bool inverse_kinematics_LM(
28+
const kinematics_utils::links_t & links, const kinematics_utils::link_id_t & target_id,
29+
const Eigen::Vector3d & target_p, const Eigen::Matrix3d & target_R,
30+
kinematics_utils::q_list_t & result_q_list);
2631
} // namespace kinematics
2732

2833
#endif // RT_MANIPULATORS_LIB_INCLUDE_KINEMATICS_HPP_

rt_manipulators_lib/include/kinematics_utils.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,35 @@
1616
#define RT_MANIPULATORS_LIB_INCLUDE_KINEMATICS_UTILS_HPP_
1717

1818
#include <eigen3/Eigen/Dense>
19+
#include <map>
1920
#include <string>
2021
#include <vector>
2122

2223
#include "link.hpp"
2324

2425
namespace kinematics_utils {
2526

27+
using links_t = std::vector<manipulators_link::Link>;
28+
using link_id_t = unsigned int;
29+
using q_list_t = std::map<link_id_t, double>;
30+
2631
std::vector<manipulators_link::Link> parse_link_config_file(const std::string & file_path);
2732
void print_links(const std::vector<manipulators_link::Link> & links, const int & start_id);
2833
Eigen::Matrix3d skew_symmetric_matrix_for_cross_product(const Eigen::Vector3d & v);
2934
Eigen::Matrix3d rodrigues(const Eigen::Vector3d & a, const double theta);
3035
Eigen::Vector3d rotation_to_euler_ZYX(const Eigen::Matrix3d & mat);
3136
Eigen::Matrix3d rotation_from_euler_ZYX(
3237
const double & z, const double & y, const double & x);
38+
Eigen::Vector3d rotation_to_axis_angle_representation(const Eigen::Matrix3d & mat);
39+
std::vector<link_id_t> find_route(const links_t & links, const link_id_t & target_id);
40+
q_list_t get_q_list(const links_t & links, const std::vector<link_id_t> & id_list);
41+
bool set_q_list(links_t & links, const q_list_t & q_list, const bool & within_limit = false);
42+
Eigen::Vector3d calc_error_R(const Eigen::Matrix3d & target, const Eigen::Matrix3d & current);
43+
Eigen::Vector3d calc_error_p(const Eigen::Vector3d & target, const Eigen::Vector3d & current);
44+
Eigen::VectorXd calc_error(
45+
const Eigen::Vector3d & target_p, const Eigen::Matrix3d & target_R,
46+
const manipulators_link::Link & current_link);
47+
Eigen::MatrixXd calc_basic_jacobian(const links_t & links, const link_id_t & target_id);
3348

3449
} // namespace kinematics_utils
3550

rt_manipulators_lib/include/link.hpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#define RT_MANIPULATORS_LIB_INCLUDE_LINK_HPP_
1717

1818
#include <eigen3/Eigen/Dense>
19+
20+
#include <algorithm>
1921
#include <string>
2022

2123
namespace manipulators_link {
@@ -25,7 +27,8 @@ namespace manipulators_link {
2527
class Link{
2628
public:
2729
Link():
28-
name("ダミーリンク"), sibling(0), child(0), parent(0), q(0), dq(0), ddq(0), m(0) {
30+
name("ダミーリンク"), sibling(0), child(0), parent(0), q(0), dq(0), ddq(0), m(0), dxl_id(0),
31+
min_q(0), max_q(0) {
2932
// 0ベクトル、単位行列で初期化
3033
p.setZero();
3134
R.setIdentity();
@@ -52,6 +55,10 @@ class Link{
5255
double m; // 質量
5356
Eigen::Vector3d c; // 自リンクに対する重心位置
5457
Eigen::Matrix3d I; // 自リンクに対する慣性テンソル
58+
int dxl_id; // 対応するDynamixelのID
59+
double min_q; // 関節位置の下限値
60+
double max_q; // 関節位置の上限値
61+
void set_q_within_limit(const double & desired_q) { q = std::clamp(desired_q, min_q, max_q); }
5562
};
5663

5764
} // namespace manipulators_link

rt_manipulators_lib/src/hardware.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,14 @@ bool Hardware::get_temperatures(const std::string& group_name, std::vector<int8_
448448
return joints_.get_temperatures(group_name, temperatures);
449449
}
450450

451+
bool Hardware::get_max_position_limit(const uint8_t & id, double & max_position_limit) {
452+
return joints_.get_max_position_limit(id, max_position_limit);
453+
}
454+
455+
bool Hardware::get_min_position_limit(const uint8_t & id, double & min_position_limit) {
456+
return joints_.get_min_position_limit(id, min_position_limit);
457+
}
458+
451459
bool Hardware::set_position(const uint8_t id, const double position) {
452460
return joints_.set_position(id, position);
453461
}

rt_manipulators_lib/src/hardware_joints.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,24 @@ bool Joints::get_temperatures(
211211
return true;
212212
}
213213

214+
bool Joints::get_max_position_limit(const dxl_id_t & id, position_t & max_position_limit) {
215+
if (!has_joint(id)) {
216+
std::cerr << "ID:" << std::to_string(id) << "のジョイントは存在しません." << std::endl;
217+
return false;
218+
}
219+
max_position_limit = joint(id)->max_position_limit();
220+
return true;
221+
}
222+
223+
bool Joints::get_min_position_limit(const dxl_id_t & id, position_t & min_position_limit) {
224+
if (!has_joint(id)) {
225+
std::cerr << "ID:" << std::to_string(id) << "のジョイントは存在しません." << std::endl;
226+
return false;
227+
}
228+
min_position_limit = joint(id)->min_position_limit();
229+
return true;
230+
}
231+
214232
bool Joints::set_position(const dxl_id_t & id, const position_t & position) {
215233
if (!has_joint(id)) {
216234
std::cerr << "ID:" << std::to_string(id) << "のジョイントは存在しません." << std::endl;

rt_manipulators_lib/src/kinematics.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,73 @@ void forward_kinematics(std::vector<manipulators_link::Link> & links, const int
4444
forward_kinematics(links, links[start_id].child);
4545
}
4646

47+
bool inverse_kinematics_LM(
48+
const kinematics_utils::links_t & links, const kinematics_utils::link_id_t & target_id,
49+
const Eigen::Vector3d & target_p, const Eigen::Matrix3d & target_R,
50+
kinematics_utils::q_list_t & result_q_list) {
51+
// 参考: https://www.jstage.jst.go.jp/article/jrsj/29/3/29_3_269/_pdf/-char/ja
52+
auto calc_links = links;
53+
auto route = kinematics_utils::find_route(links, target_id);
54+
auto q_list = kinematics_utils::get_q_list(links, route);
55+
56+
const double we_pos = 1 / 0.1; // 位置成分の重み (代表長さの逆数)
57+
const double we_ang = 1 / (2*M_PI); // 姿勢成分の重み
58+
const int num_of_iterations = 100; // qを更新するための反復回数
59+
const double omega = 0.01; // 0.1 ~ 0.001 * 代表リンク長の2乗
60+
const double initial_q_value = 0; // 初期値を0にすると特異姿勢になるため、適当な角度を設定する
61+
const double error_tolerance = 1.0E-6; // 誤差の許容量。誤差がこれより小さければ反復計算を終える
62+
63+
// We : 拘束条件に対する重み行列
64+
auto We_vec = Eigen::VectorXd(6);
65+
We_vec << we_pos, we_pos, we_pos, we_ang, we_ang, we_ang;
66+
Eigen::MatrixXd We = We_vec.asDiagonal();
67+
68+
// qを0にリセットする
69+
for (auto q_i = q_list.begin(); q_i != q_list.end(); ++q_i) {
70+
q_i->second = initial_q_value;
71+
}
72+
73+
// qをセットしてリンク情報を更新する
74+
bool set_q_within_limit = true;
75+
kinematics_utils::set_q_list(calc_links, q_list, set_q_within_limit);
76+
forward_kinematics(calc_links, 1);
77+
78+
for (int n=0; n < num_of_iterations; n++) {
79+
// 基礎ヤコビ行列を計算
80+
auto J = kinematics_utils::calc_basic_jacobian(calc_links, target_id);
81+
82+
// 位置・姿勢の誤差を計算
83+
auto error = kinematics_utils::calc_error(target_p, target_R, calc_links[target_id]);
84+
85+
// 誤差が小さければ終了
86+
if (error.norm() < error_tolerance) {
87+
result_q_list = q_list;
88+
return true;
89+
}
90+
91+
// 減衰因子の計算
92+
auto E = 0.5 * error.transpose() * We * error;
93+
auto Wn_ = omega * Eigen::MatrixXd::Identity(q_list.size(), q_list.size());
94+
auto Wn = E * Eigen::MatrixXd::Identity(q_list.size(), q_list.size()) + Wn_;
95+
96+
// LM法の更新則
97+
auto H = J.transpose() * We * J + Wn;
98+
auto dq = H.inverse() * J.transpose() * We * error; // H-1 * g
99+
100+
// dqをqに加算
101+
int dq_i = 0;
102+
for (auto q_i = q_list.begin(); q_i != q_list.end(); ++q_i) {
103+
q_i->second += dq(dq_i);
104+
dq_i++;
105+
}
106+
107+
// qをセットしてリンク情報を更新する
108+
kinematics_utils::set_q_list(calc_links, q_list, set_q_within_limit);
109+
forward_kinematics(calc_links, 1);
110+
}
111+
112+
result_q_list = q_list;
113+
return false;
114+
}
115+
47116
} // namespace kinematics

rt_manipulators_lib/src/kinematics_utils.cpp

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
#include <algorithm>
1516
#include <fstream>
1617
#include <iostream>
18+
#include <limits>
1719
#include <string>
1820

1921
#include "kinematics_utils.hpp"
@@ -57,6 +59,7 @@ std::vector<manipulators_link::Link> parse_link_config_file(const std::string &
5759
const int COL_INERTIA_YZ = 22;
5860
const int COL_INERTIA_ZZ = 23;
5961
const int COL_AXIS_OF_ROTATION = 29;
62+
const int COL_DXL_ID = 32;
6063
const double MM_TO_METERS = 1e-3;
6164
const double MM2_TO_METERS2 = 1e-9;
6265
const double G_TO_KG = 1e-3;
@@ -157,6 +160,11 @@ std::vector<manipulators_link::Link> parse_link_config_file(const std::string &
157160
// link.c = rot * link.c;
158161
// link.I = rot * link.I * rot.transpose();
159162

163+
try {
164+
link.dxl_id = std::stoi(str_vec[COL_DXL_ID]);
165+
} catch (...) {
166+
}
167+
160168
links.push_back(link);
161169
}
162170

@@ -228,4 +236,117 @@ Eigen::Matrix3d rotation_from_euler_ZYX(
228236
return q.matrix();
229237
}
230238

239+
Eigen::Vector3d rotation_to_axis_angle_representation(const Eigen::Matrix3d & mat) {
240+
// 姿勢行列から等価角軸ベクトルへの変換
241+
// 参考:https://www.jstage.jst.go.jp/article/jrsj/29/3/29_3_269/_pdf/-char/ja
242+
auto l = Eigen::Vector3d(
243+
mat(2, 1) - mat(1, 2),
244+
mat(0, 2) - mat(2, 0),
245+
mat(1, 0) - mat(0, 1));
246+
auto l_norm = l.norm();
247+
248+
if (l_norm != 0) {
249+
return (std::atan2(l_norm, mat.trace() - 1) / l_norm) * l;
250+
} else if (mat(0, 0) > 0 && mat(1, 1) > 0 && mat(2, 2) > 0) {
251+
// 行列の対角成分がすべて1のとき、ゼロベクトルを返す
252+
return Eigen::Vector3d::Zero();
253+
}
254+
// 行列の対角成分が(1, -1, -1), (-1, 1, -1), (-1, -1, 1)のときの処理
255+
return M_PI_2 * Eigen::Vector3d(mat(0, 0) + 1, mat(1, 1) + 1, mat(2, 2) + 1);
256+
}
257+
258+
std::vector<link_id_t> find_route(const links_t & links, const link_id_t & target_id) {
259+
// 目標リンク(target_id)までの経路を抽出する
260+
// 返り値のベクトルの末尾がtarget_idになる
261+
std::vector<link_id_t> id_list;
262+
263+
if (target_id <= 1 || target_id >= links.size()) {
264+
std::cerr << __func__ << ": 目標リンクIDには1より大きく、"
265+
<< "linksのサイズより小さい数値をセットしてください" << std::endl;
266+
return id_list;
267+
}
268+
269+
id_list.push_back(target_id);
270+
271+
auto parent_id = links[target_id].parent;
272+
// ベースリンク(ID = 1)は含めない
273+
while (parent_id != 1) {
274+
id_list.push_back(parent_id);
275+
parent_id = links[parent_id].parent;
276+
}
277+
278+
// 末尾をtarget_idにするため、ベクトルの並びを逆順にする
279+
std::reverse(id_list.begin(), id_list.end());
280+
return id_list;
281+
}
282+
283+
q_list_t get_q_list(const links_t & links, const std::vector<link_id_t> & id_list) {
284+
// リンク構成から指定されたIDの関節位置qを抽出する
285+
q_list_t q_list;
286+
for (auto target_id : id_list) {
287+
if (target_id < links.size()) {
288+
q_list[target_id] = links[target_id].q;
289+
} else {
290+
std::cerr << __func__ << ": 無効なIDです:" << target_id << std::endl;
291+
}
292+
}
293+
return q_list;
294+
}
295+
296+
bool set_q_list(links_t & links, const q_list_t & q_list, const bool & within_limit) {
297+
// リンク構成の指定されたIDに関節位置qを書き込む
298+
bool result = true;
299+
for (const auto & [target_id, q_value] : q_list) {
300+
if (target_id < links.size()) {
301+
if (within_limit) {
302+
links[target_id].set_q_within_limit(q_value);
303+
} else {
304+
links[target_id].q = q_value;
305+
}
306+
} else {
307+
std::cerr << __func__ << ": 無効なIDです:" << target_id << std::endl;
308+
result = false;
309+
}
310+
}
311+
return result;
312+
}
313+
314+
Eigen::Vector3d calc_error_R(const Eigen::Matrix3d & target, const Eigen::Matrix3d & current) {
315+
// 回転行列の差を求める
316+
return rotation_to_axis_angle_representation(target * current.transpose());
317+
}
318+
319+
Eigen::Vector3d calc_error_p(const Eigen::Vector3d & target, const Eigen::Vector3d & current) {
320+
// 位置の差を求める
321+
return target - current;
322+
}
323+
324+
Eigen::VectorXd calc_error(
325+
const Eigen::Vector3d & target_p, const Eigen::Matrix3d & target_R,
326+
const manipulators_link::Link & current_link) {
327+
// 目標位置・姿勢と、リンクの現在位置・姿勢との差を求める
328+
auto error_p = calc_error_p(target_p, current_link.p);
329+
auto error_r = calc_error_R(target_R, current_link.R);
330+
331+
Eigen::VectorXd error(6);
332+
error << error_p, error_r;
333+
return error;
334+
}
335+
336+
Eigen::MatrixXd calc_basic_jacobian(const links_t & links, const link_id_t & target_id) {
337+
// 基礎ヤコビ行列を求める(各軸は回転関節)
338+
auto route = find_route(links, target_id);
339+
auto target_p = links[target_id].p;
340+
Eigen::MatrixXd J(6, route.size());
341+
J.setZero();
342+
343+
for (int i=0; i < route.size(); i++) {
344+
auto link_id = route[i];
345+
auto a = links[link_id].R * links[link_id].a;
346+
J.col(i) << a.cross(target_p - links[link_id].p), a;
347+
}
348+
349+
return J;
350+
}
351+
231352
} // namespace kinematics_utils

rt_manipulators_lib/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ include(GoogleTest)
1717
set(list_tests
1818
test_joint
1919
test_kinematics_utils
20+
test_kinematics
2021
)
2122
foreach(test_executable IN LISTS list_tests)
2223
message("${test_executable}")

0 commit comments

Comments
 (0)