9#include <boost/geometry/algorithms/buffer.hpp>
10#include <boost/geometry/algorithms/convex_hull.hpp>
11#include <boost/geometry/algorithms/distance.hpp>
12#include <boost/geometry/algorithms/simplify.hpp>
13#include <boost/geometry/algorithms/transform.hpp>
14#include <boost/geometry/algorithms/union.hpp>
15#include <boost/geometry/core/cs.hpp>
16#include <boost/geometry/geometries/multi_linestring.hpp>
17#include <boost/geometry/geometries/multi_point.hpp>
18#include <boost/geometry/geometries/multi_polygon.hpp>
19#include <boost/geometry/io/wkt/write.hpp>
20#include <boost/geometry/strategies/strategies.hpp>
21#include <boost/geometry/strategies/transform/matrix_transformers.hpp>
22#include <boost/pending/disjoint_sets.hpp>
24#include <Eigen/Geometry>
26#include <VirtualRobot/CollisionDetection/CollisionChecker.h>
27#include <VirtualRobot/CollisionDetection/CollisionModel.h>
28#include <VirtualRobot/Nodes/RobotNode.h>
29#include <VirtualRobot/Robot.h>
30#include <VirtualRobot/RobotFactory.h>
31#include <VirtualRobot/SceneObjectSet.h>
32#include <VirtualRobot/VirtualRobot.h>
33#include <VirtualRobot/Visualization/TriMeshModel.h>
38#include <ArmarXCore/interface/serialization/Eigen/Eigen_fdi.h>
45#include <range/v3/range/conversion.hpp>
46#include <range/v3/view/all.hpp>
47#include <range/v3/view/filter.hpp>
48#include <range/v3/view/transform.hpp>
67 robot->clone(
"collision_robot_" + std::to_string(omp_get_thread_num()));
73 ARMARX_INFO <<
"Setting primitive approximation IDs: "
76 distanceRobot.
robot->setPrimitiveApproximationModel(
82 distanceRobot.
robot->setUpdateVisualization(
true);
95 const auto collisionRobotNode =
99 distanceRobot.
collisionModels = {collisionRobotNode->getCollisionModel()};
105 ARMARX_INFO <<
"Using reduced robot (merging all collision models) ...";
107 std::vector<std::string> otherNodeNames{distanceRobot.
robot->getRootNode()->getName()};
110 otherNodeNames.push_back(params.
rootNode);
125 <<
"Collision model not available. "
126 "Make sure that you load the robot correctly!";
130 for (
auto& colModel : distanceRobot.
robot->getCollisionModels())
135 return distanceRobot;
146 return robot.robot !=
nullptr && not robot.collisionModels.empty();
172 robot.robot->setGlobalPose(globalPose.matrix());
177 float distanceNonArticulated = std::numeric_limits<float>::max();
178 if (scene.staticObjects->getSize() > 0)
180 distanceNonArticulated =
181 VirtualRobot::CollisionChecker::getGlobalCollisionChecker()->calculateDistance(
182 robot.collisionModels.front(),
183 scene.staticObjects);
187 float distanceArticulatedMin = std::numeric_limits<float>::max();
188 for (
const auto& articulatedObject : scene.articulatedObjects)
190 for (
const auto& colModel : articulatedObject->getCollisionModels())
192 const float distanceArticulated =
193 VirtualRobot::CollisionChecker::getGlobalCollisionChecker()->calculateDistance(
194 robot.collisionModels.front(),
196 distanceArticulatedMin = std::min(distanceArticulated, distanceArticulatedMin);
200 return std::min(distanceNonArticulated, distanceArticulatedMin);
207 collPolyFromVertices(
const std::vector<Eigen::Vector3f>& vertices,
210 const auto verticesConverted =
212 ranges::views::transform([](
const Eigen::Vector3f& vec)
215 boost::geometry::model::multi_point<util::geometry::point_type> mp(
216 verticesConverted.begin(),
217 verticesConverted.end());
218 boost::geometry::convex_hull(mp, out);
221 template <
typename Polygon>
222 boost::geometry::model::multi_polygon<Polygon>
223 buffer_polygon_simple(
const Polygon& poly,
double distance)
225 namespace bg = boost::geometry;
226 using Multi = bg::model::multi_polygon<Polygon>;
228 using namespace bg::strategy::buffer;
229 distance_symmetric<double> distance_strategy(
distance);
230 side_straight side_strategy;
231 join_miter join_strategy;
232 end_flat end_strategy;
233 point_circle circle_strategy(8);
246 std::vector<util::geometry::polygon_type>
247 collPolyFrom3D(
const std::vector<Eigen::Vector3f>& vertices,
248 const std::vector<VirtualRobot::MathTools::TriangleFace>& faces)
257 using point2d = boost::geometry::model::d2::point_xy<double>;
258 using polygon2d = boost::geometry::model::polygon<point2d>;
259 using multipolygon2d = boost::geometry::model::multi_polygon<polygon2d>;
261 multipolygon2d projected;
262 for (
const auto& f : faces)
265 boost::geometry::append(p,
point2d(vertices[f.id1].x(), vertices[f.id1].y()));
266 boost::geometry::append(p,
point2d(vertices[f.id2].x(), vertices[f.id2].y()));
267 boost::geometry::append(p,
point2d(vertices[f.id3].x(), vertices[f.id3].y()));
268 boost::geometry::append(p,
point2d(vertices[f.id1].x(), vertices[f.id1].y()));
269 projected.push_back(p);
273 multipolygon2d unified = {projected.front()};
275 for (
size_t i = 1; i < projected.size(); ++i)
278 polygon2d next = projected[i];
279 boost::geometry::correct(next);
282 multipolygon2d next_buffered = buffer_polygon_simple(next, 0.01);
283 if (not boost::geometry::is_valid(next_buffered))
286 << boost::geometry::wkt(next);
289 boost::geometry::union_(unified, next_buffered, tmp);
290 unified = std::move(tmp);
297 std::vector<util::geometry::polygon_type> out;
298 for (
const auto& p : unified)
301 for (
const auto& point : p.outer())
303 boost::geometry::append(
306 static_cast<float>(point.y())});
309 ARMARX_INFO <<
" Polygon: " << boost::geometry::wkt(poly);
317 const VirtualRobot::CollisionModelPtr& robotCollisionModel)
321 collisionRobot->setUpdateCollisionModel(
true);
322 collisionRobot->setUpdateVisualization(
true);
324 collisionRobot->setGlobalPose(globalPose.matrix());
328 const std::vector<Eigen::Vector3f> vertices = robotCollisionModel->getModelVeticesGlobal();
330 collPolyFromVertices(vertices, robot_polygon);
331 ARMARX_INFO <<
"Robot: " << boost::geometry::wkt(robot_polygon);
335 const std::vector<VirtualRobot::CollisionModelPtr>& robotCollisionModels)
339 collisionRobot->setUpdateCollisionModel(
true);
340 collisionRobot->setUpdateVisualization(
true);
342 collisionRobot->setGlobalPose(globalPose.matrix());
346 std::vector<Eigen::Vector3f> vertices;
347 for (
const auto& robotCollisionModel : robotCollisionModels)
349 const auto v = robotCollisionModel->getModelVeticesGlobal();
350 vertices.insert(vertices.end(), v.begin(), v.end());
353 collPolyFromVertices(vertices, robot_polygon);
354 ARMARX_INFO <<
"Robot: " << boost::geometry::wkt(robot_polygon);
360 boost::geometry::strategy::transform::
361 rotate_transformer<boost::geometry::radian, float, 2, 2>
364 boost::geometry::transform(robot_polygon, out,
transform);
371 boost::geometry::strategy::transform::translate_transformer<float, 2, 2> translate(
372 pose.translation().x(),
373 pose.translation().y());
375 -Eigen::Rotation2Df{pose.linear()}
377 boost::geometry::strategy::transform::
378 rotate_transformer<boost::geometry::radian, float, 2, 2>
381 boost::geometry::transform(robot_polygon, out, rotate);
383 boost::geometry::transform(out, out2, translate);
388 float ignoreVerticesOver)
390 initializeFromCollisionModels(collisionModels, ignoreVerticesOver);
395 std::vector<VirtualRobot::CollisionModelPtr> collisionModels;
396 const auto& sceneObjectColModels = scene.
staticObjects->getCollisionModels();
397 collisionModels.insert(collisionModels.end(),
398 sceneObjectColModels.begin(),
399 sceneObjectColModels.end());
402 const auto colModels = articulatedObject->getCollisionModels();
403 collisionModels.insert(collisionModels.end(), colModels.begin(), colModels.end());
406 initializeFromCollisionModels(collisionModels, ignoreVerticesOver);
412 double minDistance = std::numeric_limits<float>::max();
413 for (
const auto& obs : obstacles)
415 const auto distance = boost::geometry::distance(obs, robot);
416 minDistance = std::min(
distance, minDistance);
418 return static_cast<float>(minDistance);
421 std::vector<CollisionPolygon>&
424 return this->obstacles;
430 std::vector<std::vector<Eigen::Vector2f>>
431 getCollModelClusters(VirtualRobot::CollisionModelPtr collModel,
float ignoreVerticesOver)
433 std::vector<std::vector<Eigen::Vector2f>> clusters;
435 auto allVertices = collModel->getModelVeticesGlobal();
436 auto allFaces = collModel->getTriMeshModel()->faces;
439 auto facesFiltered = allFaces |
440 ranges::views::filter(
441 [ignoreVerticesOver, &allVertices](
const auto& f)
443 return allVertices[f.id1].z() < ignoreVerticesOver &&
444 allVertices[f.id2].z() < ignoreVerticesOver &&
445 allVertices[f.id3].z() < ignoreVerticesOver;
451 std::vector<int> rank;
452 std::vector<int> parent;
453 rank.reserve(allVertices.size());
454 parent.reserve(allVertices.size());
455 boost::disjoint_sets<int*, int*> ds(rank.data(), parent.data());
457 for (std::size_t i = 0; i < allVertices.size(); i++)
460 parent.push_back(
static_cast<int>(i));
461 ds.make_set(
static_cast<int>(i));
463 for (
const auto& face : facesFiltered)
465 ds.union_set(face.id1, face.id2);
466 ds.union_set(face.id2, face.id3);
467 ds.union_set(face.id1, face.id3);
470 for (std::size_t i = 0; i < allVertices.size(); i++)
472 for (std::size_t j = i + 1; j < allVertices.size(); j++)
474 if (allVertices[i].z() >= ignoreVerticesOver ||
475 allVertices[j].z() >= ignoreVerticesOver)
479 const float dist = (allVertices[i] - allVertices[j]).squaredNorm();
482 ds.union_set(
static_cast<int>(i),
static_cast<int>(j));
487 std::map<int, std::vector<Eigen::Vector2f>> clustersMap;
488 for (std::size_t i = 0; i < allVertices.size(); i++)
490 if (allVertices[i].z() < ignoreVerticesOver)
492 const int setId = ds.find_set(
static_cast<int>(i));
493 clustersMap[setId].emplace_back(allVertices[i].x(), allVertices[i].y());
496 for (
const auto& [setId, vertices] : clustersMap)
498 clusters.push_back(vertices);
503 std::vector<util::geometry::polygon_type>
504 mergeOverlappingPolygons(std::vector<util::geometry::polygon_type>& obstacles)
506 std::vector<util::geometry::polygon_type> mergedObstacles;
507 std::vector<bool> merged(obstacles.size(),
false);
508 for (std::size_t i = 0; i < obstacles.size(); i++)
516 bool mergedSomething =
true;
517 while (mergedSomething)
519 mergedSomething =
false;
520 for (std::size_t j = 0; j < obstacles.size(); j++)
522 if (i == j || merged[j])
526 if (boost::geometry::intersects(currentPoly, obstacles[j]) ||
527 boost::geometry::touches(currentPoly, obstacles[j]) ||
528 boost::geometry::within(currentPoly, obstacles[j]) ||
529 boost::geometry::within(obstacles[j], currentPoly))
531 std::vector<util::geometry::polygon_type> unionPoly;
532 boost::geometry::union_(currentPoly, obstacles[j], unionPoly);
533 if (unionPoly.size() > 1)
536 <<
"Merging two polygons resulted in multiple polygons. "
537 "This should not happen. Keeping the first polygon.";
539 else if (unionPoly.empty())
542 "polygon. This should "
543 "not happen. Keeping the original polygon.";
546 currentPoly = unionPoly[0];
548 mergedSomething =
true;
552 mergedObstacles.push_back(currentPoly);
554 return mergedObstacles;
559 Scene2D::initializeFromCollisionModels(
560 std::vector<VirtualRobot::CollisionModelPtr>& collisionModels,
561 float ignoreVerticesOver)
564 bool useFaces =
true;
565 for (
const auto& collModel : collisionModels)
567 if (ignoreVerticesOver >= 0)
569 const auto clusters = getCollModelClusters(collModel, ignoreVerticesOver);
570 for (
const auto& cluster : clusters)
572 if (cluster.size() >= 3)
581 ARMARX_INFO <<
"Using only vertices for collision model";
582 auto& collPoly = obstacles.emplace_back();
583 collPolyFromVertices(
585 ranges::views::transform(
586 [](
const Eigen::Vector2f& v)
587 {
return Eigen::Vector3f{
v.x(),
v.y(), 0.0f}; }) |
594 ARMARX_INFO <<
"Ignoring cluster with less than 3 vertices";
600 const auto vertices = collModel->getModelVeticesGlobal();
604 const auto triMeshModel = collModel->getTriMeshModel();
605 const auto faces = triMeshModel->faces;
606 const auto polygons = collPolyFrom3D(vertices, faces);
607 ARMARX_INFO << faces.size() <<
" faces in collision model";
608 ARMARX_INFO <<
"Found " << polygons.size() <<
" polygons in collision model";
609 obstacles.insert(obstacles.end(), polygons.begin(), polygons.end());
613 ARMARX_INFO <<
"Using only vertices for collision model";
614 auto& collPoly = obstacles.emplace_back();
615 collPolyFromVertices(vertices, collPoly);
621 auto mergedObstacles = mergeOverlappingPolygons(obstacles);
622 ARMARX_INFO <<
"Reduced " << obstacles.size() <<
" obstacles to "
623 << mergedObstacles.size() <<
" after merging.";
624 obstacles = std::move(mergedObstacles);
627 for (
const auto& obs : obstacles)
629 ARMARX_INFO <<
"Obstacle: " << boost::geometry::wkt(obs);
#define ARMARX_CHECK_NOT_EMPTY(c)
void setRobot(Robot3D &robot)
float distanceToObstacles(const core::Pose2D &pose2d) const
void initializeRobot(const VirtualRobot::RobotPtr &robot, Robot3D::Params params)
DistanceCalculator(SceneRepresentation &scene)
bool isRobotInitialized() const
CollisionPolygon getRobotAtPose(const core::Pose2D &pose) const
CollisionPolygon getOrientedRobot(float orientationRad) const
std::vector< CollisionPolygon > & getCollisionPolygons()
float distance(const CollisionPolygon &robot) const
Scene2D(std::vector< VirtualRobot::CollisionModelPtr > &collisionModels, float ignoreVerticesOver)
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#define ARMARX_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
#define ARMARX_INFO
The normal logging level.
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
std::shared_ptr< class Robot > RobotPtr
double v(double t, double v0, double a0, double j)
This file is part of ArmarX.
util::geometry::polygon_type CollisionPolygon
std::vector< Eigen::Vector3f > to3D(const std::vector< Eigen::Vector2f > &v)
boost::geometry::model::d2::point_xy< float > point_type
boost::geometry::model::polygon< point_type > polygon_type
auto transform(const Container< InputT, Alloc > &in, OutputT(*func)(InputT const &)) -> Container< OutputT, typename std::allocator_traits< Alloc >::template rebind_alloc< OutputT > >
Convenience function (with less typing) to transform a container of type InputT into the same contain...
double distance(const Point &a, const Point &b)
double angle(const Point &a, const Point &b, const Point &c)
Eigen::Vector3f scaleFactor
std::string collisionModelName
std::set< std::string > primitiveApproximationIDs
bool isInitialized() const
VirtualRobot::RobotPtr robot
static Robot3D fromSimoxRobot(const VirtualRobot::RobotPtr &robot, Robot3D::Params params)
std::vector< VirtualRobot::CollisionModelPtr > collisionModels
const std::vector< VirtualRobot::RobotPtr > articulatedObjects
const VirtualRobot::SceneObjectSetPtr staticObjects