148 const Eigen::Vector2f startPos2D =
conv::to2D(start.translation());
151 if (costmap_->isInCollision(startPos2D))
153 ARMARX_WARNING <<
"Start position " << startPos2D <<
" is in collision. "
154 <<
"Searching for recovery position within "
155 << generalConfig_.inCollisionDistanceThresholdForRecovery <<
" mm...";
157 const auto recoveryVertex = costmap_->findClosestCollisionFreeVertex(
158 startPos2D, generalConfig_.inCollisionDistanceThresholdForRecovery);
162 ARMARX_ERROR <<
"No valid recovery position found within threshold of "
163 << generalConfig_.inCollisionDistanceThresholdForRecovery <<
" mm";
167 const Eigen::Vector2f recoveryPos = recoveryVertex->position;
170 <<
" (distance: " << (recoveryPos - startPos2D).
norm() <<
" mm). "
171 <<
"Prepending original start position to trajectory.";
175 if (!planningResult.plan.has_value())
182 auto result =
calculatePath(planningResult, goal, recoveryInfo);
184 if (!result && generalConfig_.navigateCloseAsPossible)
186 const Eigen::Vector2f goalPos =
conv::to2D(goal.translation());
188 planningResult.plan.value(), goalPos, costmap_.value());
192 <<
"Navigating to closest reachable position at "
194 <<
" (distance to goal: " << closest->euclideanDistanceToGoal
197 alternativeGoal.translation().head<2>() = closest->position;
198 result =
calculatePath(planningResult, alternativeGoal, recoveryInfo);
209 if (!result && generalConfig_.navigateCloseAsPossible && planningResult.plan.has_value())
211 const Eigen::Vector2f goalPos =
conv::to2D(goal.translation());
213 planningResult.plan.value(), goalPos, costmap_.value());
217 <<
"Navigating to closest reachable position at "
219 <<
" (distance to goal: " << closest->euclideanDistanceToGoal
222 alternativeGoal.translation().head<2>() = closest->position;
282 const std::optional<RecoveryInfo>& recovery)
284 if (not planner.
plan.has_value())
286 ARMARX_INFO <<
"Invalid spfa result, could not calculate path!";
290 const Eigen::Vector2f goalPos =
conv::to2D(goal.translation());
293 Eigen::Vector2f effectiveStartPos;
294 if (recovery.has_value())
296 effectiveStartPos = recovery->recoveryPosition;
312 ARMARX_INFO <<
"Could not plan collision-free path from"
313 << (recovery ? recovery->recoveryPosition
315 <<
" to " <<
"(" << goal.translation().x() <<
"," << goal.translation().y()
322 if (recovery.has_value())
325 conv::to2D(recovery->originalStartPose.translation()));
326 ARMARX_INFO <<
"Prepended original start position. Path now has " <<
plan.path.size()
332 ARMARX_DEBUG <<
"The plan consists of the following positions:";
333 for (
const auto& position :
plan.path)
341 std::vector<core::Position> wpts;
343 if (plan3d.size() >= 2)
345 for (
size_t i = 1; i < (plan3d.size() - 1); i++)
347 wpts.push_back(plan3d.at(i));
364 const std::vector<float> velocitiesRespectingObstacles = [&]()
366 const float defaultVelocity = generalConfig_.maxVel.linear;
369 positions.reserve(wpts.size() + 2);
374 ? recovery->originalStartPose.translation()
375 : planner.
start.translation();
377 positions.emplace_back(startPosition);
378 positions.insert(positions.end(), wpts.begin(), wpts.end());
379 positions.emplace_back(goal.translation());
381 const auto maxVelocityBasedOnObstacles = [&](
const core::Position& position) ->
float
383 const auto minDistanceToObstaclesOpt =
384 costmap_->value(Eigen::Vector2f{position.head<2>()});
386 if (not minDistanceToObstaclesOpt.has_value())
388 return defaultVelocity;
391 const float clippedObstacleDistance =
400 return defaultVelocity * obstacleFactor;
403 return positions | ranges::views::transform(maxVelocityBasedOnObstacles) |
412 recovery.has_value() ? recovery->originalStartPose : planner.
start;
415 trajectoryStart, wpts, goal, velocitiesRespectingObstacles);
419 std::optional<core::GlobalTrajectory> resampledTrajectory;
423 resampledTrajectory =
trajectory.resample(200);
424 ARMARX_DEBUG <<
"Terminal velocity: " << resampledTrajectory->points().back().velocity;
432 ARMARX_VERBOSE <<
"Resampled trajectory contains " << resampledTrajectory->points().size()
435 resampledTrajectory->setMaxVelocity(generalConfig_.maxVel.linear);
436 ARMARX_DEBUG <<
"Terminal velocity: " << resampledTrajectory->points().back().velocity;
438 if (resampledTrajectory->points().size() == 2)
440 ARMARX_VERBOSE <<
"Only start and goal provided. Not optimizing orientation";
443 .helperTrajectory = std::nullopt};
447 const auto computeRecoveryDistance = [&]() ->
float
449 if (!recovery.has_value())
451 return (recovery->recoveryPosition -
452 recovery->originalStartPose.translation().head<2>())
455 const float recoveryDistance = computeRecoveryDistance();
458 size_t subTrajectoryStartIndex = 0;
459 if (recoveryDistance > 0)
461 auto& pts = resampledTrajectory->mutablePoints();
462 const auto startOrientation = pts.front().waypoint.pose.linear();
465 for (
size_t i = 0; i < pts.size(); i++)
468 (pts[i].waypoint.pose.translation() - pts.front().waypoint.pose.translation())
470 if (dist <= recoveryDistance)
472 pts[i].waypoint.pose.linear() = startOrientation;
477 subTrajectoryStartIndex = i;
478 if (subTrajectoryStartIndex < pts.size())
480 pts[subTrajectoryStartIndex].waypoint.pose.linear() = startOrientation;
485 ARMARX_INFO <<
"Recovery distance: " << recoveryDistance <<
" mm, fixed "
486 << subTrajectoryStartIndex
487 <<
" collision segment points, sub-trajectory starts at index "
488 << subTrajectoryStartIndex;
492 std::optional<core::GlobalTrajectory> trajectoryToOptimize;
493 if (subTrajectoryStartIndex > 0)
495 trajectoryToOptimize = resampledTrajectory->getSubTrajectory(
496 subTrajectoryStartIndex, resampledTrajectory->points().size());
500 trajectoryToOptimize = resampledTrajectory;
506 "config/global_planning/OrientationOptimizer.json");
510 <<
"OrientationOptimizer config file does not exist: " << filename;
512 ARMARX_INFO <<
"Loading config from file `" << filename <<
"`.";
513 std::ifstream ifs{filename};
515 nlohmann::json jsonConfig;
523 armarx::navigation::global_planning::arondto::OrientationOptimizerParams dto;
526 dto.read(reader, jsonConfig);
543 if (recoveryDistance > 0 && subTrajectoryStartIndex > 0)
546 auto& resampledPts = resampledTrajectory->mutablePoints();
547 const auto& optimizedPts = result.trajectory->points();
550 if (subTrajectoryStartIndex + optimizedPts.size() == resampledPts.size())
553 for (
size_t i = 0; i < optimizedPts.size(); i++)
555 resampledPts[subTrajectoryStartIndex + i].waypoint.pose.linear() =
556 optimizedPts[i].waypoint.pose.linear();
557 resampledPts[subTrajectoryStartIndex + i].velocity = optimizedPts[i].velocity;
561 finalTrajectory = resampledTrajectory.value();
563 ARMARX_INFO <<
"Combined fixed collision segment (" << subTrajectoryStartIndex
564 <<
" points) with optimized trajectory (" << optimizedPts.size()
569 ARMARX_WARNING <<
"Trajectory size mismatch in collision recovery stitching. "
570 <<
"Using optimized trajectory only.";
581 const auto& costmap = costmap_.value();
585 const float distance = std::min<float>(
587 costmap.value(Eigen::Vector2f{point.waypoint.pose.translation().head<2>()})
592 const float obstacleBasedVelocity =
593 generalConfig_.maxVel.linear /
599 point.velocity = std::min(obstacleBasedVelocity, point.velocity);
606 return GlobalPlannerResult{.trajectory = finalTrajectory, .helperTrajectory = std::nullopt};