36 #include <geometry_msgs/PoseArray.h> 37 #include <mcl_3dl_msgs/Status.h> 38 #include <nav_msgs/Odometry.h> 39 #include <sensor_msgs/Imu.h> 40 #include <sensor_msgs/PointCloud2.h> 42 #include <std_srvs/Trigger.h> 46 #include <gtest/gtest.h> 50 void generateSamplePointcloud2(
51 sensor_msgs::PointCloud2& cloud,
59 const float offset_yaw)
61 std::random_device seed;
62 std::default_random_engine engine(seed());
63 std::normal_distribution<float> rand(0, 0.01);
66 cloud.is_bigendian =
false;
67 cloud.is_dense =
false;
69 modifier.setPointCloud2Fields(
71 "x", 1, sensor_msgs::PointField::FLOAT32,
72 "y", 1, sensor_msgs::PointField::FLOAT32,
73 "z", 1, sensor_msgs::PointField::FLOAT32,
74 "intensity", 1, sensor_msgs::PointField::FLOAT32);
80 Point(
const float x,
const float y,
const float z)
87 std::vector<Point> points;
88 const float grid_xy = 0.15;
89 const float grid_z = 0.1;
90 const float floor_size = 6.0;
92 for (
float x = -floor_size;
x < floor_size;
x += grid_xy)
93 for (
float y = -floor_size;
y < floor_size;
y += grid_xy)
94 if (x0 <
x &&
x < x1 && y0 <
y &&
y < y1)
95 points.push_back(
Point(
x,
y, 0.0));
97 for (
float x = -2;
x < 1.6;
x += grid_xy)
99 for (
float z = 0.5;
z < 1.5;
z += grid_z)
100 points.push_back(
Point(
x, 1.6,
z));
101 for (
float y = -0.5;
y < 1.6;
y += grid_xy)
102 if (y0 <
y &&
y < y1)
103 for (
float z = 0.5;
z < 2.0;
z += grid_z)
104 points.push_back(
Point(1.6,
y,
z));
105 for (
float x = 0;
x < 1.6;
x += grid_xy)
106 if (x0 <
x &&
x < x1)
107 for (
float z = 0.5;
z < 2.0;
z += grid_z)
108 points.push_back(
Point(
x, -2.1 +
x,
z));
110 const float o_cos = cosf(offset_yaw);
111 const float o_sin = sinf(offset_yaw);
112 modifier.resize(points.size());
113 cloud.width = points.size();
118 for (
const Point& p : points)
120 *iter_x = p.x_ * o_cos - p.y_ * o_sin + offset_x + rand(engine);
121 *iter_y = p.x_ * o_sin + p.y_ * o_cos + offset_y + rand(engine);
122 *iter_z = p.z_ + offset_z + rand(engine);
129 inline sensor_msgs::PointCloud2 generateMapMsg()
131 sensor_msgs::PointCloud2 cloud;
132 generateSamplePointcloud2(cloud, -100, -100, 100, 100, 0, 0, 0, 0);
133 cloud.header.frame_id =
"map";
136 inline sensor_msgs::PointCloud2 generateCloudMsg(
137 const float offset_x,
138 const float offset_y,
139 const float offset_z,
140 const float offset_yaw)
142 sensor_msgs::PointCloud2 cloud;
143 generateSamplePointcloud2(
145 offset_x, offset_y, offset_z, offset_yaw);
146 cloud.header.frame_id =
"laser";
150 inline sensor_msgs::Imu generateImuMsg()
152 sensor_msgs::Imu imu;
153 imu.header.frame_id =
"base_link";
155 imu.orientation.w = 1;
156 imu.linear_acceleration.z = 9.8;
159 inline nav_msgs::Odometry generateOdomMsg(
const float x)
161 nav_msgs::Odometry odom;
162 odom.header.frame_id =
"odom";
164 odom.pose.pose.position.x =
x;
165 odom.pose.pose.position.y = 5;
166 odom.pose.pose.orientation.w = 1;
177 ::testing::Values(5.0, 100.0));
181 geometry_msgs::PoseArray::ConstPtr poses;
182 mcl_3dl_msgs::Status::ConstPtr status;
184 const boost::function<void(const geometry_msgs::PoseArray::ConstPtr&)> cb_pose =
185 [&poses](
const geometry_msgs::PoseArray::ConstPtr&
msg) ->
void 189 const boost::function<void(const mcl_3dl_msgs::Status::ConstPtr&)> cb_status =
190 [&status](
const mcl_3dl_msgs::Status::ConstPtr&
msg) ->
void 200 nh.
serviceClient<std_srvs::TriggerRequest, std_srvs::TriggerResponse>(
201 "global_localization");
208 pub_mapcloud.
publish(generateMapMsg());
210 for (
int i = 0; i < 100; i++)
219 for (
float offset_x = -0.5; offset_x <= 0.51; offset_x += 1.0)
221 for (
float offset_yaw = -M_PI / 2; offset_yaw <= M_PI / 2 + 0.1; offset_yaw += M_PI)
223 const float laser_frame_height = 0.5;
224 const float offset_y = 0.54;
225 const float offset_z = 0.0;
233 if (status && status->status == mcl_3dl_msgs::Status::NORMAL)
236 generateCloudMsg(offset_x, offset_y, offset_z - laser_frame_height, offset_yaw));
237 pub_imu.publish(generateImuMsg());
238 pub_odom.publish(generateOdomMsg(0.0));
241 for (
int i = 0; i < 5; ++i)
244 pub_odom.publish(generateOdomMsg(GetParam()));
249 std_srvs::Trigger trigger;
250 ASSERT_TRUE(src_global_localization.
call(trigger));
258 if (status && status->status == mcl_3dl_msgs::Status::GLOBAL_LOCALIZATION)
261 generateCloudMsg(offset_x, offset_y, offset_z - laser_frame_height, offset_yaw));
262 pub_imu.publish(generateImuMsg());
263 pub_odom.publish(generateOdomMsg(GetParam()));
272 if (status && status->status == mcl_3dl_msgs::Status::NORMAL)
275 generateCloudMsg(offset_x, offset_y, offset_z - laser_frame_height, offset_yaw));
276 pub_imu.publish(generateImuMsg());
277 pub_odom.publish(generateOdomMsg(GetParam()));
282 for (
int i = 0; i < 40; ++i)
287 generateCloudMsg(offset_x, offset_y, offset_z - laser_frame_height, offset_yaw));
288 pub_imu.publish(generateImuMsg());
289 pub_odom.publish(generateOdomMsg(GetParam()));
293 ASSERT_TRUE(static_cast<bool>(status));
294 ASSERT_TRUE(static_cast<bool>(poses));
299 -(offset_x *
cos(-offset_yaw) - offset_y *
sin(-offset_yaw)),
300 -(offset_x *
sin(-offset_yaw) + offset_y *
cos(-offset_yaw)),
302 bool found_true_positive(
false);
303 float dist_err_min = std::numeric_limits<float>::max();
304 float ang_err_min = std::numeric_limits<float>::max();
305 for (
const auto& pose : poses->poses)
311 const float dist_err = tf_diff.
getOrigin().length();
313 if (dist_err < 2e-1 && ang_err < 2e-1)
314 found_true_positive =
true;
316 if (dist_err_min > dist_err)
318 dist_err_min = dist_err;
319 ang_err_min = ang_err;
322 ASSERT_TRUE(found_true_positive)
323 <<
"Minimum position error: " << dist_err_min << std::endl
324 <<
"Angular error: " << ang_err_min << std::endl;
329 int main(
int argc,
char** argv)
331 testing::InitGoogleTest(&argc, argv);
332 ros::init(argc, argv,
"test_global_localization");
335 return RUN_ALL_TESTS();
ServiceClient serviceClient(const std::string &service_name, bool persistent=false, const M_string &header_values=M_string())
void publish(const boost::shared_ptr< M > &message) const
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
bool call(MReq &req, MRes &res)
TFSIMD_FORCE_INLINE const tfScalar & x() const
int main(int argc, char **argv)
TFSIMD_FORCE_INLINE const tfScalar & z() const
void fromMsg(const A &, B &b)
TFSIMD_FORCE_INLINE const tfScalar & y() const
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
double getYaw(const A &a)
INSTANTIATE_TEST_CASE_P(OdometryOffset, GlobalLocalization,::testing::Values(5.0, 100.0))
TEST_P(GlobalLocalization, Localize)
INLINE Rall1d< T, V, S > cos(const Rall1d< T, V, S > &arg)
ROSCPP_DECL void spinOnce()
INLINE Rall1d< T, V, S > sin(const Rall1d< T, V, S > &arg)