moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
test_cost_functions.cpp
Go to the documentation of this file.
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2023, PickNik 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 PickNik Inc. 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
39#include <gtest/gtest.h>
41
42constexpr size_t TIMESTEPS = 100;
43constexpr size_t VARIABLES = 6;
44constexpr double PENALTY = 1.0;
45
46TEST(NoiseGeneratorTest, testGetCostFunctionAllValidStates)
47{
48 // GIVEN a cost function with a state validator that only returns valid costs of 0.0
49 auto state_validator_fn = [](const Eigen::VectorXd& /* state_positions */) { return 0.0; };
50 auto cost_fn =
51 stomp_moveit::costs::getCostFunctionFromStateValidator(state_validator_fn, 0.1 /* interpolation_step_size */);
52
53 // GIVEN a trajectory with TIMESTEPS states, with waypoints interpolating from 0.0 to 1.0 joint values
54 Eigen::MatrixXd values = Eigen::MatrixXd::Zero(VARIABLES, TIMESTEPS);
55 const int last_timestep = values.cols() - 1;
56 for (int timestep = 0; timestep <= last_timestep; ++timestep)
57 {
58 values.col(timestep).fill(static_cast<double>(timestep) / last_timestep);
59 }
60
61 // WHEN the cost function is applied to the trajectory
62 Eigen::VectorXd costs(TIMESTEPS);
63 bool validity;
64 ASSERT_TRUE(cost_fn(values, costs, validity));
65
66 // THEN the trajectory must be valid and have zero costs
67 EXPECT_TRUE(validity);
68 EXPECT_EQ(costs.sum(), 0.0);
69}
70
71TEST(NoiseGeneratorTest, testGetCostFunctionInvalidStates)
72{
73 // GIVEN a cost function with a simulated state validator that tags selected timesteps as invalid.
74 // The state validation function is called once per timestep since interpolation is disabled.
75 // This assumption is confirmed as boundary assumption after calling the solver.
76 static const std::set<int> INVALID_TIMESTEPS(
77 { 0, 10, 11, 12, 25, 26, 27, 46, 63, 64, 65, 66, 67, 68, 69, 97, 98, 99 });
78 size_t timestep_counter = 0;
79 auto state_validator_fn = [&](const Eigen::VectorXd& /* state_positions */) {
80 return PENALTY * INVALID_TIMESTEPS.count(timestep_counter++);
81 };
82 auto cost_fn =
83 stomp_moveit::costs::getCostFunctionFromStateValidator(state_validator_fn, 0.0 /* interpolation disabled */);
84
85 // GIVEN a trajectory with TIMESTEPS states, with waypoints interpolating from 0.0 to 1.0 joint values
86 Eigen::MatrixXd values = Eigen::MatrixXd::Zero(VARIABLES, TIMESTEPS);
87 const int last_timestep = values.cols() - 1;
88 for (int timestep = 0; timestep <= last_timestep; ++timestep)
89 {
90 values.col(timestep).fill(static_cast<double>(timestep) / last_timestep);
91 }
92
93 // WHEN the cost function is applied to the trajectory
94 Eigen::VectorXd costs(TIMESTEPS);
95 bool validity;
96 ASSERT_TRUE(cost_fn(values, costs, validity));
97
98 // THEN the following boundary assumptions about cost function outputs, costs and validity need to be met
99 EXPECT_FALSE(validity); // invalid states must result in an invalid trajectory
100 EXPECT_EQ(timestep_counter, 100u); // 100 timesteps checked without interpolation
101 EXPECT_LE(costs.maxCoeff(), PENALTY); // the highest cost must not be higher than the configured penalty
102 EXPECT_GE(costs.minCoeff(), 0.0); // no negative cost values should be computed
103
104 // THEN the total cost must equal the sum of penalties produced by all invalid timesteps
105 EXPECT_DOUBLE_EQ(costs.sum(), PENALTY * INVALID_TIMESTEPS.size());
106
107 // THEN invalid timesteps must account for the majority of the total cost.
108 // We expect that invalid windows cover at least 2*sigma (=68.1%) of each cost distribution.
109 const std::vector<int> invalid_timesteps_vec(INVALID_TIMESTEPS.begin(), INVALID_TIMESTEPS.end());
110 EXPECT_GE(costs(invalid_timesteps_vec).sum(), 0.681 * PENALTY);
111}
112
113int main(int argc, char** argv)
114{
115 testing::InitGoogleTest(&argc, argv);
116 return RUN_ALL_TESTS();
117}
MoveIt-based cost functions that can be passed to STOMP via a ComposableTask.
CostFn getCostFunctionFromStateValidator(const StateValidatorFn &state_validator_fn, double interpolation_step_size)
constexpr double PENALTY
int main(int argc, char **argv)
constexpr size_t VARIABLES
constexpr size_t TIMESTEPS
TEST(NoiseGeneratorTest, testGetCostFunctionAllValidStates)