2021-12-27 01:28:18 +00:00
|
|
|
#ifndef TESTING_HELPERS_H_
|
|
|
|
#define TESTING_HELPERS_H_
|
|
|
|
|
2022-06-29 15:48:13 +00:00
|
|
|
#include <nav_msgs/msg/occupancy_grid.hpp>
|
|
|
|
#include <geometry_msgs/msg/transform.hpp>
|
|
|
|
#include <geometry_msgs/msg/transform_stamped.hpp>
|
2021-12-27 01:28:18 +00:00
|
|
|
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
|
2022-06-29 15:48:13 +00:00
|
|
|
#include <tf2/LinearMath/Quaternion.h>
|
|
|
|
#include <tf2/LinearMath/Transform.h>
|
|
|
|
#include <tf2/convert.h>
|
2021-12-27 01:28:18 +00:00
|
|
|
#include <opencv2/core/utility.hpp>
|
|
|
|
#include <opencv2/imgcodecs.hpp>
|
|
|
|
#include <random>
|
|
|
|
|
|
|
|
const float resolution = 0.05f;
|
|
|
|
|
2022-06-29 15:48:13 +00:00
|
|
|
nav_msgs::msg::OccupancyGrid::ConstSharedPtr loadMap(const std::string& filename);
|
2021-12-27 01:28:18 +00:00
|
|
|
void saveMap(const std::string& filename,
|
2022-06-29 15:48:13 +00:00
|
|
|
const nav_msgs::msg::OccupancyGrid::ConstSharedPtr& map);
|
2021-12-27 01:28:18 +00:00
|
|
|
std::tuple<double, double, double> randomAngleTxTy();
|
2022-06-29 15:48:13 +00:00
|
|
|
geometry_msgs::msg::Transform randomTransform();
|
2021-12-27 01:28:18 +00:00
|
|
|
cv::Mat randomTransformMatrix();
|
|
|
|
|
|
|
|
/* map_server is really bad. until there is no replacement I will implement it
|
|
|
|
* by myself */
|
|
|
|
template <typename InputIt>
|
2022-06-29 15:48:13 +00:00
|
|
|
std::vector<nav_msgs::msg::OccupancyGrid::ConstSharedPtr> loadMaps(InputIt filenames_begin,
|
2021-12-27 01:28:18 +00:00
|
|
|
InputIt filenames_end)
|
|
|
|
{
|
2022-06-29 15:48:13 +00:00
|
|
|
std::vector<nav_msgs::msg::OccupancyGrid::ConstSharedPtr> result;
|
2021-12-27 01:28:18 +00:00
|
|
|
|
|
|
|
for (InputIt it = filenames_begin; it != filenames_end; ++it) {
|
|
|
|
result.emplace_back(loadMap(*it));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-06-29 15:48:13 +00:00
|
|
|
nav_msgs::msg::OccupancyGrid::ConstSharedPtr loadMap(const std::string& filename)
|
2021-12-27 01:28:18 +00:00
|
|
|
{
|
|
|
|
cv::Mat lookUpTable(1, 256, CV_8S);
|
|
|
|
signed char* p = lookUpTable.ptr<signed char>();
|
|
|
|
p[254] = 0;
|
|
|
|
p[205] = -1;
|
|
|
|
p[0] = 100;
|
|
|
|
|
|
|
|
cv::Mat img = cv::imread(filename, cv::IMREAD_GRAYSCALE);
|
|
|
|
if (img.empty()) {
|
|
|
|
throw std::runtime_error("could not load map");
|
|
|
|
}
|
2022-06-29 15:48:13 +00:00
|
|
|
nav_msgs::msg::OccupancyGrid::SharedPtr grid{new nav_msgs::msg::OccupancyGrid()};
|
2021-12-27 01:28:18 +00:00
|
|
|
grid->info.width = static_cast<uint>(img.size().width);
|
|
|
|
grid->info.height = static_cast<uint>(img.size().height);
|
|
|
|
grid->info.resolution = resolution;
|
|
|
|
grid->data.resize(static_cast<size_t>(img.size().area()));
|
|
|
|
cv::Mat grid_view(img.size(), CV_8S,
|
|
|
|
const_cast<signed char*>(grid->data.data()));
|
|
|
|
cv::LUT(img, lookUpTable, grid_view);
|
|
|
|
|
|
|
|
return grid;
|
|
|
|
}
|
|
|
|
|
|
|
|
void saveMap(const std::string& filename,
|
2022-06-29 15:48:13 +00:00
|
|
|
const nav_msgs::msg::OccupancyGrid::ConstSharedPtr& map)
|
2021-12-27 01:28:18 +00:00
|
|
|
{
|
|
|
|
cv::Mat lookUpTable(1, 256, CV_8U);
|
|
|
|
uchar* p = lookUpTable.ptr();
|
|
|
|
for (int i = 0; i < 255; ++i) {
|
|
|
|
if (i >= 0 && i < 10)
|
|
|
|
p[i] = 254;
|
|
|
|
else
|
|
|
|
p[i] = 0;
|
|
|
|
}
|
|
|
|
p[255] = 205;
|
|
|
|
|
|
|
|
cv::Mat img(map->info.height, map->info.width, CV_8S,
|
|
|
|
const_cast<signed char*>(map->data.data()));
|
|
|
|
cv::Mat out_img;
|
|
|
|
cv::LUT(img, lookUpTable, out_img);
|
|
|
|
cv::imwrite(filename, out_img);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::tuple<double, double, double> randomAngleTxTy()
|
|
|
|
{
|
|
|
|
static std::mt19937_64 g(156468754 /*magic*/);
|
|
|
|
std::uniform_real_distribution<double> rotation_dis(0., 2 * std::acos(-1));
|
|
|
|
std::uniform_real_distribution<double> translation_dis(-1000, 1000);
|
|
|
|
|
|
|
|
return std::tuple<double, double, double>(rotation_dis(g), translation_dis(g),
|
|
|
|
translation_dis(g));
|
|
|
|
}
|
|
|
|
|
2022-06-29 15:48:13 +00:00
|
|
|
geometry_msgs::msg::Transform randomTransform()
|
2021-12-27 01:28:18 +00:00
|
|
|
{
|
|
|
|
double angle, tx, ty;
|
|
|
|
std::tie(angle, tx, ty) = randomAngleTxTy();
|
|
|
|
tf2::Transform transform;
|
|
|
|
tf2::Quaternion rotation;
|
|
|
|
rotation.setEuler(0., 0., angle);
|
|
|
|
rotation.normalize();
|
|
|
|
transform.setRotation(rotation);
|
|
|
|
transform.setOrigin(tf2::Vector3(tx, ty, 0.));
|
|
|
|
|
|
|
|
auto msg = toMsg(transform);
|
|
|
|
// normalize quaternion such that w > 0 (q and -q represents the same
|
|
|
|
// transformation)
|
|
|
|
if (msg.rotation.w < 0.) {
|
|
|
|
msg.rotation.x *= -1.;
|
|
|
|
msg.rotation.y *= -1.;
|
|
|
|
msg.rotation.z *= -1.;
|
|
|
|
msg.rotation.w *= -1.;
|
|
|
|
}
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
cv::Mat randomTransformMatrix()
|
|
|
|
{
|
|
|
|
double angle, tx, ty;
|
|
|
|
std::tie(angle, tx, ty) = randomAngleTxTy();
|
|
|
|
cv::Mat transform =
|
|
|
|
(cv::Mat_<double>(3, 3) << std::cos(angle), -std::sin(angle), tx,
|
|
|
|
std::sin(angle), std::cos(angle), ty, 0., 0., 1.);
|
|
|
|
|
|
|
|
return transform;
|
|
|
|
}
|
|
|
|
|
2022-06-29 15:48:13 +00:00
|
|
|
static inline bool isIdentity(const geometry_msgs::msg::Transform& transform)
|
2021-12-27 01:28:18 +00:00
|
|
|
{
|
|
|
|
tf2::Transform t;
|
|
|
|
tf2::fromMsg(transform, t);
|
|
|
|
return tf2::Transform::getIdentity() == t;
|
|
|
|
}
|
|
|
|
|
2022-06-29 15:48:13 +00:00
|
|
|
static inline bool isIdentity(const geometry_msgs::msg::Quaternion& rotation)
|
2021-12-27 01:28:18 +00:00
|
|
|
{
|
|
|
|
tf2::Quaternion q;
|
|
|
|
tf2::fromMsg(rotation, q);
|
|
|
|
return tf2::Quaternion::getIdentity() == q;
|
|
|
|
}
|
|
|
|
|
|
|
|
// data size is consistent with height and width
|
2022-06-29 15:48:13 +00:00
|
|
|
static inline bool consistentData(const nav_msgs::msg::OccupancyGrid& grid)
|
2021-12-27 01:28:18 +00:00
|
|
|
{
|
|
|
|
return grid.info.width * grid.info.height == grid.data.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ignores header, map_load_time and origin
|
2022-06-29 15:48:13 +00:00
|
|
|
// static inline bool operator==(const nav_msgs::msg::OccupancyGrid::SharedPtr grid1,
|
|
|
|
// const nav_msgs::msg::OccupancyGrid::SharedPtr grid2)
|
|
|
|
// {
|
|
|
|
// bool equal = true;
|
|
|
|
// equal &= grid1->info.width == grid2->info.width;
|
|
|
|
// equal &= grid1->info.height == grid2->info.height;
|
|
|
|
// equal &= std::abs(grid1->info.resolution - grid2->info.resolution) <
|
|
|
|
// std::numeric_limits<float>::epsilon();
|
|
|
|
// equal &= grid1->data.size() == grid2->data.size();
|
|
|
|
// for (size_t i = 0; i < grid1->data.size(); ++i) {
|
|
|
|
// equal &= grid1->data[i] == grid2->data[i];
|
|
|
|
// }
|
|
|
|
// return equal;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// ignores header, map_load_time and origin
|
|
|
|
static inline bool maps_equal(const nav_msgs::msg::OccupancyGrid& grid1,
|
|
|
|
const nav_msgs::msg::OccupancyGrid& grid2)
|
2021-12-27 01:28:18 +00:00
|
|
|
{
|
2022-06-29 15:48:13 +00:00
|
|
|
// std::cout << "asdasdadsdth: " << std::endl;
|
2021-12-27 01:28:18 +00:00
|
|
|
bool equal = true;
|
|
|
|
equal &= grid1.info.width == grid2.info.width;
|
|
|
|
equal &= grid1.info.height == grid2.info.height;
|
|
|
|
equal &= std::abs(grid1.info.resolution - grid2.info.resolution) <
|
|
|
|
std::numeric_limits<float>::epsilon();
|
|
|
|
equal &= grid1.data.size() == grid2.data.size();
|
|
|
|
for (size_t i = 0; i < grid1.data.size(); ++i) {
|
|
|
|
equal &= grid1.data[i] == grid2.data[i];
|
|
|
|
}
|
|
|
|
return equal;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // TESTING_HELPERS_H_
|