moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
robot_state_test.cpp
Go to the documentation of this file.
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2013, Willow Garage, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 * * Neither the name of the Willow Garage nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *********************************************************************/
34
35/* Author: Ioan Sucan */
39#include <urdf_parser/urdf_parser.h>
40#include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
41#include <gtest/gtest.h>
42#include <gmock/gmock-matchers.h>
43#include <sstream>
44#include <algorithm>
45#include <ctype.h>
46
47namespace
48{
49constexpr double EPSILON{ 1.e-9 };
50
51// Helper to create an Eigen::VectorXd from an initializer list, e.g.:
52// auto vector = makeVector({1.0, 2.0, 3.0});
53Eigen::VectorXd makeVector(const std::vector<double>& values)
54{
55 Eigen::VectorXd vector = Eigen::VectorXd::Zero(values.size());
56 for (std::size_t i = 0; i < values.size(); i++)
57 {
58 vector[i] = values[i];
59 }
60 return vector;
61}
62
63// Checks the validity of state.getJacobian() at the given 'joint_values' and 'joint_velocities'.
64void checkJacobian(moveit::core::RobotState& state, const moveit::core::JointModelGroup& joint_model_group,
65 const Eigen::VectorXd& joint_values, const Eigen::VectorXd& joint_velocities,
66 const moveit::core::LinkModel* reference_link = nullptr)
67{
68 // Using the Jacobian, compute the Cartesian velocity vector at which the end-effector would move, with the given
69 // joint velocities.
70 state.setToDefaultValues();
71 state.setJointGroupPositions(&joint_model_group, joint_values);
73
74 const moveit::core::JointModel* root_joint_model = joint_model_group.getJointModels().front();
75 const moveit::core::LinkModel* root_link_model = root_joint_model->getParentLinkModel();
76 const Eigen::Isometry3d root_pose_world = state.getGlobalLinkTransform(root_link_model).inverse();
77
78 if (!reference_link)
79 {
80 reference_link = joint_model_group.getLinkModels().back();
81 }
82 const Eigen::Isometry3d tip_pose_initial = root_pose_world * state.getGlobalLinkTransform(reference_link);
83 Eigen::MatrixXd jacobian;
84 state.getJacobian(&joint_model_group, reference_link, Eigen::Vector3d::Zero(), jacobian);
85
86 // Verify that only elements of the Jacobian contain values that correspond to joints that are being used based on the reference link.
87 const std::vector<const moveit::core::JointModel*>& joint_models = joint_model_group.getJointModels();
88 auto it = std::find_if(joint_models.begin(), joint_models.end(), [&](const moveit::core::JointModel* jm) {
89 return jm->getParentLinkModel() == reference_link;
90 });
91 if (it != joint_models.end())
92 {
93 std::size_t index = 0;
94 for (auto jt = joint_models.begin(); jt != it; ++jt)
95 {
96 index += (*jt)->getVariableCount();
97 }
98
99 EXPECT_TRUE(jacobian.block(0, index, jacobian.rows(), jacobian.cols() - index).isZero())
100 << "Jacobian contains non-zero values for joints that are not used based on the reference link "
101 << reference_link->getName() << ". This is the faulty Jacobian: " << '\n'
102 << jacobian << '\n'
103 << "The columns " << index << " to " << jacobian.cols() << " should be zero. Instead the values are: " << '\n'
104 << jacobian.block(0, index, jacobian.rows(), jacobian.cols() - index);
105 }
106
107 // Compute the Cartesian velocity vector using the Jacobian.
108 const Eigen::VectorXd cartesian_velocity = jacobian * joint_velocities;
109
110 // Compute the instantaneous displacement that the end-effector would achieve if the given joint
111 // velocities were applied for a small amount of time.
112 constexpr double time_step = 1e-5;
113 const Eigen::VectorXd delta_joint_angles = time_step * joint_velocities;
114 state.setJointGroupPositions(&joint_model_group, joint_values + delta_joint_angles);
115 state.updateLinkTransforms();
116 const Eigen::Isometry3d tip_pose_after_delta = root_pose_world * state.getGlobalLinkTransform(reference_link);
117 const Eigen::Vector3d displacement = tip_pose_after_delta.translation() - tip_pose_initial.translation();
118
119 // The Cartesian velocity vector obtained via the Jacobian should be aligned with the instantaneous robot motion, i.e.
120 // the angle between the vectors should be close to zero.
121 double angle = std::acos(displacement.dot(cartesian_velocity.head<3>()) /
122 (displacement.norm() * cartesian_velocity.head<3>().norm()));
123 EXPECT_NEAR(angle, 0.0, 1e-05) << "Angle between Cartesian velocity and Cartesian displacement larger than expected. "
124 "Angle: "
125 << angle << ". displacement: " << displacement.transpose()
126 << ". Cartesian velocity: " << cartesian_velocity.head<3>().transpose() << '\n';
127}
128} // namespace
129
130static void expect_near(const Eigen::MatrixXd& x, const Eigen::MatrixXd& y,
131 double eps = std::numeric_limits<double>::epsilon())
132{
133 ASSERT_EQ(x.rows(), y.rows());
134 ASSERT_EQ(x.cols(), y.cols());
135 for (int r = 0; r < x.rows(); ++r)
136 {
137 for (int c = 0; c < x.cols(); ++c)
138 EXPECT_NEAR(x(r, c), y(r, c), eps) << "(r,c) = (" << r << ',' << c << ')';
139 }
140}
141
142// clang-format off
143#define EXPECT_NEAR_TRACED(...) { \
144 SCOPED_TRACE("expect_near(" #__VA_ARGS__ ")"); \
145 expect_near(__VA_ARGS__); \
146}
147// clang-format on
148
150{
151 moveit::core::RobotModelBuilder builder("myrobot", "base_link");
152 builder.addVirtualJoint("odom_combined", "base_link", "floating", "base_joint");
153 ASSERT_TRUE(builder.isValid());
154 moveit::core::RobotModelPtr model = builder.build();
155 moveit::core::RobotState state(model);
156
157 state.setToDefaultValues();
158
159 // make sure that this copy constructor works
160 moveit::core::RobotState new_state(state);
161
162 EXPECT_EQ(new_state.getVariablePosition("base_joint/rot_w"), 1.0);
163
164 EXPECT_EQ(std::string("myrobot"), model->getName());
165 EXPECT_EQ(7u, new_state.getVariableCount());
166
167 const std::vector<moveit::core::LinkModel*>& links = model->getLinkModels();
168 EXPECT_EQ(1u, links.size());
169
170 const std::vector<moveit::core::JointModel*>& joints = model->getJointModels();
171 EXPECT_EQ(1u, joints.size());
172
173 const std::vector<std::string>& pgroups = model->getJointModelGroupNames();
174 EXPECT_EQ(0u, pgroups.size());
175}
176
177TEST(LoadingAndFK, SimpleRobot)
178{
179 moveit::core::RobotModelBuilder builder("myrobot", "base_link");
180 geometry_msgs::msg::Pose pose;
181 pose.position.x = -0.1;
182 pose.position.y = 0;
183 pose.position.z = 0;
184
185 tf2::Quaternion q;
186 q.setRPY(0, 0, -1);
187 pose.orientation = tf2::toMsg(q);
188 builder.addCollisionBox("base_link", { 1, 2, 1 }, pose);
189 pose.position.x = 0;
190 pose.position.y = 0;
191 pose.position.z = 0;
192 q.setRPY(0, 0, 0);
193 pose.orientation = tf2::toMsg(q);
194 builder.addVisualBox("base_link", { 1, 2, 1 }, pose);
195 pose.position.x = 0;
196 pose.position.y = 0.099;
197 pose.position.z = 0;
198 q.setRPY(0, 0, 0);
199 pose.orientation = tf2::toMsg(q);
200 builder.addInertial("base_link", 2.81, pose, 0.1, -0.2, 0.5, -0.09, 1, 0.101);
201 builder.addVirtualJoint("odom_combined", "base_link", "planar", "base_joint");
202 builder.addGroup({}, { "base_joint" }, "base");
203
204 ASSERT_TRUE(builder.isValid());
205 moveit::core::RobotModelPtr model = builder.build();
206 moveit::core::RobotState state(model);
207
208 EXPECT_EQ(3u, state.getVariableCount());
209
210 state.setToDefaultValues();
211
212 EXPECT_EQ(1u, static_cast<unsigned int>(model->getJointModelCount()));
213 EXPECT_EQ(3u, static_cast<unsigned int>(model->getJointModels()[0]->getLocalVariableNames().size()));
214
215 std::map<std::string, double> joint_values;
216 joint_values["base_joint/x"] = 10.0;
217 joint_values["base_joint/y"] = 8.0;
218
219 // testing incomplete state
220 std::vector<std::string> missing_states;
221 state.setVariablePositions(joint_values, missing_states);
222 ASSERT_EQ(missing_states.size(), 1u);
223 EXPECT_EQ(missing_states[0], std::string("base_joint/theta"));
224 joint_values["base_joint/theta"] = 0.1;
225
226 state.setVariablePositions(joint_values, missing_states);
227 ASSERT_EQ(missing_states.size(), 0u);
228
229 EXPECT_NEAR_TRACED(state.getGlobalLinkTransform("base_link").translation(), Eigen::Vector3d(10, 8, 0));
230
231 state.setVariableAcceleration("base_joint/x", 0.0);
232
233 const auto new_state = std::make_unique<moveit::core::RobotState>(state); // making sure that values get copied
234 EXPECT_NEAR_TRACED(state.getGlobalLinkTransform("base_link").translation(), Eigen::Vector3d(10, 8, 0));
235
236 std::vector<double> jv(state.getVariableCount(), 0.0);
237 jv[state.getRobotModel()->getVariableIndex("base_joint/x")] = 5.0;
238 jv[state.getRobotModel()->getVariableIndex("base_joint/y")] = 4.0;
239 jv[state.getRobotModel()->getVariableIndex("base_joint/theta")] = 0.0;
240
241 state.setVariablePositions(jv);
242 EXPECT_NEAR_TRACED(state.getGlobalLinkTransform("base_link").translation(), Eigen::Vector3d(5, 4, 0));
243}
244
245class OneRobot : public testing::Test
246{
247protected:
248 void SetUp() override
249 {
250 static const std::string MODEL2 = R"(
251<?xml version="1.0" ?>
252<robot name="one_robot">
253<link name="base_link">
254 <inertial>
255 <mass value="2.81"/>
256 <origin rpy="0 0 0" xyz="0.0 0.0 .0"/>
257 <inertia ixx="0.1" ixy="-0.2" ixz="0.5" iyy="-.09" iyz="1" izz="0.101"/>
258 </inertial>
259 <collision name="my_collision">
260 <origin rpy="0 0 0" xyz="0 0 0"/>
261 <geometry>
262 <box size="1 2 1" />
263 </geometry>
264 </collision>
265 <visual>
266 <origin rpy="0 0 0" xyz="0.0 0 0"/>
267 <geometry>
268 <box size="1 2 1" />
269 </geometry>
270 </visual>
271</link>
272<joint name="joint_a" type="continuous">
273 <axis xyz="0 0 1"/>
274 <parent link="base_link"/>
275 <child link="link_a"/>
276 <origin rpy=" 0.0 0 0 " xyz="0.0 0 0 "/>
277</joint>
278<link name="link_a">
279 <inertial>
280 <mass value="1.0"/>
281 <origin rpy="0 0 0" xyz="0.0 0.0 .0"/>
282 <inertia ixx="0.1" ixy="-0.2" ixz="0.5" iyy="-.09" iyz="1" izz="0.101"/>
283 </inertial>
284 <collision>
285 <origin rpy="0 0 0" xyz="0 0 0"/>
286 <geometry>
287 <box size="1 2 1" />
288 </geometry>
289 </collision>
290 <visual>
291 <origin rpy="0 0 0" xyz="0.0 0 0"/>
292 <geometry>
293 <box size="1 2 1" />
294 </geometry>
295 </visual>
296</link>
297<joint name="joint_b" type="fixed">
298 <parent link="link_a"/>
299 <child link="link_b"/>
300 <origin rpy=" 0.0 -0.42 0 " xyz="0.0 0.5 0 "/>
301</joint>
302<link name="link_b">
303 <inertial>
304 <mass value="1.0"/>
305 <origin rpy="0 0 0" xyz="0.0 0.0 .0"/>
306 <inertia ixx="0.1" ixy="-0.2" ixz="0.5" iyy="-.09" iyz="1" izz="0.101"/>
307 </inertial>
308 <collision>
309 <origin rpy="0 0 0" xyz="0 0 0"/>
310 <geometry>
311 <box size="1 2 1" />
312 </geometry>
313 </collision>
314 <visual>
315 <origin rpy="0 0 0" xyz="0.0 0 0"/>
316 <geometry>
317 <box size="1 2 1" />
318 </geometry>
319 </visual>
320</link>
321 <joint name="joint_c" type="prismatic">
322 <axis xyz="1 0 0"/>
323 <limit effort="100.0" lower="0.0" upper="0.09" velocity="0.2"/>
324 <safety_controller k_position="20.0" k_velocity="500.0" soft_lower_limit="0.0"
325soft_upper_limit="0.089"/>
326 <parent link="link_b"/>
327 <child link="link_c"/>
328 <origin rpy=" 0.0 0.42 0.0 " xyz="0.0 -0.1 0 "/>
329 </joint>
330<link name="link_c">
331 <inertial>
332 <mass value="1.0"/>
333 <origin rpy="0 0 0" xyz="0.0 0 .0"/>
334 <inertia ixx="0.1" ixy="-0.2" ixz="0.5" iyy="-.09" iyz="1" izz="0.101"/>
335 </inertial>
336 <collision>
337 <origin rpy="0 0 0" xyz="0 0 0"/>
338 <geometry>
339 <box size="1 2 1" />
340 </geometry>
341 </collision>
342 <visual>
343 <origin rpy="0 0 0" xyz="0.0 0 0"/>
344 <geometry>
345 <box size="1 2 1" />
346 </geometry>
347 </visual>
348</link>
349 <joint name="mim_f" type="prismatic">
350 <axis xyz="1 0 0"/>
351 <limit effort="100.0" lower="0.0" upper="0.19" velocity="0.2"/>
352 <parent link="link_c"/>
353 <child link="link_d"/>
354 <origin rpy=" 0.0 0.0 0.0 " xyz="0.1 0.1 0 "/>
355 <mimic joint="joint_f" multiplier="1.5" offset="0.1"/>
356 </joint>
357 <joint name="joint_f" type="prismatic">
358 <axis xyz="1 0 0"/>
359 <limit effort="100.0" lower="0.0" upper="0.19" velocity="0.2"/>
360 <parent link="link_d"/>
361 <child link="link_e"/>
362 <origin rpy=" 0.0 0.0 0.0 " xyz="0.1 0.1 0 "/>
363 </joint>
364<link name="link_d">
365 <collision>
366 <origin rpy="0 0 0" xyz="0 0 0"/>
367 <geometry>
368 <box size="1 2 1" />
369 </geometry>
370 </collision>
371 <visual>
372 <origin rpy="0 1 0" xyz="0 0.1 0"/>
373 <geometry>
374 <box size="1 2 1" />
375 </geometry>
376 </visual>
377</link>
378<link name="link_e">
379 <collision>
380 <origin rpy="0 0 0" xyz="0 0 0"/>
381 <geometry>
382 <box size="1 2 1" />
383 </geometry>
384 </collision>
385 <visual>
386 <origin rpy="0 1 0" xyz="0 0.1 0"/>
387 <geometry>
388 <box size="1 2 1" />
389 </geometry>
390 </visual>
391</link>
392<link name="link/with/slash" />
393<joint name="joint_link_with_slash" type="fixed">
394 <parent link="base_link"/>
395 <child link="link/with/slash"/>
396 <origin rpy="0 0 0" xyz="0 0 0"/>
397</joint>
398</robot>
399)";
400 static const std::string SMODEL2 = R"(
401<?xml version="1.0" ?>
402<robot name="one_robot">
403<virtual_joint name="base_joint" child_link="base_link" parent_frame="odom_combined" type="planar"/>
404<group name="base_from_joints">
405<joint name="base_joint"/>
406<joint name="joint_a"/>
407<joint name="joint_c"/>
408</group>
409<group name="mim_joints">
410<joint name="joint_f"/>
411<joint name="mim_f"/>
412</group>
413<group name="base_with_subgroups">
414<group name="base_from_base_to_tip"/>
415<joint name="joint_c"/>
416</group>
417<group name="base_from_base_to_tip">
418<chain base_link="base_link" tip_link="link_b"/>
419<joint name="base_joint"/>
420</group>
421<group name="base_from_base_to_e">
422<chain base_link="base_link" tip_link="link_e"/>
423<joint name="base_joint"/>
424</group>
425<group name="base_with_bad_subgroups">
426<group name="error"/>
427</group>
428</robot>
429)";
430
431 urdf::ModelInterfaceSharedPtr urdf_model = urdf::parseURDF(MODEL2);
432 auto srdf_model = std::make_shared<srdf::Model>();
433 srdf_model->initString(*urdf_model, SMODEL2);
434 robot_model_ = std::make_shared<moveit::core::RobotModel>(urdf_model, srdf_model);
435 }
436
437 void TearDown() override
438 {
439 }
440
441protected:
442 moveit::core::RobotModelConstPtr robot_model_;
443};
444
445TEST_F(OneRobot, setToDefaultValues)
446{
447 moveit::core::RobotState state(robot_model_);
448 state.setToDefaultValues();
449
450 // Get default values for joint_a.
451 double joint_a_default_position = state.getVariablePosition("joint_a");
452 double joint_a_default_velocity = state.getVariableVelocity("joint_a");
453 double joint_a_default_acceleration = state.getVariableAcceleration("joint_a");
454
455 // Set joint_a to some values.
456 state.setVariablePosition("joint_a", 1.0);
457 state.setVariableVelocity("joint_a", 2.0);
458 state.setVariableAcceleration("joint_a", 3.0);
459
460 // Check that joint_a has the right values.
461 EXPECT_EQ(state.getVariablePosition("joint_a"), 1.0);
462 EXPECT_EQ(state.getVariableVelocity("joint_a"), 2.0);
463 EXPECT_EQ(state.getVariableAcceleration("joint_a"), 3.0);
464
465 // Set to default values.
466 // Check that joint_a is back to the default values.
467 state.setToDefaultValues();
468 EXPECT_EQ(state.getVariablePosition("joint_a"), joint_a_default_position);
469 EXPECT_EQ(state.getVariableVelocity("joint_a"), joint_a_default_velocity);
470 EXPECT_EQ(state.getVariableAcceleration("joint_a"), joint_a_default_acceleration);
471}
472
474{
475 moveit::core::RobotModelConstPtr model = robot_model_;
476
477 // testing that the two planning groups are the same
478 const moveit::core::JointModelGroup* g_one = model->getJointModelGroup("base_from_joints");
479 const moveit::core::JointModelGroup* g_two = model->getJointModelGroup("base_from_base_to_tip");
480 const moveit::core::JointModelGroup* g_three = model->getJointModelGroup("base_with_subgroups");
481 const moveit::core::JointModelGroup* g_four = model->getJointModelGroup("base_with_bad_subgroups");
482 const moveit::core::JointModelGroup* g_mim = model->getJointModelGroup("mim_joints");
483
484 ASSERT_TRUE(g_one != nullptr);
485 ASSERT_TRUE(g_two != nullptr);
486 ASSERT_TRUE(g_three != nullptr);
487 ASSERT_TRUE(g_four == nullptr);
488
489 EXPECT_THAT(g_one->getJointModelNames(), ::testing::ElementsAreArray({ "base_joint", "joint_a", "joint_c" }));
490 EXPECT_THAT(g_two->getJointModelNames(), ::testing::ElementsAreArray({ "base_joint", "joint_a", "joint_b" }));
491 EXPECT_THAT(g_three->getJointModelNames(),
492 ::testing::ElementsAreArray({ "base_joint", "joint_a", "joint_b", "joint_c" }));
493 EXPECT_THAT(g_mim->getJointModelNames(), ::testing::ElementsAreArray({ "mim_f", "joint_f" }));
494
495 EXPECT_THAT(g_one->getLinkModelNames(), ::testing::ElementsAreArray({ "base_link", "link_a", "link_c" }));
496 EXPECT_THAT(g_two->getLinkModelNames(), ::testing::ElementsAreArray({ "base_link", "link_a", "link_b" }));
497 EXPECT_THAT(g_three->getLinkModelNames(), ::testing::ElementsAreArray({ "base_link", "link_a", "link_b", "link_c" }));
498
499 // but they should have the same links to be updated
500 auto updated_link_model_names = { "base_link", "link_a", "link_b", "link_c", "link_d", "link_e", "link/with/slash" };
501 EXPECT_THAT(g_one->getUpdatedLinkModelNames(), ::testing::ElementsAreArray(updated_link_model_names));
502 EXPECT_THAT(g_two->getUpdatedLinkModelNames(), ::testing::ElementsAreArray(updated_link_model_names));
503 EXPECT_THAT(g_three->getUpdatedLinkModelNames(), ::testing::ElementsAreArray(updated_link_model_names));
504
505 moveit::core::RobotState state(model);
506
507 EXPECT_EQ(state.getVariableCount(), 7u);
508
509 state.setToDefaultValues();
510
511 std::map<std::string, double> joint_values;
512 joint_values["base_joint/x"] = 1.0;
513 joint_values["base_joint/y"] = 1.0;
514 joint_values["base_joint/theta"] = 0.5;
515 joint_values["joint_a"] = -0.5;
516 joint_values["joint_c"] = 0.08;
517 state.setVariablePositions(joint_values);
518
519 EXPECT_NEAR_TRACED(state.getGlobalLinkTransform("base_link").translation(), Eigen::Vector3d(1, 1, 0));
520 EXPECT_NEAR(0.0, Eigen::Quaterniond(state.getGlobalLinkTransform("base_link").linear()).x(), 1e-5);
521 EXPECT_NEAR(0.0, Eigen::Quaterniond(state.getGlobalLinkTransform("base_link").linear()).y(), 1e-5);
522 EXPECT_NEAR(0.247404, Eigen::Quaterniond(state.getGlobalLinkTransform("base_link").linear()).z(), 1e-5);
523 EXPECT_NEAR(0.968912, Eigen::Quaterniond(state.getGlobalLinkTransform("base_link").linear()).w(), 1e-5);
524
525 EXPECT_NEAR_TRACED(state.getGlobalLinkTransform("link_a").translation(), Eigen::Vector3d(1, 1, 0));
526 EXPECT_NEAR(0.0, Eigen::Quaterniond(state.getGlobalLinkTransform("link_a").linear()).x(), 1e-5);
527 EXPECT_NEAR(0.0, Eigen::Quaterniond(state.getGlobalLinkTransform("link_a").linear()).y(), 1e-5);
528 EXPECT_NEAR(0.0, Eigen::Quaterniond(state.getGlobalLinkTransform("link_a").linear()).z(), 1e-5);
529 EXPECT_NEAR(1.0, Eigen::Quaterniond(state.getGlobalLinkTransform("link_a").linear()).w(), 1e-5);
530
531 EXPECT_NEAR_TRACED(state.getGlobalLinkTransform("link_b").translation(), Eigen::Vector3d(1, 1.5, 0));
532 EXPECT_NEAR(0.0, Eigen::Quaterniond(state.getGlobalLinkTransform("link_b").linear()).x(), 1e-5);
533 EXPECT_NEAR(-0.2084598, Eigen::Quaterniond(state.getGlobalLinkTransform("link_b").linear()).y(), 1e-5);
534 EXPECT_NEAR(0.0, Eigen::Quaterniond(state.getGlobalLinkTransform("link_b").linear()).z(), 1e-5);
535 EXPECT_NEAR(0.97803091, Eigen::Quaterniond(state.getGlobalLinkTransform("link_b").linear()).w(), 1e-5);
536
537 EXPECT_NEAR_TRACED(state.getGlobalLinkTransform("link_c").translation(), Eigen::Vector3d(1.08, 1.4, 0));
538 EXPECT_NEAR(0.0, Eigen::Quaterniond(state.getGlobalLinkTransform("link_c").linear()).x(), 1e-5);
539 EXPECT_NEAR(0.0, Eigen::Quaterniond(state.getGlobalLinkTransform("link_c").linear()).y(), 1e-5);
540 EXPECT_NEAR(0.0, Eigen::Quaterniond(state.getGlobalLinkTransform("link_c").linear()).z(), 1e-5);
541 EXPECT_NEAR(1.0, Eigen::Quaterniond(state.getGlobalLinkTransform("link_c").linear()).w(), 1e-5);
542
543 EXPECT_TRUE(state.satisfiesBounds());
544
545 std::map<std::string, double> upd_a;
546 upd_a["joint_a"] = 0.2;
547 state.setVariablePositions(upd_a);
548 EXPECT_TRUE(state.satisfiesBounds(model->getJointModel("joint_a")));
549 EXPECT_NEAR(state.getVariablePosition("joint_a"), 0.2, 1e-3);
550 state.enforceBounds();
551 EXPECT_NEAR(state.getVariablePosition("joint_a"), 0.2, 1e-3);
552
553 upd_a["joint_a"] = 3.2;
554 state.setVariablePositions(upd_a);
555 EXPECT_TRUE(state.satisfiesBounds(model->getJointModel("joint_a")));
556 EXPECT_NEAR(state.getVariablePosition("joint_a"), 3.2, 1e-3);
557 state.enforceBounds();
558 EXPECT_NEAR(state.getVariablePosition("joint_a"), -3.083185, 1e-3);
559 EXPECT_TRUE(state.satisfiesBounds(model->getJointModel("joint_a")));
560
561 // mimic joints
562 state.setToDefaultValues();
563 EXPECT_TRUE(state.dirtyLinkTransforms());
564 EXPECT_NEAR_TRACED(state.getGlobalLinkTransform("link_c").translation(), Eigen::Vector3d(0.0, 0.4, 0));
565 EXPECT_NEAR_TRACED(state.getGlobalLinkTransform("link_d").translation(), Eigen::Vector3d(0.2, 0.5, 0));
566 EXPECT_NEAR_TRACED(state.getGlobalLinkTransform("link_e").translation(), Eigen::Vector3d(0.3, 0.6, 0));
567
568 // setVariablePosition should update corresponding mimic joints too
569 state.setVariablePosition("joint_f", 1.0);
570 EXPECT_EQ(state.getVariablePosition("joint_f"), 1.0);
571 EXPECT_EQ(state.getVariablePosition("mim_f"), 1.6);
572 EXPECT_TRUE(state.dirtyLinkTransforms());
573 EXPECT_NEAR_TRACED(state.getGlobalLinkTransform("link_c").translation(), Eigen::Vector3d(0.0, 0.4, 0));
574 EXPECT_NEAR_TRACED(state.getGlobalLinkTransform("link_d").translation(), Eigen::Vector3d(1.7, 0.5, 0));
575 EXPECT_NEAR_TRACED(state.getGlobalLinkTransform("link_e").translation(), Eigen::Vector3d(2.8, 0.6, 0));
576
577 ASSERT_EQ(g_mim->getVariableCount(), 2u);
578 double gstate[2];
579 state.copyJointGroupPositions(g_mim, gstate);
580
581 // setToRandomPositions() uses a different mechanism to update mimic joints
582 state.setToRandomPositions(g_mim);
583 double joint_f = state.getVariablePosition("joint_f");
584 double mim_f = state.getVariablePosition("mim_f");
585 EXPECT_NEAR(mim_f, 1.5 * joint_f + 0.1, 1e-8);
586 EXPECT_TRUE(state.dirtyLinkTransforms());
587 EXPECT_NEAR_TRACED(state.getGlobalLinkTransform("link_c").translation(), Eigen::Vector3d(0.0, 0.4, 0));
588 EXPECT_NEAR_TRACED(state.getGlobalLinkTransform("link_d").translation(), Eigen::Vector3d(0.1 + mim_f, 0.5, 0));
589 EXPECT_NEAR_TRACED(state.getGlobalLinkTransform("link_e").translation(),
590 Eigen::Vector3d(0.1 + mim_f + joint_f + 0.1, 0.6, 0));
591
592 // setJointGroupPositions() uses a different mechanism to update mimic joints
593 state.setJointGroupPositions(g_mim, gstate);
594 EXPECT_EQ(state.getVariablePosition("joint_f"), 1.0);
595 EXPECT_EQ(state.getVariablePosition("mim_f"), 1.6);
596 EXPECT_TRUE(state.dirtyLinkTransforms());
597 EXPECT_NEAR_TRACED(state.getGlobalLinkTransform("link_c").translation(), Eigen::Vector3d(0.0, 0.4, 0));
598 EXPECT_NEAR_TRACED(state.getGlobalLinkTransform("link_d").translation(), Eigen::Vector3d(1.7, 0.5, 0));
599 EXPECT_NEAR_TRACED(state.getGlobalLinkTransform("link_e").translation(), Eigen::Vector3d(2.8, 0.6, 0));
600}
601
602TEST_F(OneRobot, testPrintCurrentPositionWithJointLimits)
603{
604 moveit::core::RobotState state(robot_model_);
605 const moveit::core::JointModelGroup* joint_model_group = robot_model_->getJointModelGroup("base_from_base_to_e");
606 ASSERT_TRUE(joint_model_group);
607
608 state.setToDefaultValues();
609
610 std::cout << "\nVisual inspection should show NO joints out of bounds:\n";
611 state.printStatePositionsWithJointLimits(joint_model_group);
612
613 std::cout << "\nVisual inspection should show ONE joint out of bounds:\n";
614 std::vector<double> single_joint(1);
615 single_joint[0] = -1.0;
616 state.setJointPositions("joint_c", single_joint);
617 state.printStatePositionsWithJointLimits(joint_model_group);
618
619 std::cout << "\nVisual inspection should show TWO joint out of bounds:\n";
620 single_joint[0] = 1.0;
621 state.setJointPositions("joint_f", single_joint);
622 state.printStatePositionsWithJointLimits(joint_model_group);
623
624 std::cout << "\nVisual inspection should show ONE joint out of bounds:\n";
625 single_joint[0] = 0.19;
626 state.setJointPositions("joint_f", single_joint);
627 state.printStatePositionsWithJointLimits(joint_model_group);
628}
629
630TEST_F(OneRobot, testInterpolation)
631{
632 moveit::core::RobotState state_a(robot_model_);
633
634 // Interpolate with itself
635 state_a.setToDefaultValues();
636 moveit::core::RobotState state_b(state_a);
637 moveit::core::RobotState interpolated_state(state_a);
638 for (size_t i = 0; i <= 10; ++i)
639 {
640 state_a.interpolate(state_b, static_cast<double>(i) / 10., interpolated_state,
641 robot_model_->getJointModelGroup("base_from_base_to_e"));
642 EXPECT_NEAR(state_a.distance(state_b), 0, EPSILON) << "Interpolation between identical states yielded a different "
643 "state.";
644
645 for (const auto& link_name : robot_model_->getLinkModelNames())
646 {
647 EXPECT_FALSE(interpolated_state.getCollisionBodyTransform(link_name, 0).matrix().hasNaN()) << "Interpolation "
648 "between identical "
649 "states yielded "
650 "NaN value.";
651 }
652 }
653
654 // Some simple interpolation
655 std::map<std::string, double> joint_values;
656 joint_values["base_joint/x"] = 1.0;
657 joint_values["base_joint/y"] = 1.0;
658 state_a.setVariablePositions(joint_values);
659 joint_values["base_joint/x"] = 0.0;
660 joint_values["base_joint/y"] = 2.0;
661 state_b.setVariablePositions(joint_values);
662 EXPECT_NEAR(3 * std::sqrt(2), state_a.distance(state_b), EPSILON) << "Simple interpolation of base_joint failed.";
663
664 state_a.interpolate(state_b, 0.5, interpolated_state, robot_model_->getJointModelGroup("base_from_base_to_e"));
665 EXPECT_NEAR(0., state_a.distance(interpolated_state) - state_b.distance(interpolated_state), EPSILON) << "Simple "
666 "interpolati"
667 "on of "
668 "base_joint "
669 "failed.";
670 EXPECT_NEAR(0.5, interpolated_state.getVariablePosition("base_joint/x"), EPSILON) << "Simple interpolation of "
671 "base_joint failed.";
672 EXPECT_NEAR(1.5, interpolated_state.getVariablePosition("base_joint/y"), EPSILON) << "Simple interpolation of "
673 "base_joint failed.";
674 state_a.interpolate(state_b, 0.1, interpolated_state, robot_model_->getJointModelGroup("base_from_base_to_e"));
675 EXPECT_NEAR(0.9, interpolated_state.getVariablePosition("base_joint/x"), EPSILON) << "Simple interpolation of "
676 "base_joint failed.";
677 EXPECT_NEAR(1.1, interpolated_state.getVariablePosition("base_joint/y"), EPSILON) << "Simple interpolation of "
678 "base_joint failed.";
679
680 // Interpolate all the joints
681 joint_values["base_joint/x"] = 0.0;
682 joint_values["base_joint/y"] = 20.0;
683 joint_values["base_joint/theta"] = 3 * M_PI / 4;
684 joint_values["joint_a"] = -4 * M_PI / 5;
685 joint_values["joint_c"] = 0.0;
686 joint_values["joint_f"] = 1.0;
687 state_a.setVariablePositions(joint_values);
688
689 joint_values["base_joint/x"] = 10.0;
690 joint_values["base_joint/y"] = 0.0;
691 joint_values["base_joint/theta"] = -3 * M_PI / 4;
692 joint_values["joint_a"] = 4 * M_PI / 5;
693 joint_values["joint_c"] = 0.07;
694 joint_values["joint_f"] = 0.0;
695 state_b.setVariablePositions(joint_values);
696
697 for (size_t i = 0; i <= 5; ++i)
698 {
699 double t = static_cast<double>(i) / 5.;
700 state_a.interpolate(state_b, t, interpolated_state, robot_model_->getJointModelGroup("base_from_base_to_e"));
701 EXPECT_NEAR(10.0 * t, interpolated_state.getVariablePosition("base_joint/x"), EPSILON) << "Base joint "
702 "interpolation failed.";
703 EXPECT_NEAR(20.0 * (1 - t), interpolated_state.getVariablePosition("base_joint/y"), EPSILON) << "Base joint "
704 "interpolation "
705 "failed.";
706 if (t < 0.5)
707 {
708 EXPECT_NEAR(3 * M_PI / 4 + (M_PI / 2) * t, interpolated_state.getVariablePosition("base_joint/theta"), EPSILON)
709 << "Base joint theta interpolation failed.";
710 EXPECT_NEAR(-4 * M_PI / 5 - (2 * M_PI / 5) * t, interpolated_state.getVariablePosition("joint_a"), EPSILON)
711 << "Continuous joint interpolation failed.";
712 }
713 else
714 {
715 EXPECT_NEAR(-3 * M_PI / 4 - (M_PI / 2) * (1 - t), interpolated_state.getVariablePosition("base_joint/theta"),
716 EPSILON)
717 << "Base joint theta interpolation failed.";
718 EXPECT_NEAR(4 * M_PI / 5 + (2 * M_PI / 5) * (1 - t), interpolated_state.getVariablePosition("joint_a"), EPSILON)
719 << "Continuous joint interpolation failed.";
720 }
721 EXPECT_NEAR(0.07 * t, interpolated_state.getVariablePosition("joint_c"), EPSILON) << "Interpolation of joint_c "
722 "failed.";
723 EXPECT_NEAR(1 - t, interpolated_state.getVariablePosition("joint_f"), EPSILON) << "Interpolation of joint_f "
724 "failed.";
725 EXPECT_NEAR(1.5 * (1 - t) + 0.1, interpolated_state.getVariablePosition("mim_f"), EPSILON) << "Interpolation of "
726 "mimic joint mim_f "
727 "failed.";
728 }
729
730 bool nan_exception = false;
731 try
732 {
733 state_a.interpolate(state_b, 1. / 0., interpolated_state, robot_model_->getJointModelGroup("base_from_base_to_e"));
734 }
735 catch (std::exception& e)
736 {
737 std::cout << "Caught expected exception: " << e.what() << '\n';
738 nan_exception = true;
739 }
740 EXPECT_TRUE(nan_exception) << "NaN interpolation parameter did not create expected exception.";
741}
742
743TEST_F(OneRobot, rigidlyConnectedParent)
744{
745 // link_e is its own rigidly-connected parent
746 const moveit::core::LinkModel* link_e{ robot_model_->getLinkModel("link_e") };
747 EXPECT_EQ(robot_model_->getRigidlyConnectedParentLinkModel(link_e), link_e);
748
749 // link_b is rigidly connected to its parent link_a
750 const moveit::core::LinkModel* link_a{ robot_model_->getLinkModel("link_a") };
751 const moveit::core::LinkModel* link_b{ robot_model_->getLinkModel("link_b") };
752 EXPECT_EQ(robot_model_->getRigidlyConnectedParentLinkModel(link_b), link_a);
753
754 moveit::core::RobotState state(robot_model_);
755 state.setToDefaultValues();
756
757 Eigen::Isometry3d a_to_b;
758 EXPECT_EQ(state.getRigidlyConnectedParentLinkModel("link_b"), link_a);
759
760 // attach "object" with "subframe" to link_b
761 state.attachBody(std::make_unique<moveit::core::AttachedBody>(
762 link_b, "object", Eigen::Isometry3d(Eigen::Translation3d(1, 0, 0)), std::vector<shapes::ShapeConstPtr>{},
763 EigenSTL::vector_Isometry3d{}, std::set<std::string>{}, trajectory_msgs::msg::JointTrajectory{},
764 moveit::core::FixedTransformsMap{ { "subframe", Eigen::Isometry3d(Eigen::Translation3d(0, 0, 1)) } }));
765
766 // RobotState's version should resolve these too
767 Eigen::Isometry3d transform;
768 EXPECT_EQ(link_a, state.getRigidlyConnectedParentLinkModel("object"));
769 EXPECT_EQ(link_a, state.getRigidlyConnectedParentLinkModel("object/subframe"));
770
771 // test failure cases
772 EXPECT_EQ(nullptr, state.getRigidlyConnectedParentLinkModel("no_object"));
773 EXPECT_EQ(nullptr, state.getRigidlyConnectedParentLinkModel("object/no_subframe"));
774 EXPECT_EQ(nullptr, state.getRigidlyConnectedParentLinkModel(""));
775 EXPECT_EQ(nullptr, state.getRigidlyConnectedParentLinkModel("object/"));
776 EXPECT_EQ(nullptr, state.getRigidlyConnectedParentLinkModel("/"));
777
778 // link names with '/' should still work as before
779 const moveit::core::LinkModel* link_with_slash{ robot_model_->getLinkModel("link/with/slash") };
780 EXPECT_TRUE(link_with_slash);
781 const moveit::core::LinkModel* rigid_parent_of_link_with_slash =
782 state.getRigidlyConnectedParentLinkModel("link/with/slash");
783 ASSERT_TRUE(rigid_parent_of_link_with_slash);
784 EXPECT_EQ("base_link", rigid_parent_of_link_with_slash->getName());
785
786 // the last /-separated component of an object might be a subframe
787 state.attachBody(std::make_unique<moveit::core::AttachedBody>(
788 link_with_slash, "object/with/slash", Eigen::Isometry3d(Eigen::Translation3d(1, 0, 0)),
789 std::vector<shapes::ShapeConstPtr>{}, EigenSTL::vector_Isometry3d{}, std::set<std::string>{},
790 trajectory_msgs::msg::JointTrajectory{},
791 moveit::core::FixedTransformsMap{ { "sub/frame", Eigen::Isometry3d(Eigen::Translation3d(0, 0, 1)) } }));
792 const moveit::core::LinkModel* rigid_parent_of_object =
793 state.getRigidlyConnectedParentLinkModel("object/with/slash/sub/frame");
794 ASSERT_TRUE(rigid_parent_of_object);
795 EXPECT_EQ(rigid_parent_of_link_with_slash, rigid_parent_of_object);
796}
797
798TEST(getJacobian, RevoluteJoints)
799{
800 // Robot URDF with four revolute joints.
801 constexpr char robot_urdf[] = R"(
802 <?xml version="1.0" ?>
803 <robot name="one_robot">
804 <link name="base_link"/>
805 <joint name="joint_a_revolute" type="revolute">
806 <axis xyz="0 0 1"/>
807 <parent link="base_link"/>
808 <child link="link_a"/>
809 <origin rpy="0 0 0" xyz="0 0 0"/>
810 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
811 </joint>
812 <link name="link_a"/>
813 <joint name="joint_b_revolute" type="revolute">
814 <axis xyz="0 0 1"/>
815 <parent link="link_a"/>
816 <child link="link_b"/>
817 <origin rpy="0 0 0" xyz="0.0 0.5 0"/>
818 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
819 </joint>
820 <link name="link_b"/>
821 <joint name="joint_c_revolute" type="revolute">
822 <axis xyz="0 1 0"/>
823 <parent link="link_b"/>
824 <child link="link_c"/>
825 <origin rpy="0 0 0" xyz="0.2 0.2 0"/>
826 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
827 </joint>
828 <link name="link_c"/>
829 <joint name="joint_d_revolute" type="revolute">
830 <axis xyz="1 0 0"/>
831 <parent link="link_c"/>
832 <child link="link_d"/>
833 <origin rpy="0 0 0" xyz="0.0 0.2 0.4"/>
834 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
835 </joint>
836 <link name="link_d"/>
837 </robot>
838 )";
839
840 constexpr char robot_srdf[] = R"xml(
841 <?xml version="1.0" ?>
842 <robot name="one_robot">
843 <group name="base_to_tip">
844 <joint name="joint_a_revolute"/>
845 <joint name="joint_b_revolute"/>
846 <joint name="joint_c_revolute"/>
847 <joint name="joint_d_revolute"/>
848 </group>
849 </robot>
850 )xml";
851
852 const urdf::ModelInterfaceSharedPtr urdf_model = urdf::parseURDF(robot_urdf);
853 ASSERT_TRUE(urdf_model);
854 const auto srdf_model = std::make_shared<srdf::Model>();
855 ASSERT_TRUE(srdf_model->initString(*urdf_model, robot_srdf));
856 const auto robot_model = std::make_shared<moveit::core::RobotModel>(urdf_model, srdf_model);
857
858 moveit::core::RobotState state(robot_model);
859 const moveit::core::JointModelGroup* jmg = state.getJointModelGroup("base_to_tip");
860
861 // Some made-up numbers, at zero and non-zero robot configurations.
862 checkJacobian(state, *jmg, makeVector({ 0.0, 0.0, 0.0, 0.0 }), makeVector({ 0.1, 0.2, 0.3, 0.4 }));
863 checkJacobian(state, *jmg, makeVector({ 0.1, 0.2, 0.3, 0.4 }), makeVector({ 0.5, 0.3, 0.2, 0.1 }));
864}
865
866TEST(getJacobian, RevoluteJointsButDifferentLink)
867{
868 // Robot URDF with four revolute joints.
869 constexpr char robot_urdf[] = R"(
870 <?xml version="1.0" ?>
871 <robot name="one_robot">
872 <link name="base_link"/>
873 <joint name="joint_a_revolute" type="revolute">
874 <axis xyz="0 0 1"/>
875 <parent link="base_link"/>
876 <child link="link_a"/>
877 <origin rpy="0 0 0" xyz="0 0 0"/>
878 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
879 </joint>
880 <link name="link_a"/>
881 <joint name="joint_b_revolute" type="revolute">
882 <axis xyz="0 0 1"/>
883 <parent link="link_a"/>
884 <child link="link_b"/>
885 <origin rpy="0 0 0" xyz="0.0 0.5 0"/>
886 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
887 </joint>
888 <link name="link_b"/>
889 <joint name="joint_c_revolute" type="revolute">
890 <axis xyz="0 1 0"/>
891 <parent link="link_b"/>
892 <child link="link_c"/>
893 <origin rpy="0 0 0" xyz="0.2 0.2 0"/>
894 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
895 </joint>
896 <link name="link_c"/>
897 <joint name="joint_d_revolute" type="revolute">
898 <axis xyz="1 0 0"/>
899 <parent link="link_c"/>
900 <child link="link_d"/>
901 <origin rpy="0 0 0" xyz="0.0 0.2 0.4"/>
902 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
903 </joint>
904 <link name="link_d"/>
905 </robot>
906 )";
907
908 constexpr char robot_srdf[] = R"xml(
909 <?xml version="1.0" ?>
910 <robot name="one_robot">
911 <group name="base_to_tip">
912 <joint name="joint_a_revolute"/>
913 <joint name="joint_b_revolute"/>
914 <joint name="joint_c_revolute"/>
915 <joint name="joint_d_revolute"/>
916 </group>
917 </robot>
918 )xml";
919
920 const urdf::ModelInterfaceSharedPtr urdf_model = urdf::parseURDF(robot_urdf);
921 ASSERT_TRUE(urdf_model);
922 const auto srdf_model = std::make_shared<srdf::Model>();
923 ASSERT_TRUE(srdf_model->initString(*urdf_model, robot_srdf));
924 const auto robot_model = std::make_shared<moveit::core::RobotModel>(urdf_model, srdf_model);
925
926 moveit::core::RobotState state(robot_model);
927 const moveit::core::JointModelGroup* jmg = state.getJointModelGroup("base_to_tip");
928
929 // Some made-up numbers, at zero and non-zero robot configurations.
930 checkJacobian(state, *jmg, makeVector({ 0.0, 0.0, 0.0, 0.0 }), makeVector({ 0.1, 0.2, 0.3, 0.4 }),
931 state.getLinkModel("link_c"));
932 checkJacobian(state, *jmg, makeVector({ 0.1, 0.2, 0.3, 0.4 }), makeVector({ 0.5, 0.3, 0.2, 0.1 }),
933 state.getLinkModel("link_c"));
934}
935
936TEST(getJacobian, RevoluteAndPrismaticJoints)
937{
938 // Robot URDF with three revolute joints and one prismatic joint.
939 constexpr char robot_urdf[] = R"(
940 <?xml version="1.0" ?>
941 <robot name="one_robot">
942 <link name="base_link"/>
943 <joint name="joint_a_revolute" type="revolute">
944 <axis xyz="0 0 1"/>
945 <parent link="base_link"/>
946 <child link="link_a"/>
947 <origin rpy="0 0 0" xyz="0 0 0"/>
948 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
949 </joint>
950 <link name="link_a"/>
951 <joint name="joint_b_revolute" type="revolute">
952 <axis xyz="0 0 1"/>
953 <parent link="link_a"/>
954 <child link="link_b"/>
955 <origin rpy="0 0 0" xyz="0.0 0.5 0"/>
956 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
957 </joint>
958 <link name="link_b"/>
959 <joint name="joint_c_prismatic" type="prismatic">
960 <axis xyz="0 1 0"/>
961 <parent link="link_b"/>
962 <child link="link_c"/>
963 <origin rpy="0 0 0" xyz="0.2 0.2 0"/>
964 <limit effort="100.0" lower="-1.0" upper="1.0" velocity="0.2"/>
965 </joint>
966 <link name="link_c"/>
967 <joint name="joint_d_revolute" type="revolute">
968 <axis xyz="1 0 0"/>
969 <parent link="link_c"/>
970 <child link="link_d"/>
971 <origin rpy="0 0 0" xyz="0.0 0.2 0.4"/>
972 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
973 </joint>
974 <link name="link_d"/>
975 </robot>
976 )";
977
978 constexpr char robot_srdf[] = R"xml(
979 <?xml version="1.0" ?>
980 <robot name="one_robot">
981 <group name="base_to_tip">
982 <joint name="joint_a_revolute"/>
983 <joint name="joint_b_revolute"/>
984 <joint name="joint_c_prismatic"/>
985 <joint name="joint_d_revolute"/>
986 </group>
987 </robot>
988 )xml";
989
990 const urdf::ModelInterfaceSharedPtr urdf_model = urdf::parseURDF(robot_urdf);
991 ASSERT_TRUE(urdf_model);
992 const auto srdf_model = std::make_shared<srdf::Model>();
993 ASSERT_TRUE(srdf_model->initString(*urdf_model, robot_srdf));
994 const auto robot_model = std::make_shared<moveit::core::RobotModel>(urdf_model, srdf_model);
995
996 moveit::core::RobotState state(robot_model);
997 const moveit::core::JointModelGroup* jmg = state.getJointModelGroup("base_to_tip");
998
999 // Some made-up numbers, at zero and non-zero robot configurations.
1000 checkJacobian(state, *jmg, makeVector({ 0.0, 0.0, 0.0, 0.0 }), makeVector({ 0.1, 0.2, 0.3, 0.4 }));
1001 checkJacobian(state, *jmg, makeVector({ 0.1, 0.2, 0.3, 0.4 }), makeVector({ 0.5, 0.3, 0.2, 0.1 }));
1002}
1003
1004TEST(getJacobian, RevoluteAndFixedJoints)
1005{
1006 // Robot URDF with two revolute joints and two fixed joints.
1007 constexpr char robot_urdf[] = R"(
1008 <?xml version="1.0" ?>
1009 <robot name="one_robot">
1010 <link name="base_link"/>
1011 <joint name="joint_a_revolute" type="revolute">
1012 <axis xyz="0 0 1"/>
1013 <parent link="base_link"/>
1014 <child link="link_a"/>
1015 <origin rpy="0 0 0" xyz="0 0 0"/>
1016 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
1017 </joint>
1018 <link name="link_a"/>
1019 <joint name="joint_b_fixed" type="fixed">
1020 <parent link="link_a"/>
1021 <child link="link_b"/>
1022 </joint>
1023 <link name="link_b"/>
1024 <joint name="joint_c_fixed" type="fixed">
1025 <parent link="link_b"/>
1026 <child link="link_c"/>
1027 </joint>
1028 <link name="link_c"/>
1029 <joint name="joint_d_revolute" type="revolute">
1030 <axis xyz="1 0 0"/>
1031 <parent link="link_c"/>
1032 <child link="link_d"/>
1033 <origin rpy="0 0 0" xyz="0.0 0.2 0.4"/>
1034 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
1035 </joint>
1036 <link name="link_d"/>
1037 </robot>
1038 )";
1039
1040 constexpr char robot_srdf[] = R"xml(
1041 <?xml version="1.0" ?>
1042 <robot name="one_robot">
1043 <group name="base_to_tip">
1044 <joint name="joint_a_revolute"/>
1045 <joint name="joint_b_fixed"/>
1046 <joint name="joint_c_fixed"/>
1047 <joint name="joint_d_revolute"/>
1048 </group>
1049 </robot>
1050 )xml";
1051
1052 const urdf::ModelInterfaceSharedPtr urdf_model = urdf::parseURDF(robot_urdf);
1053 ASSERT_TRUE(urdf_model);
1054 const auto srdf_model = std::make_shared<srdf::Model>();
1055 ASSERT_TRUE(srdf_model->initString(*urdf_model, robot_srdf));
1056 const auto robot_model = std::make_shared<moveit::core::RobotModel>(urdf_model, srdf_model);
1057
1058 moveit::core::RobotState state(robot_model);
1059 const moveit::core::JointModelGroup* jmg = state.getJointModelGroup("base_to_tip");
1060
1061 // Some made-up numbers, at zero and non-zero robot configurations.
1062 checkJacobian(state, *jmg, makeVector({ 0.0, 0.0 }), makeVector({ 0.1, 0.4 }));
1063 checkJacobian(state, *jmg, makeVector({ 0.1, 0.4 }), makeVector({ 0.5, 0.1 }));
1064}
1065
1066TEST(getJacobian, RevolutePlanarAndPrismaticJoints)
1067{
1068 // Robot URDF with two revolute joints, one planar joint and one prismatic.
1069 constexpr char robot_urdf[] = R"(
1070 <?xml version="1.0" ?>
1071 <robot name="one_robot">
1072 <link name="base_link"/>
1073 <joint name="joint_a_planar" type="planar">
1074 <axis xyz="0 0 1"/>
1075 <parent link="base_link"/>
1076 <child link="link_a"/>
1077 <origin rpy="0 0 0" xyz="0 0 0"/>
1078 <limit effort="100.0" lower="-1.0" upper="1.0" velocity="0.2"/>
1079 </joint>
1080 <link name="link_a"/>
1081 <joint name="joint_b_revolute" type="revolute">
1082 <axis xyz="0 0 1"/>
1083 <parent link="link_a"/>
1084 <child link="link_b"/>
1085 <origin rpy="0 0 0" xyz="0.0 0.5 0"/>
1086 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
1087 </joint>
1088 <link name="link_b"/>
1089 <joint name="joint_c_prismatic" type="prismatic">
1090 <axis xyz="0 1 0"/>
1091 <parent link="link_b"/>
1092 <child link="link_c"/>
1093 <origin rpy="0 0 0" xyz="0.2 0.2 0"/>
1094 <limit effort="100.0" lower="-1.0" upper="1.0" velocity="0.2"/>
1095 </joint>
1096 <link name="link_c"/>
1097 <joint name="joint_d_revolute" type="revolute">
1098 <axis xyz="1 0 0"/>
1099 <parent link="link_c"/>
1100 <child link="link_d"/>
1101 <origin rpy="0 0 0" xyz="0.0 0.2 0.4"/>
1102 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
1103 </joint>
1104 <link name="link_d"/>
1105 </robot>
1106 )";
1107
1108 constexpr char robot_srdf[] = R"xml(
1109 <?xml version="1.0" ?>
1110 <robot name="one_robot">
1111 <group name="base_to_tip">
1112 <joint name="joint_a_planar"/>
1113 <joint name="joint_b_revolute"/>
1114 <joint name="joint_c_prismatic"/>
1115 <joint name="joint_d_revolute"/>
1116 </group>
1117 </robot>
1118 )xml";
1119
1120 const urdf::ModelInterfaceSharedPtr urdf_model = urdf::parseURDF(robot_urdf);
1121 ASSERT_TRUE(urdf_model);
1122 const auto srdf_model = std::make_shared<srdf::Model>();
1123 ASSERT_TRUE(srdf_model->initString(*urdf_model, robot_srdf));
1124 const auto robot_model = std::make_shared<moveit::core::RobotModel>(urdf_model, srdf_model);
1125
1126 moveit::core::RobotState state(robot_model);
1127 const moveit::core::JointModelGroup* jmg = state.getJointModelGroup("base_to_tip");
1128
1129 checkJacobian(state, *jmg, makeVector({ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }),
1130 makeVector({ 0.2, 0.05, 0.1, 0.2, 0.3, 0.4 }));
1131}
1132
1133TEST(getJacobian, GroupNotAtOrigin)
1134{
1135 // URDF with a 3 DOF robot not at the URDF origin.
1136 constexpr char robot_urdf[] = R"(
1137 <?xml version="1.0" ?>
1138 <robot name="one_robot">
1139 <link name="origin"/>
1140 <joint name="fixed_offset" type="fixed">
1141 <parent link="origin"/>
1142 <child link="link_a"/>
1143 <origin rpy="0 0 1.5707" xyz="0.0 0.0 0.0"/>
1144 </joint>
1145 <link name="link_a"/>
1146 <joint name="joint_a_revolute" type="revolute">
1147 <axis xyz="1 0 0"/>
1148 <parent link="link_a"/>
1149 <child link="link_b"/>
1150 <origin rpy="0 0 0" xyz="0.0 0.2 0.4"/>
1151 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
1152 </joint>
1153 <link name="link_b"/>
1154 <joint name="joint_b_revolute" type="revolute">
1155 <axis xyz="1 0 0"/>
1156 <parent link="link_b"/>
1157 <child link="link_c"/>
1158 <origin rpy="0 0 0" xyz="0.0 0.2 0.4"/>
1159 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
1160 </joint>
1161 <link name="link_c"/>
1162 <joint name="joint_c_revolute" type="revolute">
1163 <axis xyz="1 0 0"/>
1164 <parent link="link_c"/>
1165 <child link="link_d"/>
1166 <origin rpy="0 0 0" xyz="0.0 0.2 0.4"/>
1167 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
1168 </joint>
1169 <link name="link_d"/>
1170 </robot>
1171 )";
1172
1173 constexpr char robot_srdf[] = R"xml(
1174 <?xml version="1.0" ?>
1175 <robot name="one_robot">
1176 <group name="base_to_tip">
1177 <joint name="joint_a_revolute"/>
1178 <joint name="joint_b_revolute"/>
1179 <joint name="joint_c_revolute"/>
1180 </group>
1181 </robot>
1182 )xml";
1183
1184 const urdf::ModelInterfaceSharedPtr urdf_model = urdf::parseURDF(robot_urdf);
1185 ASSERT_TRUE(urdf_model);
1186 const auto srdf_model = std::make_shared<srdf::Model>();
1187 ASSERT_TRUE(srdf_model->initString(*urdf_model, robot_srdf));
1188 const auto robot_model = std::make_shared<moveit::core::RobotModel>(urdf_model, srdf_model);
1189
1190 moveit::core::RobotState state(robot_model);
1191 const moveit::core::JointModelGroup* jmg = state.getJointModelGroup("base_to_tip");
1192
1193 // Some made-up numbers, at zero and non-zero robot configurations.
1194 checkJacobian(state, *jmg, makeVector({ 0.0, 0.0, 0.0 }), makeVector({ 0.1, 0.4, 0.2 }));
1195 checkJacobian(state, *jmg, makeVector({ 0.1, 0.4, 0.3 }), makeVector({ 0.5, 0.1, 0.2 }));
1196}
1197
1198TEST(getJointPositions, getFixedJointValue)
1199{
1200 // Robot URDF with two revolute joints and a final fixed joint.
1201 // Trying to get the value of the fixed joint should work and return nullptr.
1202 constexpr char robot_urdf[] = R"(
1203 <?xml version="1.0" ?>
1204 <robot name="one_robot">
1205 <link name="base_link"/>
1206 <joint name="joint_a_revolute" type="revolute">
1207 <axis xyz="0 0 1"/>
1208 <parent link="base_link"/>
1209 <child link="link_a"/>
1210 <origin rpy="0 0 0" xyz="0 0 0"/>
1211 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
1212 </joint>
1213 <link name="link_a"/>
1214 <joint name="joint_b_revolute" type="revolute">
1215 <axis xyz="0 0 1"/>
1216 <parent link="link_a"/>
1217 <child link="link_b"/>
1218 <origin rpy="0 0 0" xyz="0 0 0"/>
1219 <limit effort="100.0" lower="-3.14" upper="3.14" velocity="0.2"/>
1220 </joint>
1221 <link name="link_b"/>
1222 <joint name="joint_c_fixed" type="fixed">
1223 <parent link="link_b"/>
1224 <child link="link_c"/>
1225 </joint>
1226 <link name="link_c"/>
1227 </robot>
1228 )";
1229
1230 constexpr char robot_srdf[] = R"xml(
1231 <?xml version="1.0" ?>
1232 <robot name="one_robot">
1233 <group name="base_to_tip">
1234 <joint name="joint_a_revolute"/>
1235 <joint name="joint_b_revolute"/>
1236 <joint name="joint_c_fixed"/>
1237 </group>
1238 </robot>
1239 )xml";
1240
1241 const urdf::ModelInterfaceSharedPtr urdf_model = urdf::parseURDF(robot_urdf);
1242 ASSERT_TRUE(urdf_model);
1243 const auto srdf_model = std::make_shared<srdf::Model>();
1244 ASSERT_TRUE(srdf_model->initString(*urdf_model, robot_srdf));
1245 const auto robot_model = std::make_shared<moveit::core::RobotModel>(urdf_model, srdf_model);
1246
1247 // Getting the value of the last fixed joint should return nullptr, since it doesn't have an active variable.
1248 moveit::core::RobotState state(robot_model);
1249 state.setToDefaultValues();
1250 const double* joint_value = state.getJointPositions("joint_c_fixed");
1251 ASSERT_EQ(joint_value, nullptr);
1252}
1253
1254int main(int argc, char** argv)
1255{
1256 testing::InitGoogleTest(&argc, argv);
1257 return RUN_ALL_TESTS();
1258}
void TearDown() override
void SetUp() override
moveit::core::RobotModelConstPtr robot_model_
const std::vector< std::string > & getJointModelNames() const
Get the names of the joints in this group. These are the names of the joints returned by getJointMode...
const std::vector< const LinkModel * > & getLinkModels() const
Get the links that are part of this joint group.
const std::vector< std::string > & getUpdatedLinkModelNames() const
Get the names of the links returned by getUpdatedLinkModels()
unsigned int getVariableCount() const
Get the number of variables that describe this joint group. This includes variables necessary for mim...
const std::vector< std::string > & getLinkModelNames() const
Get the names of the links that are part of this joint group.
const std::vector< const JointModel * > & getJointModels() const
Get all the joints in this group (including fixed and mimic joints).
A joint from the robot. Models the transform that this joint applies in the kinematic chain....
const LinkModel * getParentLinkModel() const
Get the link that this joint connects to. The robot is assumed to start with a joint,...
A link from the robot. Contains the constant transform applied to the link and its geometry.
const std::string & getName() const
The name of this link.
Easily build different robot models for testing. Essentially a programmer-friendly light wrapper arou...
void addInertial(const std::string &link_name, double mass, geometry_msgs::msg::Pose origin, double ixx, double ixy, double ixz, double iyy, double iyz, double izz)
void addCollisionBox(const std::string &link_name, const std::vector< double > &dims, geometry_msgs::msg::Pose origin)
Adds a collision box to a specific link.
bool isValid()
Returns true if the building process so far has been valid.
void addGroup(const std::vector< std::string > &links, const std::vector< std::string > &joints, const std::string &name)
Adds a new group using a list of links and a list of joints.
moveit::core::RobotModelPtr build()
Builds and returns the robot model added to the builder.
void addVirtualJoint(const std::string &parent_frame, const std::string &child_link, const std::string &type, const std::string &name="")
Adds a virtual joint to the SRDF.
void addVisualBox(const std::string &link_name, const std::vector< double > &size, geometry_msgs::msg::Pose origin)
Adds a visual box to a specific link.
Representation of a robot's state. This includes position, velocity, acceleration and effort.
void setVariablePositions(const double *position)
It is assumed positions is an array containing the new positions for all variables in this state....
void setVariableAcceleration(const std::string &variable, double value)
Set the acceleration of a variable. If an unknown variable name is specified, an exception is thrown.
double getVariableAcceleration(const std::string &variable) const
Get the acceleration of a particular variable. An exception is thrown if the variable is not known.
void attachBody(std::unique_ptr< AttachedBody > attached_body)
Add an attached body to this state.
bool getJacobian(const JointModelGroup *group, const LinkModel *link, const Eigen::Vector3d &reference_point_position, Eigen::MatrixXd &jacobian, bool use_quaternion_representation=false) const
Compute the Jacobian with reference to a particular point on a given link, for a specified group.
const double * getJointPositions(const std::string &joint_name) const
void setVariableVelocity(const std::string &variable, double value)
Set the velocity of a variable. If an unknown variable name is specified, an exception is thrown.
double getVariableVelocity(const std::string &variable) const
Get the velocity of a particular variable. An exception is thrown if the variable is not known.
void setVariablePosition(const std::string &variable, double value)
Set the position of a single variable. An exception is thrown if the variable name is not known.
void setJointGroupPositions(const std::string &joint_group_name, const double *gstate)
Given positions for the variables that make up a group, in the order found in the group (including va...
void copyJointGroupPositions(const std::string &joint_group_name, std::vector< double > &gstate) const
For a given group, copy the position values of the variables that make up the group into another loca...
const JointModelGroup * getJointModelGroup(const std::string &group) const
Get the model of a particular joint group.
void printStatePositionsWithJointLimits(const moveit::core::JointModelGroup *jmg, std::ostream &out=std::cout) const
Output to console the current state of the robot's joint limits.
const LinkModel * getLinkModel(const std::string &link) const
Get the model of a particular link.
const RobotModelConstPtr & getRobotModel() const
Get the robot model this state is constructed for.
void updateLinkTransforms()
Update the reference frame transforms for links. This call is needed before using the transforms of l...
void interpolate(const RobotState &to, double t, RobotState &state) const
const Eigen::Isometry3d & getCollisionBodyTransform(const std::string &link_name, std::size_t index)
Get the link transform w.r.t. the root link (model frame) of the RobotModel. This is typically the ro...
double distance(const RobotState &other) const
Return the sum of joint distances to "other" state. An L1 norm. Only considers active joints.
void setToRandomPositions()
Set all joints to random values. Values will be within default bounds.
bool satisfiesBounds(double margin=0.0) const
double getVariablePosition(const std::string &variable) const
Get the position of a particular variable. An exception is thrown if the variable is not known.
void setToDefaultValues()
Set all joints to their default positions. The default position is 0, or if that is not within bounds...
std::size_t getVariableCount() const
Get the number of variables that make up this state.
const Eigen::Isometry3d & getGlobalLinkTransform(const std::string &link_name)
Get the link transform w.r.t. the root link (model frame) of the RobotModel. This is typically the ro...
const moveit::core::LinkModel * getRigidlyConnectedParentLinkModel(const std::string &frame, const moveit::core::JointModelGroup *jmg=nullptr) const
Get the latest link upwards the kinematic tree which is only connected via fixed joints.
void setJointPositions(const std::string &joint_name, const double *position)
std::map< std::string, Eigen::Isometry3d, std::less< std::string >, Eigen::aligned_allocator< std::pair< const std::string, Eigen::Isometry3d > > > FixedTransformsMap
Map frame names to the transformation matrix that can transform objects from the frame name to the pl...
int main(int argc, char **argv)
TEST(Loading, SimpleRobot)
#define EXPECT_NEAR_TRACED(...)
TEST_F(OneRobot, setToDefaultValues)