15 #include <gmock/gmock.h>
16 #include <gtest/gtest.h>
24 #include <range/v3/view/common.hpp>
25 #include <range/v3/view/generate.hpp>
26 #include <range/v3/view/take_exactly.hpp>
39 using Eigen::Vector2d;
46 class OmnidirectionalDriveModelTest :
public ::testing::Test {
49 std::mt19937 generator_{std::random_device()()};
52 TEST_F(OmnidirectionalDriveModelTest, OneUpdate) {
54 const auto control_action = std::make_tuple(
56 const auto state_sampling_function = motion_model_(control_action);
58 ASSERT_THAT(state_sampling_function(pose, generator_),
SE2Near(pose,
kTolerance));
61 TEST_F(OmnidirectionalDriveModelTest, Translate) {
64 const auto state_sampling_function = motion_model_(control_action);
65 const auto result1 = state_sampling_function(
SE2d{
SO2d{0.0},
Vector2d{2.0, 0.0}}, generator_);
67 const auto result2 = state_sampling_function(
SE2d{
SO2d{0.0},
Vector2d{0.0, 3.0}}, generator_);
71 TEST_F(OmnidirectionalDriveModelTest, RotateTranslate) {
73 const auto control_action =
75 const auto state_sampling_function = motion_model_(control_action);
76 const auto result1 = state_sampling_function(
SE2d{
SO2d{0.0},
Vector2d{0.0, 0.0}}, generator_);
78 const auto result2 = state_sampling_function(
SE2d{
SO2d{-Constants::pi() / 2},
Vector2d{2.0, 3.0}}, generator_);
82 TEST_F(OmnidirectionalDriveModelTest, Rotate) {
84 const auto control_action =
86 const auto state_sampling_function = motion_model_(control_action);
87 const auto result1 = state_sampling_function(
SE2d{
SO2d{Constants::pi()},
Vector2d{0.0, 0.0}}, generator_);
89 const auto result2 = state_sampling_function(
SE2d{
SO2d{-Constants::pi() / 2},
Vector2d{0.0, 0.0}}, generator_);
93 TEST_F(OmnidirectionalDriveModelTest, TranslateStrafe) {
96 const auto state_sampling_function = motion_model_(control_action);
97 const auto result1 = state_sampling_function(
SE2d{
SO2d{0.0},
Vector2d{0.0, 0.0}}, generator_);
101 template <
class Range>
102 auto get_statistics(Range&& range) {
103 const auto size =
static_cast<double>(std::distance(std::begin(range), std::end(range)));
104 const double sum = std::accumulate(std::begin(range), std::end(range), 0.0);
105 const double mean = sum / size;
106 const double squared_diff_sum =
107 std::transform_reduce(std::begin(range), std::end(range), 0.0, std::plus<>{}, [mean](
double value) {
108 const double diff = value - mean;
111 const double stddev = std::sqrt(squared_diff_sum / size);
112 return std::pair{mean, stddev};
115 TEST(OmnidirectionalDriveModelSamples, Translate) {
116 const double tolerance = 0.01;
117 const double alpha = 0.2;
118 const double origin = 5.0;
119 const double distance = 3.0;
120 const auto motion_model =
122 auto generator = std::mt19937{std::random_device()()};
123 const auto control_action =
125 const auto state_sampling_function = motion_model(control_action);
126 auto view = ranges::views::generate([&]() {
128 return state_sampling_function(pose, generator).
translation().x();
130 ranges::views::take_exactly(1
'000'000) | ranges::views::common;
131 const auto [mean, stddev] = get_statistics(view);
132 ASSERT_NEAR(mean, origin + distance, tolerance);
133 ASSERT_NEAR(stddev, std::sqrt(alpha * distance * distance), tolerance);
136 TEST(OmnidirectionalDriveModelSamples, RotateFirstQuadrant) {
137 const double tolerance = 0.01;
138 const double alpha = 0.2;
139 const double initial_angle = Constants::pi() / 6;
140 const double motion_angle = Constants::pi() / 4;
142 auto generator = std::mt19937{std::random_device()()};
143 const auto control_action =
145 const auto state_sampling_function = motion_model(control_action);
146 auto view = ranges::views::generate([&]() {
148 return state_sampling_function(pose, generator).
so2().log();
150 ranges::views::take_exactly(100
'000) | ranges::views::common;
151 const auto [mean, stddev] = get_statistics(view);
152 ASSERT_NEAR(mean, initial_angle + motion_angle, tolerance);
153 ASSERT_NEAR(stddev, std::sqrt(alpha * motion_angle * motion_angle), tolerance);
156 TEST(OmnidirectionalDriveModelSamples, RotateThirdQuadrant) {
157 const double tolerance = 0.01;
158 const double alpha = 0.2;
159 const double initial_angle = Constants::pi() / 6;
160 const double motion_angle = -Constants::pi() * 3 / 4;
161 const auto motion_model = UUT{beluga::OmnidirectionalDriveModelParam{alpha, 0.0, 0.0, 0.0, 0.0}};
162 auto generator = std::mt19937{std::random_device()()};
163 const auto control_action =
164 std::make_tuple(SE2d{SO2d{motion_angle}, Vector2d{0.0, 0.0}}, SE2d{SO2d{0.0}, Vector2d{0.0, 0.0}});
165 const auto state_sampling_function = motion_model(control_action);
166 auto view = ranges::views::generate([&]() {
167 const auto pose = SE2d{SO2d{initial_angle}, Vector2d{0.0, 0.0}};
168 return state_sampling_function(pose, generator).so2().log();
170 ranges::views::take_exactly(100'000) | ranges::views::common;
171 const auto [mean, stddev] = get_statistics(view);
172 ASSERT_NEAR(mean, initial_angle + motion_angle, tolerance);
175 const double flipped_angle = Constants::pi() + motion_angle;
176 ASSERT_NEAR(stddev, std::sqrt(alpha * flipped_angle * flipped_angle), tolerance);