12#include <gtest/gtest.h>
14#include <kdl/chaindynparam.hpp>
15#include <kdl/chainfksolverpos_recursive.hpp>
16#include <kdl/chainiksolverpos_nr_jl.hpp>
17#include <kdl/chainiksolvervel_pinv.hpp>
27namespace fs = std::filesystem;
29static constexpr double kHomePose[7] = { 0.0, 0.2618, 3.1416, -2.2689, 0.0, 0.9599, 1.5708 };
30static constexpr double kCubeX = 0.4;
31static constexpr double kCubeY = 0.0;
32static constexpr double kCubeHS = 0.02;
33static constexpr double kCubeZ = kCubeHS;
34static constexpr double kGripperReach = 0.18422;
36static constexpr double kKp[7] = { 100, 200, 100, 200, 100, 200, 100 };
37static constexpr double kKd[7] = { 10, 20, 10, 20, 10, 20, 10 };
39static fs::path repo_root() {
return fs::path(__FILE__).parent_path().parent_path(); }
41static double clamp01(
double v) {
return std::max(0.0, std::min(1.0, v)); }
43static void lerp_q(
const KDL::JntArray &a,
const KDL::JntArray &b,
double t, KDL::JntArray &out)
45 for (
unsigned i = 0; i < a.rows(); ++i) out(i) = a(i) + t * (b(i) - a(i));
49 impedance_ctrl(
mj_kdl::Robot &s,
const KDL::JntArray &q_des,
unsigned n, KDL::ChainDynParam &dyn)
51 KDL::JntArray q(n), g(n);
52 for (
unsigned i = 0; i < n; ++i) q(i) = s.
jnt_pos_msr[i];
53 dyn.JntToGravity(q, g);
54 for (
unsigned i = 0; i < n; ++i) {
72 std::unique_ptr<KDL::ChainFkSolverPos_recursive>
fk_;
73 std::unique_ptr<KDL::ChainDynParam>
dyn_;
74 std::unique_ptr<KDL::ChainIkSolverVel_pinv>
ik_vel_;
75 std::unique_ptr<KDL::ChainIkSolverPos_NR_JL>
ik_;
82 if (!fs::exists(
root_ /
"third_party/menagerie")) {
83 GTEST_SKIP() <<
"third_party/menagerie/ not found";
87 const std::string arm_mjcf =
88 (
root_ /
"third_party/menagerie/kinova_gen3/gen3.xml").
string();
89 const std::string grp_mjcf =
90 (
root_ /
"third_party/menagerie/robotiq_2f85/2f85.xml").
string();
96 gs.
pos[2] = -0.061525;
100 rs.
path = arm_mjcf.c_str();
107 cube.
pos[0] = kCubeX;
108 cube.
pos[1] = kCubeY;
109 cube.
pos[2] = kCubeZ;
127 &
s_,
model_,
data_,
"base_link",
"bracelet_link",
"",
"g_base"
135 ASSERT_GE(
fingers_act_, 0) <<
"g_fingers_actuator not found";
137 ASSERT_GE(
cube_jnt_, 0) <<
"cube_joint not found";
140 KDL::JntArray q_min(
n_), q_max(
n_);
141 for (
unsigned i = 0; i <
n_; ++i) {
143 if (
model_->jnt_limited[jid]) {
144 q_min(i) =
model_->jnt_range[2 * jid];
145 q_max(i) =
model_->jnt_range[2 * jid + 1];
147 q_min(i) = -2 * M_PI;
151 fk_ = std::make_unique<KDL::ChainFkSolverPos_recursive>(
s_.
chain);
152 dyn_ = std::make_unique<KDL::ChainDynParam>(
s_.
chain, KDL::Vector(0, 0, -9.81));
154 ik_ = std::make_unique<KDL::ChainIkSolverPos_NR_JL>(
159 for (
unsigned i = 0; i <
n_; ++i)
q_home_(i) = kHomePose[i];
161 const double kGraspZ = kCubeZ + kGripperReach + 0.02;
162 const double kPreGraspZ = kGraspZ + 0.20;
163 const double kLiftZ = kGraspZ + 0.30;
164 const KDL::Rotation kDownRot = KDL::Rotation::Identity();
174 const KDL::JntArray *seed;
181 for (
auto &wp : wps) {
182 KDL::Frame tgt(kDownRot, KDL::Vector(kCubeX, kCubeY, wp.z));
183 int ret =
ik_->CartToJnt(*wp.seed, tgt, *wp.out);
185 TEST_WARN(
"IK warning: did not converge for z=" << wp.z <<
" (ret=" << ret <<
")");
203 data_->qpos[qadr] = kCubeX;
204 data_->qpos[qadr + 1] = kCubeY;
205 data_->qpos[qadr + 2] = kCubeZ;
206 data_->qpos[qadr + 3] = 1.0;
207 data_->qpos[qadr + 4] =
data_->qpos[qadr + 5] =
data_->qpos[qadr + 6] = 0.0;
210 for (
unsigned i = 0; i <
n_; ++i) {
224 const KDL::JntArray &q_enter,
225 const KDL::JntArray &q_target,
232 double t_enter =
data_->time;
233 KDL::JntArray q_des(
n_);
237 double alpha = clamp01((
data_->time - t_enter) / duration);
238 lerp_q(q_enter, q_target, alpha, q_des);
239 impedance_ctrl(
s_, q_des,
n_, *
dyn_);
241 for (
unsigned i = 0; i <
n_; ++i)
246 double t_rel =
data_->time - t_enter;
247 bool done_time = t_rel >= duration;
248 bool done_pose = (settle_tol < 0.0);
250 double max_err = 0.0;
251 for (
unsigned i = 0; i <
n_; ++i)
252 max_err = std::max(max_err, std::abs(q_target(i) -
s_.
jnt_pos_msr[i]));
253 done_pose = (max_err <= settle_tol);
255 if ((done_time && done_pose) || t_rel >= timeout)
break;
262 TEST_INFO(
"arm KDL chain: " << n_ <<
" joints");
268 const double kMaxPosErr = 2e-3;
269 const KDL::Rotation kDownRot = KDL::Rotation::Identity();
271 const double kGraspZ = kCubeZ + kGripperReach + 0.02;
272 const double kPreGraspZ = kGraspZ + 0.20;
273 const double kLiftZ = kGraspZ + 0.30;
279 const KDL::JntArray *q;
282 {
"pre-grasp", kPreGraspZ, &q_pregrasp_ },
283 {
"grasp", kGraspZ, &q_grasp_ },
284 {
"lift", kLiftZ, &q_lift_ },
286 for (
auto &wp : wps) {
287 KDL::Frame tgt(kDownRot, KDL::Vector(kCubeX, kCubeY, wp.z));
289 fk_->JntToCart(*wp.q, fk_out);
290 double pos_err = (tgt.p - fk_out.p).Norm();
292 "IK " << wp.label <<
" pos error: " << std::fixed << std::setprecision(3)
293 << pos_err * 1000.0 <<
" mm"
295 EXPECT_LE(pos_err, kMaxPosErr) << wp.label <<
" IK error " << pos_err * 1000.0 <<
" mm";
303 KDL::JntArray q_enter(n_);
306 for (
unsigned i = 0; i < n_; ++i) q_enter(i) = s_.jnt_pos_msr[i];
310 run_phase(q_enter, q_home_, 1.0, 2.5, 0.08, 0.0);
312 run_phase(q_enter, q_pregrasp_, 2.0, 4.0, 0.08, 0.0);
314 run_phase(q_enter, q_grasp_, 2.0, 4.0, 0.06, 0.0);
316 run_phase(q_enter, q_grasp_, 1.5, 2.5, -1.0, 255.0);
318 run_phase(q_enter, q_lift_, 3.0, 5.0, 0.08, 255.0);
319 run_phase(q_lift_, q_lift_, 1.0, 1.0, -1.0, 255.0);
321 int qadr = model_->jnt_qposadr[cube_jnt_];
322 double cube_z = data_->qpos[qadr + 2];
323 TEST_INFO(
"cube Z after pick: " << std::fixed << std::setprecision(3) << cube_z <<
" m");
324 EXPECT_GT(cube_z, 0.20) <<
"cube was not lifted (z=" << cube_z <<
" m)";
327int main(
int argc,
char *argv[])
329 testing::InitGoogleTest(&argc, argv);
330 return RUN_ALL_TESTS();
std::unique_ptr< KDL::ChainFkSolverPos_recursive > fk_
std::unique_ptr< KDL::ChainIkSolverVel_pinv > ik_vel_
std::unique_ptr< KDL::ChainDynParam > dyn_
std::unique_ptr< KDL::ChainIkSolverPos_NR_JL > ik_
KDL::JntArray q_pregrasp_
void run_phase(const KDL::JntArray &q_enter, const KDL::JntArray &q_target, double duration, double timeout, double settle_tol, double gripper_cmd)
bool init_robot_from_mjcf(Robot *r, mjModel *model, mjData *data, const char *base_body, const char *tip_body, const char *prefix="", const char *tool_body=nullptr)
void set_joint_pos(Robot *r, const KDL::JntArray &q, bool call_forward=true)
bool build_scene(mjModel **out_model, mjData **out_data, const SceneSpec *spec)
void destroy_scene(mjModel *model, mjData *data)
std::vector< AttachmentSpec > attachments
std::vector< int > kdl_to_mj_qpos
std::vector< int > kdl_to_mj_dof
std::vector< double > jnt_pos_msr
std::vector< double > jnt_pos_cmd
std::vector< double > jnt_vel_msr
std::vector< double > jnt_trq_cmd
std::vector< RobotSpec > robots
std::vector< SceneObject > objects
int main(int argc, char *argv[])
TEST_F(MjcfPickTest, KDLChain)