moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
robot_state_benchmark.cpp
Go to the documentation of this file.
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2023, PickNik Robotics.
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: Robert Haschke, Mario Prats */
36
37// This file contains various benchmarks related to RobotState and matrix multiplication and inverse with Eigen types.
38// To run this benchmark, 'cd' to the build/moveit_core/robot_state directory and directly run the binary.
39
40#include <benchmark/benchmark.h>
41#include <random>
42#include <kdl_parser/kdl_parser.hpp>
43#include <kdl/treejnttojacsolver.hpp>
47
48// Robot and planning group for benchmarks.
49constexpr char PANDA_TEST_ROBOT[] = "panda";
50constexpr char PANDA_TEST_GROUP[] = "panda_arm";
51
52namespace
53{
54Eigen::Isometry3d createTestIsometry()
55{
56 // An arbitrary Eigen::Isometry3d object.
57 return Eigen::Translation3d(1, 2, 3) * Eigen::AngleAxisd(0.13 * M_PI, Eigen::Vector3d::UnitX()) *
58 Eigen::AngleAxisd(0.29 * M_PI, Eigen::Vector3d::UnitY()) *
59 Eigen::AngleAxisd(0.42 * M_PI, Eigen::Vector3d::UnitZ());
60}
61} // namespace
62
63// Benchmark time to multiply an Eigen::Affine3d with an Eigen::Matrix4d.
64// The NoAlias versions just use Eigen's .noalias() modifier, allowing to write the result of matrix multiplication
65// directly into the result matrix instead of using an intermediate temporary (which is the default).
66static void multiplyAffineTimesMatrixNoAlias(benchmark::State& st)
67{
68 Eigen::Isometry3d isometry = createTestIsometry();
69 Eigen::Affine3d result;
70 for (auto _ : st)
71 {
72 benchmark::DoNotOptimize(result.affine().noalias() = isometry.affine() * isometry.matrix());
73 benchmark::ClobberMemory();
74 }
75}
76
77// Benchmark time to multiply an Eigen::Matrix4d with an Eigen::Matrix4d.
78static void multiplyMatrixTimesMatrixNoAlias(benchmark::State& st)
79{
80 Eigen::Isometry3d isometry = createTestIsometry();
81 Eigen::Isometry3d result;
82 for (auto _ : st)
83 {
84 benchmark::DoNotOptimize(result.matrix().noalias() = isometry.matrix() * isometry.matrix());
85 benchmark::ClobberMemory();
86 }
87}
88
89// Benchmark time to multiply an Eigen::Isometry3d with an Eigen::Isometry3d.
90static void multiplyIsometryTimesIsometryNoAlias(benchmark::State& st)
91{
92 Eigen::Isometry3d isometry = createTestIsometry();
93 Eigen::Isometry3d result;
94 for (auto _ : st)
95 {
96 benchmark::DoNotOptimize(result.affine().noalias() = isometry.affine() * isometry.matrix());
97 benchmark::ClobberMemory();
98 }
99}
100
101// Benchmark time to multiply an Eigen::Matrix4d with an Eigen::Matrix4d.
102static void multiplyMatrixTimesMatrix(benchmark::State& st)
103{
104 Eigen::Isometry3d isometry = createTestIsometry();
105 Eigen::Isometry3d result;
106 for (auto _ : st)
107 {
108 benchmark::DoNotOptimize(result.matrix() = isometry.matrix() * isometry.matrix());
109 benchmark::ClobberMemory();
110 }
111}
112// Benchmark time to multiply an Eigen::Isometry3d with an Eigen::Isometry3d.
113static void multiplyIsometryTimesIsometry(benchmark::State& st)
114{
115 Eigen::Isometry3d isometry = createTestIsometry();
116 Eigen::Isometry3d result;
117 for (auto _ : st)
118 {
119 benchmark::DoNotOptimize(result = isometry * isometry);
120 benchmark::ClobberMemory();
121 }
122}
123
124// Benchmark time to invert an Eigen::Isometry3d.
125static void inverseIsometry3d(benchmark::State& st)
126{
127 Eigen::Isometry3d isometry = createTestIsometry();
128 Eigen::Isometry3d result;
129 for (auto _ : st)
130 {
131 benchmark::DoNotOptimize(result = isometry.inverse());
132 benchmark::ClobberMemory();
133 }
134}
135
136// Benchmark time to invert an Eigen::Affine3d(Eigen::Isometry).
137static void inverseAffineIsometry(benchmark::State& st)
138{
139 Eigen::Affine3d affine = createTestIsometry();
140 Eigen::Affine3d result;
141 for (auto _ : st)
142 {
143 benchmark::DoNotOptimize(result = affine.inverse(Eigen::Isometry));
144 benchmark::ClobberMemory();
145 }
146}
147
148// Benchmark time to invert an Eigen::Affine3d.
149static void inverseAffine(benchmark::State& st)
150{
151 Eigen::Affine3d affine = createTestIsometry();
152 Eigen::Affine3d result;
153 for (auto _ : st)
154 {
155 benchmark::DoNotOptimize(result = affine.inverse());
156 benchmark::ClobberMemory();
157 }
158}
159
160// Benchmark time to invert an Eigen::Matrix4d.
161static void inverseMatrix4d(benchmark::State& st)
162{
163 Eigen::Affine3d affine = createTestIsometry();
164 Eigen::Affine3d result;
165 for (auto _ : st)
166 {
167 benchmark::DoNotOptimize(result = affine.matrix().inverse());
168 benchmark::ClobberMemory();
169 }
170}
171
172struct RobotStateBenchmark : ::benchmark::Fixture
173{
174 void SetUp(const ::benchmark::State& /*state*/) override
175 {
176 std::ignore = rcutils_logging_set_logger_level("moveit_robot_model.robot_model", RCUTILS_LOG_SEVERITY_WARN);
178 }
179
180 std::vector<moveit::core::RobotState> constructStates(size_t num)
181 {
182 std::vector<moveit::core::RobotState> states;
183 states.reserve(num);
184 for (size_t i = 0; i < num; i++)
185 states.emplace_back(robot_model);
186 return states;
187 }
188 std::vector<moveit::core::RobotState> constructStates(size_t num, const moveit::core::RobotState& state)
189 {
190 std::vector<moveit::core::RobotState> states;
191 states.reserve(num);
192 for (size_t i = 0; i < num; i++)
193 states.emplace_back(state);
194 return states;
195 }
196
197 std::vector<size_t> randomPermutation(size_t num)
198 {
199 std::vector<size_t> result(num);
200 std::iota(result.begin(), result.end(), 0);
201
202 std::mt19937 generator;
203 std::shuffle(result.begin(), result.end(), generator);
204 return result;
205 }
206
207 moveit::core::RobotModelPtr robot_model;
208};
209
210// Benchmark time to construct RobotStates
211BENCHMARK_DEFINE_F(RobotStateBenchmark, construct)(benchmark::State& st)
212{
213 for (auto _ : st)
214 {
215 auto states = constructStates(st.range(0));
216 benchmark::DoNotOptimize(states);
217 benchmark::ClobberMemory();
218 }
219}
220
221// Benchmark time to copy-construct a RobotState.
222BENCHMARK_DEFINE_F(RobotStateBenchmark, copyConstruct)(benchmark::State& st)
223{
224 moveit::core::RobotState state(robot_model);
225 state.setToDefaultValues();
226 state.update();
227
228 for (auto _ : st)
229 {
230 auto states = constructStates(st.range(0), state);
231 benchmark::DoNotOptimize(states);
232 benchmark::ClobberMemory();
233 }
234}
235
236// Benchmark time to call `setToRandomPositions` and `update` on RobotState.
237BENCHMARK_DEFINE_F(RobotStateBenchmark, update)(benchmark::State& st)
238{
239 auto states = constructStates(st.range(0));
240 auto permutation = randomPermutation(states.size());
241 for (auto _ : st)
242 {
243 for (auto i : permutation) // process states in random order to challenge the cache
244 {
245 states[i].setToRandomPositions();
246 states[i].update();
247 }
248 }
249}
250
251// Benchmark time to compute the Jacobian, using MoveIt's `getJacobian` function.
252BENCHMARK_DEFINE_F(RobotStateBenchmark, jacobianMoveIt)(benchmark::State& st)
253{
254 moveit::core::RobotState state(robot_model);
256 if (!jmg)
257 {
258 st.SkipWithError("The planning group doesn't exist.");
259 return;
260 }
261
262 // Manually seeded RandomNumberGenerator for deterministic results
263 random_numbers::RandomNumberGenerator rng(0);
264
265 for (auto _ : st)
266 {
267 // Time only the jacobian computation, not the forward kinematics.
268 st.PauseTiming();
269 state.setToRandomPositions(jmg, rng);
270 state.updateLinkTransforms();
271 st.ResumeTiming();
272 state.getJacobian(jmg);
273 }
274}
275
276// Benchmark time to compute the Jacobian using KDL.
277BENCHMARK_DEFINE_F(RobotStateBenchmark, jacobianKDL)(benchmark::State& st)
278{
279 moveit::core::RobotState state(robot_model);
281 if (!jmg)
282 {
283 st.SkipWithError("The planning group doesn't exist.");
284 return;
285 }
286 KDL::Tree kdl_tree;
287 if (!kdl_parser::treeFromUrdfModel(*robot_model->getURDF(), kdl_tree))
288 {
289 st.SkipWithError("Can't create KDL tree.");
290 return;
291 }
292
293 KDL::TreeJntToJacSolver jacobian_solver(kdl_tree);
294 KDL::Jacobian jacobian(kdl_tree.getNrOfJoints());
295 KDL::JntArray kdl_q(kdl_tree.getNrOfJoints());
296 const std::string tip_link = jmg->getLinkModelNames().back();
297
298 // Manually seeded RandomNumberGenerator for deterministic results
299 random_numbers::RandomNumberGenerator rng(0);
300
301 for (auto _ : st)
302 {
303 // Time only the jacobian computation, not the forward kinematics.
304 st.PauseTiming();
305 state.setToRandomPositions(jmg, rng);
306 state.copyJointGroupPositions(jmg, &kdl_q.data[0]);
307 st.ResumeTiming();
308 jacobian_solver.JntToJac(kdl_q, jacobian, tip_link);
309 }
310}
311
312BENCHMARK(multiplyAffineTimesMatrixNoAlias);
313BENCHMARK(multiplyMatrixTimesMatrixNoAlias);
314BENCHMARK(multiplyIsometryTimesIsometryNoAlias);
315BENCHMARK(multiplyMatrixTimesMatrix);
316BENCHMARK(multiplyIsometryTimesIsometry);
317
318BENCHMARK(inverseIsometry3d);
319BENCHMARK(inverseAffineIsometry);
320BENCHMARK(inverseAffine);
321BENCHMARK(inverseMatrix4d);
322
324 ->RangeMultiplier(10)
325 ->Range(100, 10000)
326 ->Unit(benchmark::kMillisecond);
328 ->RangeMultiplier(10)
329 ->Range(100, 10000)
330 ->Unit(benchmark::kMillisecond);
331BENCHMARK_REGISTER_F(RobotStateBenchmark, update)->RangeMultiplier(10)->Range(10, 10000)->Unit(benchmark::kMillisecond);
332
const std::vector< std::string > & getLinkModelNames() const
Get the names of the links that are part of this joint group.
Representation of a robot's state. This includes position, velocity, acceleration and effort.
Definition robot_state.h:90
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.
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 updateLinkTransforms()
Update the reference frame transforms for links. This call is needed before using the transforms of l...
void setToRandomPositions()
Set all joints to random values. Values will be within default bounds.
void update(bool force=false)
Update all transforms.
void setToDefaultValues()
Set all joints to their default positions. The default position is 0, or if that is not within bounds...
moveit::core::RobotModelPtr loadTestingRobotModel(const std::string &package_name, const std::string &urdf_relative_path, const std::string &srdf_relative_path)
Loads a robot model given a URDF and SRDF file in a package.
BENCHMARK(multiplyAffineTimesMatrixNoAlias)
BENCHMARK_REGISTER_F(RobotStateBenchmark, update) -> RangeMultiplier(10) ->Range(10, 10000) ->Unit(benchmark::kMillisecond)
BENCHMARK_DEFINE_F(RobotStateBenchmark, construct)(benchmark
constexpr char PANDA_TEST_ROBOT[]
constexpr char PANDA_TEST_GROUP[]
std::vector< moveit::core::RobotState > constructStates(size_t num, const moveit::core::RobotState &state)
std::vector< moveit::core::RobotState > constructStates(size_t num)
moveit::core::RobotModelPtr robot_model
void SetUp(const ::benchmark::State &) override
std::vector< size_t > randomPermutation(size_t num)