31 #include "glog/logging.h" 32 #include "gmock/gmock.h" 38 constexpr
char kSensorId[] =
"sensor_id";
40 class LocalTrajectoryBuilderTest :
public ::testing::Test {
42 struct TrajectoryNode {
47 void SetUp()
override { GenerateBubbles(); }
49 mapping::proto::LocalTrajectoryBuilderOptions3D
50 CreateTrajectoryBuilderOptions3D() {
55 num_accumulated_range_data = 1, 56 voxel_filter_size = 0.2, 58 high_resolution_adaptive_voxel_filter = { 64 low_resolution_adaptive_voxel_filter = { 70 use_online_correlative_scan_matching = false, 71 real_time_correlative_scan_matcher = { 72 linear_search_window = 0.2, 73 angular_search_window = math.rad(1.), 74 translation_delta_cost_weight = 1e-1, 75 rotation_delta_cost_weight = 1., 78 ceres_scan_matcher = { 79 occupied_space_weight_0 = 5., 80 occupied_space_weight_1 = 20., 81 translation_weight = 0.1, 82 rotation_weight = 0.3, 83 only_optimize_yaw = false, 84 ceres_solver_options = { 85 use_nonmonotonic_steps = true, 86 max_num_iterations = 20, 92 max_time_seconds = 0.2, 93 max_distance_meters = 0.02, 94 max_angle_radians = 0.001, 97 imu_gravity_time_constant = 1., 98 rotational_histogram_size = 120, 101 high_resolution = 0.2, 102 high_resolution_max_range = 50., 103 low_resolution = 0.5, 104 num_range_data = 45000, 105 range_data_inserter = { 106 hit_probability = 0.7, 107 miss_probability = 0.4, 108 num_free_space_voxels = 0, 114 parameter_dictionary.get());
117 void GenerateBubbles() {
118 std::mt19937 prng(42);
119 std::uniform_real_distribution<float> distribution(-1.f, 1.f);
121 for (
int i = 0; i != 100; ++i) {
122 const float x = distribution(prng);
123 const float y = distribution(prng);
124 const float z = distribution(prng);
125 bubbles_.push_back(10. * Eigen::Vector3f(x, y, z).normalized());
132 Eigen::Vector3f CollideWithBox(
const Eigen::Vector3f& from,
133 const Eigen::Vector3f& to) {
135 if (to.x() > from.x()) {
136 first = std::min(first, (15.f - from.x()) / (to.x() - from.x()));
137 }
else if (to.x() < from.x()) {
138 first = std::min(first, (-15.f - from.x()) / (to.x() - from.x()));
140 if (to.y() > from.y()) {
141 first = std::min(first, (15.f - from.y()) / (to.y() - from.y()));
142 }
else if (to.y() < from.y()) {
143 first = std::min(first, (-15.f - from.y()) / (to.y() - from.y()));
145 if (to.z() > from.z()) {
146 first = std::min(first, (15.f - from.z()) / (to.z() - from.z()));
147 }
else if (to.z() < from.z()) {
148 first = std::min(first, (-15.f - from.z()) / (to.z() - from.z()));
150 return first * (to - from) + from;
155 Eigen::Vector3f CollideWithBubbles(
const Eigen::Vector3f& from,
156 const Eigen::Vector3f& to) {
158 constexpr
float kBubbleRadius = 0.5f;
159 for (
const Eigen::Vector3f& center :
bubbles_) {
160 const float a = (to - from).squaredNorm();
161 const float beta = (to - from).dot(from - center);
163 (from - center).squaredNorm() - kBubbleRadius * kBubbleRadius;
164 const float discriminant = beta * beta - a * c;
165 if (discriminant < 0.f) {
168 const float solution = (-beta - std::sqrt(discriminant)) / a;
169 if (solution < 0.f) {
172 first = std::min(first, solution);
174 return first * (to - from) + from;
180 for (
int r = -8; r != 8; ++r) {
181 for (
int s = -250; s != 250; ++s) {
182 Eigen::Vector4f first_point;
183 first_point << Eigen::AngleAxisf(M_PI * s / 250.,
184 Eigen::Vector3f::UnitZ()) *
185 Eigen::AngleAxisf(M_PI / 12. * r / 8.,
186 Eigen::Vector3f::UnitY()) *
187 Eigen::Vector3f::UnitX(),
189 directions_in_rangefinder_frame.push_back(first_point);
191 Eigen::Vector4f second_point;
192 second_point << Eigen::AngleAxisf(M_PI / 2., Eigen::Vector3f::UnitX()) *
193 Eigen::AngleAxisf(M_PI * s / 250.,
194 Eigen::Vector3f::UnitZ()) *
195 Eigen::AngleAxisf(M_PI / 12. * r / 8.,
196 Eigen::Vector3f::UnitY()) *
197 Eigen::Vector3f::UnitX(),
199 directions_in_rangefinder_frame.push_back(second_point);
205 for (
const Eigen::Vector4f& direction_in_world_frame :
207 pose.cast<
float>())) {
208 const Eigen::Vector3f origin =
209 pose.cast<
float>() * Eigen::Vector3f::Zero();
210 Eigen::Vector4f return_point;
211 return_point << CollideWithBubbles(
212 origin, CollideWithBox(origin, direction_in_world_frame.head<3>())),
214 returns_in_world_frame.push_back(return_point);
216 return {Eigen::Vector3f::Zero(),
218 pose.inverse().cast<
float>()),
224 const Eigen::Vector3d gravity =
225 expected_pose.rotation().inverse() * Eigen::Vector3d(0., 0., 9.81);
227 sensor::ImuData{
time, gravity, Eigen::Vector3d::Zero()});
230 std::vector<TrajectoryNode> GenerateCorkscrewTrajectory() {
231 std::vector<TrajectoryNode> trajectory;
234 for (
int i = 0; i != 5; ++i) {
236 trajectory.push_back(
240 for (
double t = 0.; t <= 0.6; t += 0.05) {
242 trajectory.push_back(TrajectoryNode{
245 std::sin(4. * t), 1. - std::cos(4. * t), 1. * t)) *
247 0.3 * t, Eigen::Vector3d(1., -1., 2.).normalized()))});
252 void VerifyAccuracy(
const std::vector<TrajectoryNode>& expected_trajectory,
253 double expected_accuracy) {
255 for (
const TrajectoryNode& node : expected_trajectory) {
256 AddLinearOnlyImuObservation(node.time, node.pose);
257 const auto range_data = GenerateRangeData(node.pose);
258 const std::unique_ptr<LocalTrajectoryBuilder3D::MatchingResult>
260 kSensorId, sensor::TimedPointCloudData{
261 node.time, range_data.origin, range_data.returns});
262 if (matching_result !=
nullptr) {
263 EXPECT_THAT(matching_result->local_pose,
264 transform::IsNearly(node.pose, 1e-1));
266 LOG(INFO) <<
"num_poses: " << num_poses;
275 TEST_F(LocalTrajectoryBuilderTest, MoveInsideCubeUsingOnlyCeresScanMatcher) {
277 CreateTrajectoryBuilderOptions3D(), {kSensorId}));
278 VerifyAccuracy(GenerateCorkscrewTrajectory(), 1e-1);
TimedPointCloud TransformTimedPointCloud(const TimedPointCloud &point_cloud, const transform::Rigid3f &transform)
std::unique_ptr< LuaParameterDictionary > MakeDictionary(const std::string &code)
UniversalTimeScaleClock::time_point Time
Time FromUniversal(const int64 ticks)
Duration FromSeconds(const double seconds)
std::vector< Eigen::Vector4f > TimedPointCloud
std::vector< Eigen::Vector3f > bubbles_
std::unique_ptr< LocalTrajectoryBuilder3D > local_trajectory_builder_
proto::LocalTrajectoryBuilderOptions3D CreateLocalTrajectoryBuilderOptions3D(common::LuaParameterDictionary *parameter_dictionary)