Mujoco KDL Wrapper  0.5.1
MuJoCo + KDL bridge for robot kinematics and dynamics
Loading...
Searching...
No Matches
test_mjcf_ft_sensor.cpp
Go to the documentation of this file.
1/* test_mjcf_ft_sensor.cpp
2 * A named F/T sensor between the Kinova GEN3 wrist and a Robotiq 2F-85: resolved from its
3 * <force>/<torque> sensors, read by update() and reset(), and refused when incomplete. */
4
6#include "common.hpp"
7#include "example_paths.hpp"
8
9#include <gtest/gtest.h>
10
11#include <cmath>
12#include <filesystem>
13#include <string>
14
15namespace ex = mj_kdl_examples;
16namespace fs = std::filesystem;
17
18class MjcfFtSensorTest : public testing::Test
19{
20 protected:
22
23 void SetUp() override
24 {
25 const std::string arm = ex::find_asset("kinova_gen3/gen3.xml");
26 const std::string ft = ex::find_asset("ft_sensor.xml");
27 const std::string gripper = ex::find_asset("robotiq_2f85/2f85.xml");
28 if (!fs::exists(arm)) GTEST_SKIP() << arm << " not found";
29 if (!fs::exists(ft)) GTEST_SKIP() << ft << " not found";
30 if (!fs::exists(gripper)) GTEST_SKIP() << gripper << " not found";
31
33 ft_spec.mjcf_path = ft;
34 ft_spec.attach_to = { mj_kdl::AttachKind::Site, "pinch_site" };
36 rs.path = arm;
37 rs.attachments.push_back(ft_spec);
38 rs.attachments.push_back(ex::gripper_attachment(gripper, "wrist_ft_site"));
39
41 sc.timestep = 0.002;
42 sc.add_floor = true;
43 sc.add_skybox = false;
44 sc.robots.push_back(rs);
45 ASSERT_TRUE(mj_kdl::init_env(&env_, &sc));
46 }
47
49 {
51 tool.ft_sensors.push_back(ft);
52 return tool;
53 }
54
56 {
58 ft.name = "wrist_ft";
59 ft.frame_site = "wrist_ft_site";
60 return ft;
61 }
62};
63
64TEST_F(MjcfFtSensorTest, ReadsNamedWrench)
65{
66 const mj_kdl::ToolFrameSpec tool = tool_with(wrist_ft());
67 mj_kdl::Robot robot;
68 ASSERT_TRUE(mj_kdl::init_robot_from_mjcf(&robot, &env_, "base_link", "bracelet_link", "", &tool)
69 );
70 ASSERT_EQ(robot.ft_sensors.size(), 1u);
71 const mj_kdl::ForceTorqueSensor &sensor = robot.ft_sensors.front();
72 EXPECT_EQ(sensor.name, "wrist_ft");
73 EXPECT_EQ(sensor.force_sensor, "wrist_ft_force");
74 EXPECT_EQ(sensor.torque_sensor, "wrist_ft_torque");
75 EXPECT_EQ(sensor.frame_site_id, mj_name2id(env_.model, mjOBJ_SITE, "wrist_ft_site"));
76
77 // Let the arm settle under its position servos, then read.
78 for (int i = 0; i < 500; ++i) {
79 mj_kdl::update(&env_);
80 mj_kdl::step(&env_);
81 }
82 mj_kdl::update(&env_);
83 const double *f = env_.data->sensordata + sensor.force_adr;
84 const double *t = env_.data->sensordata + sensor.torque_adr;
85 EXPECT_EQ(sensor.wrench.force, -KDL::Vector(f[0], f[1], f[2]));
86 EXPECT_EQ(sensor.wrench.torque, -KDL::Vector(t[0], t[1], t[2]));
87
88 // At rest it reads the weight of everything below it, pointing down (measured to 6e-8 N).
89 KDL::Frame world_T_site;
90 ASSERT_TRUE(mj_kdl::get_site_frame(&env_, "wrist_ft_site", &world_T_site));
91 const KDL::Vector world_f = world_T_site.M * sensor.wrench.force;
92 const int body = env_.model->site_bodyid[sensor.frame_site_id];
93 const double weight = env_.model->body_subtreemass[body] * 9.81;
94 EXPECT_NEAR(world_f.x(), 0.0, 1e-5);
95 EXPECT_NEAR(world_f.y(), 0.0, 1e-5);
96 EXPECT_NEAR(world_f.z(), -weight, 1e-5);
97}
98
99TEST_F(MjcfFtSensorTest, ResetReReadsTheWrench)
100{
101 mj_kdl::ToolFrameSpec tool = tool_with(wrist_ft());
102 tool.tcp_site.clear();
103 mj_kdl::Robot robot;
104 ASSERT_TRUE(mj_kdl::init_robot_from_mjcf(&robot, &env_, "base_link", "bracelet_link", "", &tool)
105 );
106 robot.ft_sensors[0].wrench = KDL::Wrench(KDL::Vector(99, 99, 99), KDL::Vector(99, 99, 99));
107
108 mj_kdl::reset(&env_);
109
110 const mj_kdl::ForceTorqueSensor &sensor = robot.ft_sensors[0];
111 const double *f = env_.data->sensordata + sensor.force_adr;
112 const double *t = env_.data->sensordata + sensor.torque_adr;
113 EXPECT_EQ(sensor.wrench.force, -KDL::Vector(f[0], f[1], f[2]));
114 EXPECT_EQ(sensor.wrench.torque, -KDL::Vector(t[0], t[1], t[2]));
115}
116
117TEST_F(MjcfFtSensorTest, RejectsMissingTorqueSensor)
118{
120 ft.name = "bad_ft";
121 ft.force_sensor = "wrist_ft_force";
122 ft.torque_sensor = "missing_torque";
123 const mj_kdl::ToolFrameSpec tool = tool_with(ft);
124
125 mj_kdl::Robot robot;
126 EXPECT_FALSE(
127 mj_kdl::init_robot_from_mjcf(&robot, &env_, "base_link", "bracelet_link", "", &tool)
128 );
129 EXPECT_TRUE(env_.robots.empty()) << "a robot that failed to init is not registered";
130}
131
132int main(int argc, char *argv[])
133{
134 testing::InitGoogleTest(&argc, argv);
135 return RUN_ALL_TESTS();
136}
Rotation M
double z() const
double x() const
double y() const
Vector force
Vector torque
static mj_kdl::ToolFrameSpec tool_with(const mj_kdl::ForceTorqueSensorSpec &ft)
static mj_kdl::ForceTorqueSensorSpec wrist_ft()
ResetInfo reset(Env *env, const ResetOptions *options=nullptr)
Status init_env(Env *env, const SceneSpec *spec)
void update(Env *env)
bool step(Env *env)
Status init_robot_from_mjcf(Robot *r, Env *env, const char *base_body, const char *tip_body, const char *prefix="", const ToolFrameSpec *tool=nullptr)
bool get_site_frame(Env *env, const char *site_name, KDL::Frame *out)
mj_kdl::ToolFrameSpec gripper_tool(const std::string &prefix="")
Definition common.hpp:159
std::string find_asset(const fs::path &relative)
mj_kdl::AttachmentSpec gripper_attachment(const std::string &path, const std::string &site="pinch_site")
Definition common.hpp:149
std::vector< AttachmentSpec > attachments
std::vector< ForceTorqueSensor > ft_sensors
std::vector< RobotSpec > robots
std::vector< ForceTorqueSensorSpec > ft_sensors
int main(int argc, char *argv[])
TEST_F(MjcfFtSensorTest, ReadsNamedWrench)