TrajectoryChecker.cpp
Go to the documentation of this file.
1#include "TrajectoryChecker.h"
2
3#include <cmath>
4
5#ifndef M_PIf
6#define M_PIf 3.14159265358979323846f
7#endif
8
10
12{
13 inline double normalizeAngle(double a)
14 {
15 return std::atan2(std::sin(a), std::cos(a));
16 }
17
21
22 bool TrajectoryChecker::isInCollisionBilinear(float x, float y, float theta_rad) const
23 {
24 // Uses the same bilinear position interpolation as Costmap3DWrapper
25 const auto localPosition = costmap_.origin().inverse() * Eigen::Vector2f{x, y};
26
27 // Treat points outside the costmap bounds as collision
28 const auto& bounds = costmap_.getLocalSceneBounds();
29 if (localPosition.x() < bounds.min.x() || localPosition.x() > bounds.max.x() ||
30 localPosition.y() < bounds.min.y() || localPosition.y() > bounds.max.y())
31 {
32 return true;
33 }
34
35 const float cell = costmap_.params().cellSize;
36
37 const float vX =
38 (localPosition.x() - cell / 2.0f - costmap_.getLocalSceneBounds().min.x()) / cell;
39 const float vY =
40 (localPosition.y() - cell / 2.0f - costmap_.getLocalSceneBounds().min.y()) / cell;
41
42 // Same -0.01 shift as Costmap3DWrapper::toIndexBilinear
43 const float vX_shift = vX - 0.01f;
44 const float vY_shift = vY - 0.01f;
45
46 int iXlow = static_cast<int>(std::floor(vX_shift));
47 int iXhigh = static_cast<int>(std::ceil(vX_shift));
48 int iYlow = static_cast<int>(std::floor(vY_shift));
49 int iYhigh = static_cast<int>(std::ceil(vY_shift));
50
51 const auto size = costmap_.getSize();
52 iXlow = std::clamp(iXlow, 0, size.x() - 1);
53 iXhigh = std::clamp(iXhigh, 0, size.x() - 1);
54 iYlow = std::clamp(iYlow, 0, size.y() - 1);
55 iYhigh = std::clamp(iYhigh, 0, size.y() - 1);
56
57 const float w00 =
58 (static_cast<float>(iXhigh) - vX) * (static_cast<float>(iYhigh) - vY);
59 const float w10 =
60 (vX - static_cast<float>(iXlow)) * (static_cast<float>(iYhigh) - vY);
61 const float w01 =
62 (static_cast<float>(iXhigh) - vX) * (vY - static_cast<float>(iYlow));
63 const float w11 =
64 (vX - static_cast<float>(iXlow)) * (vY - static_cast<float>(iYlow));
65
66 const auto rotation = costmap_.closestRotationFromDegrees(theta_rad * 180.0f / M_PIf);
67
68 auto gridVal = [&](int ix, int iy) -> float {
69 return costmap_.value_ignore_mask({ix, iy}, rotation.index);
70 };
71
72 float interpolated = w00 * gridVal(iXlow, iYlow) + w10 * gridVal(iXhigh, iYlow) +
73 w01 * gridVal(iXlow, iYhigh) + w11 * gridVal(iXhigh, iYhigh);
74
75 return interpolated <= 10.0f;
76 }
77
79 {
81
82 struct Point { double x, y, theta; };
83 std::vector<Point> points;
84 points.reserve(trajectory.points().size());
85 for (const auto& gp : trajectory.points())
86 {
87 const auto& R = gp.waypoint.pose.rotation();
88 points.push_back({
89 gp.waypoint.pose.translation().x(),
90 gp.waypoint.pose.translation().y(),
91 std::atan2(R(1, 0), R(0, 0))
92 });
93 }
94
95 // Check waypoints
96 for (std::size_t i = 0; i < points.size(); ++i)
97 {
98 if (isInCollisionBilinear(static_cast<float>(points[i].x),
99 static_cast<float>(points[i].y),
100 static_cast<float>(points[i].theta)))
101 {
102 result.collisionCount++;
103 result.collisionPoints.push_back(trajectory.points()[i]);
104 if (logDetails)
105 {
106 ARMARX_INFO << "Collision detected at waypoint " << i;
107 }
108 }
109 }
110
111 // Check segments
112 constexpr float segmentResolution = 50.0f;
113 for (std::size_t i = 0; i + 1 < points.size(); ++i)
114 {
115 double dx = points[i + 1].x - points[i].x;
116 double dy = points[i + 1].y - points[i].y;
117 double dist = std::hypot(dx, dy);
118 int numSamples = static_cast<int>(std::ceil(dist / segmentResolution));
119 if (numSamples < 2)
120 numSamples = 2;
121
122 for (int s = 1; s < numSamples; ++s)
123 {
124 double t = static_cast<double>(s) / numSamples;
125 double sampleX = points[i].x + t * dx;
126 double sampleY = points[i].y + t * dy;
127 double dtheta = normalizeAngle(points[i + 1].theta - points[i].theta);
128 double sampleTheta = normalizeAngle(points[i].theta + t * dtheta);
129
130 if (isInCollisionBilinear(static_cast<float>(sampleX),
131 static_cast<float>(sampleY),
132 static_cast<float>(sampleTheta)))
133 {
134 result.collisionCount++;
135 core::GlobalTrajectoryPoint samplePoint = trajectory.points()[i];
136 samplePoint.waypoint.pose =
137 Eigen::Translation3f{Eigen::Vector3f(static_cast<float>(sampleX),
138 static_cast<float>(sampleY),
139 0.0f)} *
140 Eigen::AngleAxisf{static_cast<float>(sampleTheta), Eigen::Vector3f::UnitZ()};
141 result.collisionPoints.push_back(samplePoint);
142 if (logDetails)
143 {
144 ARMARX_INFO << "Collision detected on segment between waypoints " << i << " and " << i + 1;
145 }
146 }
147 }
148 }
149
150 return result;
151 }
152
153} // namespace armarx::navigation::algorithms::orientation_aware
#define M_PIf
const SceneBounds & getLocalSceneBounds() const noexcept
Rotation closestRotationFromDegrees(RotationDegrees degrees) const
float value_ignore_mask(const Index &index, const RotationIndex rot_index) const
TrajectoryCollisionCheckResult check(const core::GlobalTrajectory &trajectory, bool logDetails=false) const
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
This file offers overloads of toIce() and fromIce() functions for STL container types.
float cellSize
How big each cell is in the uniform grid.
Definition Costmap3D.h:35