Mujoco KDL Wrapper  0.5.1
MuJoCo + KDL bridge for robot kinematics and dynamics
Loading...
Searching...
No Matches
test_dual_arm.cpp
Go to the documentation of this file.
1/* test_dual_arm.cpp
2 * Two Kinova GEN3 arms in one scene, facing each other, each its own mj_kdl::Robot with its own
3 * KDL chain: a prefix names the whole chain, KDL gravity matches MuJoCo's for both, and both hold
4 * their pose under KDL gravity compensation. Self-skips when the bundled Gen3 is missing. */
5
7#include "common.hpp"
8#include "example_paths.hpp"
9
10#include <gtest/gtest.h>
11
12#include <kdl/chaindynparam.hpp>
13#include <kdl/chainfksolverpos_recursive.hpp>
14
15#include <cmath>
16#include <filesystem>
17#include <memory>
18#include <string>
19
20namespace ex = mj_kdl_examples;
21namespace fs = std::filesystem;
22
23class DualArmTest : public testing::Test
24{
25 protected:
28 std::unique_ptr<KDL::ChainDynParam> dyn1, dyn2;
30
31 void SetUp() override
32 {
33 const std::string mjcf = ex::find_asset("kinova_gen3/gen3.xml");
34 if (!fs::exists(mjcf)) GTEST_SKIP() << mjcf << " not found";
35
36 // arm1 at x = -0.5 m facing +X; arm2 at x = +0.5 m turned 180 deg about Z, prefixed "r2_".
38 scene.timestep = 0.002;
39 scene.add_floor = true;
40 scene.add_skybox = false;
41 mj_kdl::RobotSpec left, right;
42 left.path = mjcf;
43 left.pos[0] = -0.5;
44 right.path = mjcf;
45 right.prefix = "r2_";
46 right.pos[0] = 0.5;
47 right.quat[2] = 1.0;
48 right.quat[3] = 0.0;
49 scene.robots = { left, right };
50
51 ASSERT_TRUE(mj_kdl::init_env(&env, &scene));
52 ASSERT_TRUE(mj_kdl::init_robot_from_mjcf(&arm1, &env, "base_link", "bracelet_link", ""));
53 ASSERT_TRUE(mj_kdl::init_robot_from_mjcf(&arm2, &env, "base_link", "bracelet_link", "r2_"));
54 dyn1 = std::make_unique<KDL::ChainDynParam>(arm1.chain, KDL::Vector(0, 0, -9.81));
55 dyn2 = std::make_unique<KDL::ChainDynParam>(arm2.chain, KDL::Vector(0, 0, -9.81));
56 }
57
58 int dof(const mj_kdl::Robot &r, int j) const
59 {
60 return env.model->jnt_dofadr[mj_name2id(env.model, mjOBJ_JOINT, r.joint_names[j].c_str())];
61 }
62};
63
64TEST_F(DualArmTest, PrefixNamesTheWholeChain)
65{
66 mj_kdl::Robot named;
67 ASSERT_TRUE(mj_kdl::init_robot_from_mjcf(&named, &env, "r2_base_link", "r2_bracelet_link"));
68 EXPECT_EQ(arm2.joint_names, named.joint_names);
69 EXPECT_EQ(arm2.joint_names.front(), "r2_joint_1");
70 EXPECT_EQ(arm2.chain.getNrOfSegments(), named.chain.getNrOfSegments());
71 mj_kdl::cleanup(&named);
72}
73
74TEST_F(DualArmTest, KdlGravityMatchesMujocoForBothArms)
75{
76 mj_kdl::set_joint_pos(&arm1, q_home);
77 mj_kdl::set_joint_pos(&arm2, q_home);
78 mj_forward(env.model, env.data);
79
80 // At rest qfrc_bias is the gravity torque; measured difference 1e-14 Nm.
81 KDL::JntArray g1(7), g2(7);
82 dyn1->JntToGravity(q_home, g1);
83 dyn2->JntToGravity(q_home, g2);
84 for (int j = 0; j < 7; ++j) {
85 EXPECT_NEAR(g1(j), env.data->qfrc_bias[dof(arm1, j)], 1e-9) << "arm1 joint " << j;
86 EXPECT_NEAR(g2(j), env.data->qfrc_bias[dof(arm2, j)], 1e-9) << "arm2 joint " << j;
87 }
88}
89
90TEST_F(DualArmTest, DualArmDrift)
91{
92 mj_kdl::set_joint_pos(&arm1, q_home);
93 mj_kdl::set_joint_pos(&arm2, q_home);
96 ex::prime_gravity(arm1, *dyn1, q_home);
97 ex::prime_gravity(arm2, *dyn2, q_home);
98
99 KDL::ChainFkSolverPos_recursive fk1(arm1.chain), fk2(arm2.chain);
100 KDL::Frame ee_init;
101 fk1.JntToCart(q_home, ee_init);
102
103 for (int i = 0; i < 500; ++i) {
104 mj_kdl::update(&env);
105 ex::pd_gravity(arm1, *dyn1, q_home);
106 ex::pd_gravity(arm2, *dyn2, q_home);
107 mj_kdl::step(&env);
108 }
109 mj_kdl::update(&env);
110
111 // Measured drift: 3e-18 m.
112 EXPECT_LE((ex::tcp_frame(fk1, arm1).p - ee_init.p).Norm(), 1e-6);
113 EXPECT_LE((ex::tcp_frame(fk2, arm2).p - ee_init.p).Norm(), 1e-6);
114}
115
116int main(int argc, char *argv[])
117{
118 testing::InitGoogleTest(&argc, argv);
119 return RUN_ALL_TESTS();
120}
mj_kdl::Env env
std::unique_ptr< KDL::ChainDynParam > dyn2
std::unique_ptr< KDL::ChainDynParam > dyn1
void SetUp() override
int dof(const mj_kdl::Robot &r, int j) const
mj_kdl::Robot arm2
const KDL::JntArray q_home
mj_kdl::Robot arm1
unsigned int getNrOfSegments() const
Eigen::VectorXd data
Status init_env(Env *env, const SceneSpec *spec)
void update(Env *env)
bool step(Env *env)
Status set_control_mode(Robot *r, CtrlMode mode)
Status init_robot_from_mjcf(Robot *r, Env *env, const char *base_body, const char *tip_body, const char *prefix="", const ToolFrameSpec *tool=nullptr)
void set_joint_pos(Robot *r, const KDL::JntArray &q)
void cleanup(Robot *r)
KDL::Frame tcp_frame(KDL::ChainFkSolverPos_recursive &fk, const mj_kdl::Robot &robot)
Definition common.hpp:102
std::string find_asset(const fs::path &relative)
KDL::JntArray home_q(unsigned n)
Definition common.hpp:68
void pd_gravity(mj_kdl::Robot &robot, KDL::ChainDynParam &dyn, const KDL::JntArray &q_des, const double *kp=nullptr, const double *kd=nullptr)
Definition common.hpp:112
void prime_gravity(mj_kdl::Robot &robot, KDL::ChainDynParam &dyn, const KDL::JntArray &q)
Definition common.hpp:131
std::vector< std::string > joint_names
std::vector< RobotSpec > robots
int main(int argc, char *argv[])
TEST_F(DualArmTest, PrefixNamesTheWholeChain)