42 this->clearance = clearance;
43 initialize(rows, cols, orientations,
costmap);
49 for (
int r = 0; r < rows; r++)
51 for (
int c = 0;
c < cols;
c++)
53 for (
int rot_idx = 0; rot_idx < orientations; rot_idx++)
68 return data_[((row * cols) + col) * orientations + orientation];
72 Grid::initialize(
int rows,
int cols,
int orientations,
const Costmap3D&
costmap)
77 this->orientations = orientations;
85 data_ = std::make_unique<Node[]>(rows * cols * orientations);
88 for (
int r = 0; r < rows; r++)
90 for (
int c = 0;
c < cols;
c++)
92 for (
int rot_idx = 0; rot_idx < orientations; rot_idx++)
94 const auto pos =
costmap.toPositionGlobal({r,
c});
95 const float obstacleDistance =
costmap.value_ignore_mask(
97 const auto rotation_deg =
111 for (
int r = 0; r < rows; r++)
113 for (
int c = 0;
c < cols;
c++)
115 for (
int rot_idx = 0; rot_idx < orientations; rot_idx++)
117 Node& candidate =
getNode(r,
c, rot_idx);
124 for (
int nR = -1; nR <= 1; nR++)
126 for (
int nC = -1; nC <= 1; nC++)
128 const int neighborIndexC =
c + nC;
129 const int neighborIndexR = r + nR;
130 if (neighborIndexC < 0 || neighborIndexR < 0 || (nR == 0 && nC == 0))
135 if (neighborIndexR >=
static_cast<int>(rows) ||
136 neighborIndexC >=
static_cast<int>(cols))
143 for (
int nRot = -3; nRot <= 3; nRot++)
145 int neighborRotIndex = rot_idx + nRot;
146 if (neighborRotIndex < 0)
148 neighborRotIndex += orientations;
150 if (neighborRotIndex >= orientations)
152 neighborRotIndex -= orientations;
154 getNode(neighborIndexR, neighborIndexC, neighborRotIndex)
161 bool enableRotationInPlace =
163 for (
int nRot = -1; enableRotationInPlace && nRot <= 1; nRot++)
169 int neighborRotIndex = rot_idx + nRot;
170 if (neighborRotIndex < 0)
172 neighborRotIndex += orientations;
174 if (neighborRotIndex >= orientations)
176 neighborRotIndex -= orientations;
185 ARMARX_VERBOSE <<
"Grid created in " << (t2 - t1).toMilliSeconds() <<
" ms";
194 return n.obstacleDistance > clearance;
198 costmap3d(costmap3d), params(params)
203 AStarPlanner::heuristic(
const Node& n1,
const Node& n2)
217 float rot_deg = Eigen::Rotation2Df{pose.rotation()}.angle() * 180.F /
M_PI;
224 const auto vertex = costmap3d.
toVertex(pose.translation());
226 ARMARX_DEBUG <<
"Closest rotation: " << rot.index <<
" (" << rot.degrees <<
" deg)";
228 const int r = vertex.index.x();
229 const int c = vertex.index.y();
235 const int rows =
static_cast<int>(costmap3d.
getSize().
x());
236 const int cols =
static_cast<int>(costmap3d.
getSize().y());
242 return grid->getNode(
static_cast<int>(r),
static_cast<int>(
c), rot.index);
249 angleBetween(
const Eigen::Vector2f& a,
const Eigen::Vector2f& b)
251 float dotProd = a.dot(b);
252 float magA = a.norm();
253 float magB = b.norm();
255 if (magA == 0.0f || magB == 0.0f)
261 float cosTheta = dotProd / (magA * magB);
266 if (cosTheta < -1.0f)
269 return std::acos(cosTheta);
273 smoothnessCosts(
const Node& n1,
275 const std::optional<Node>& n3,
276 const std::optional<Node>& n4)
284 const Eigen::Vector2f v21 = n1.position - n2.position;
285 const Eigen::Vector2f v23 = n3.value().position - n2.position;
286 float angle = std::abs(angleBetween(v21, -v23));
288 cost += angle_to_cost(
angle);
290 if (
false && n3.has_value() && n4.has_value())
292 const Eigen::Vector2f v32 = n2.position - n3.value().position;
293 const Eigen::Vector2f v34 = n4.value().position - n3.value().position;
294 float angle = angleBetween(v32, -v34);
296 cost += angle_to_cost(
angle);
302 std::vector<core::Pose2D>
307 if (grid.has_value())
314 const auto size = costmap3d.getSize();
315 grid = {size.x(), size.y(), costmap3d.params().orientations, costmap3d, params.clearance};
317 std::vector<core::Pose2D> result;
319 ARMARX_DEBUG <<
"Setting start node for position " << start.matrix();
320 Node& nodeStart = closestNode(start);
323 << costmap3d.closestRotationFromDegrees(nodeStart.
orientation_deg).index;
325 <<
"Start node in collision (within clearance=" << params.clearance <<
"mm)!";
327 ARMARX_DEBUG <<
"Setting goal node for position " << goal.matrix();
328 Node& nodeGoal = closestNode(goal);
331 << costmap3d.closestRotationFromDegrees(nodeGoal.
orientation_deg).index;
333 <<
"Goal node in collision (within clearance=" << params.clearance <<
"mm)!";
339 nodeStart.
fScore = nodeStart.
gScore + heuristic(nodeStart, nodeGoal);
342 auto cmp = [&](
const Node* left,
const Node* right)
343 {
return left->fScore > right->fScore; };
344 std::priority_queue<Node*, std::vector<Node*>,
decltype(cmp)> openSetPq(cmp);
345 openSetPq.push(&nodeStart);
348 bool foundSolution =
false;
349 while (!openSetPq.empty())
351 Node& currentBest = *openSetPq.top();
354 if (¤tBest == &nodeGoal)
356 foundSolution =
true;
361 for (
size_t i = 0; i < currentBest.
successors.size(); i++)
372 cost = costs(currentBest, neighbor);
374 catch (
const std::exception& e)
376 ARMARX_ERROR <<
"Cost calculation failed for neighbor at position "
377 << neighbor.
position.transpose() <<
" with orientation "
386 const auto pred2 = pred1.has_value()
388 ? std::make_optional(*(pred1->predecessor))
392 smoothnessCosts(neighbor, currentBest, pred1, pred2);
398 neighbor.
gScore = tentativeGScore;
399 neighbor.
fScore = tentativeGScore + heuristic(neighbor, nodeGoal);
402 openSetPq.push(&neighbor);
413 result = resultNodes |
414 ranges::views::transform(
415 [
this](
const Node* node)
noexcept {
416 return costmap3d.globalPose(costmap3d.toVertex(node->
position).index,
420 auto predecessorCosts = resultNodes |
421 ranges::views::transform([](
const Node* node)
noexcept
424 ranges::reverse(predecessorCosts);
426 Costs combinedCosts = {};
427 for (
const auto&
c : predecessorCosts)
430 if (not
c.has_value())
440 combinedCosts = combinedCosts +
c.value();
455 ranges::reverse(result);
461 AStarPlanner::costs(
const Node& n1,
const Node& n2)
const
471 const float obstacleProximityChangeCosts = obstacleDistanceN1 - obstacleDistanceN2;
478 float effectiveDistN2 = std::max(obstacleDistanceN2 - params.
clearance, 0.0f);
480 ratio = std::clamp(ratio, 0.0f, 1.0f);
481 const float obstacleProximityN2CostsNormalized =
490 auto ori_vec_n1 = pose_n1.linear() * Eigen::Vector2f::UnitY();
491 auto ori_vec_n2 = pose_n2.linear() * Eigen::Vector2f::UnitY();
492 Eigen::Vector2f ori_vec = ori_vec_n1 + ori_vec_n2;
494 const Eigen::Vector2f vectorDiff_norm = vectorDiff.normalized();
495 float forward_angle = 0;
496 if (vectorDiff.norm() == 0.F)
498 ARMARX_DEBUG <<
"Zero-length vector between nodes, cannot calculate forward movement "
499 "cost. n1 position: "
505 forward_angle = std::abs(angleBetween(ori_vec, -vectorDiff_norm));
513 * (1.0F - obstacleProximityRatio));
518 .obstacleProximity2 =
521 .forwardMovement = forward_angle * dynamicForwardWeight,