244 const Eigen::Vector2f startPos2D =
conv::to2D(start.translation());
247 if (costmap_->isInCollision(startPos2D))
249 ARMARX_WARNING <<
"Start position " << startPos2D <<
" is in collision. "
250 <<
"Searching for recovery position within "
251 << generalConfig_.inCollisionDistanceThresholdForRecovery <<
" mm...";
253 const auto recoveryVertex = costmap_->findClosestCollisionFreeVertex(
254 startPos2D, generalConfig_.inCollisionDistanceThresholdForRecovery);
258 ARMARX_ERROR <<
"No valid recovery position found within threshold of "
259 << generalConfig_.inCollisionDistanceThresholdForRecovery <<
" mm";
263 const Eigen::Vector2f recoveryPos = recoveryVertex->position;
266 <<
" (distance: " << (recoveryPos - startPos2D).
norm() <<
" mm). "
267 <<
"Prepending original start position to trajectory.";
271 if (!planningResult.plan.has_value())
278 auto result =
calculatePath(planningResult, goal, recoveryInfo);
280 if (!result && generalConfig_.navigateCloseAsPossible)
282 const Eigen::Vector2f goalPos =
conv::to2D(goal.translation());
284 planningResult.plan.value(), goalPos, costmap_.value());
288 <<
"Navigating to closest reachable position at "
290 <<
" (distance to goal: " << closest->euclideanDistanceToGoal
293 alternativeGoal.translation().head<2>() = closest->position;
294 result =
calculatePath(planningResult, alternativeGoal, recoveryInfo);
305 if (!result && generalConfig_.navigateCloseAsPossible && planningResult.plan.has_value())
307 const Eigen::Vector2f goalPos =
conv::to2D(goal.translation());
309 planningResult.plan.value(), goalPos, costmap_.value());
313 <<
"Navigating to closest reachable position at "
315 <<
" (distance to goal: " << closest->euclideanDistanceToGoal
318 alternativeGoal.translation().head<2>() = closest->position;
378 const std::optional<RecoveryInfo>& recovery)
380 PipelineTimings timings;
382 if (not planner.
plan.has_value())
384 ARMARX_INFO <<
"Invalid spfa result, could not calculate path!";
388 const Eigen::Vector2f goalPos =
conv::to2D(goal.translation());
391 Eigen::Vector2f effectiveStartPos;
392 if (recovery.has_value())
394 effectiveStartPos = recovery->recoveryPosition;
410 ARMARX_INFO <<
"Could not plan collision-free path from"
411 << (recovery ? recovery->recoveryPosition
413 <<
" to " <<
"(" << goal.translation().x() <<
"," << goal.translation().y()
420 if (recovery.has_value())
423 conv::to2D(recovery->originalStartPose.translation()));
424 ARMARX_INFO <<
"Prepended original start position. Path now has " <<
plan.path.size()
430 ARMARX_DEBUG <<
"The plan consists of the following positions:";
431 for (
const auto& position :
plan.path)
439 std::vector<core::Position> wpts;
441 if (plan3d.size() >= 2)
443 for (
size_t i = 1; i < (plan3d.size() - 1); i++)
445 wpts.push_back(plan3d.at(i));
463 positionsForInitialVelocities.reserve(wpts.size() + 2);
468 ? recovery->originalStartPose.translation()
469 : planner.
start.translation();
471 positionsForInitialVelocities.emplace_back(startPosition);
472 positionsForInitialVelocities.insert(
473 positionsForInitialVelocities.end(), wpts.begin(), wpts.end());
474 positionsForInitialVelocities.emplace_back(goal.translation());
476 const std::vector<float> velocitiesRespectingObstacles =
477 computeObstacleAwareVelocities(positionsForInitialVelocities);
484 recovery.has_value() ? recovery->originalStartPose : planner.
start;
488 ScopedStageTimer timer(timings.buildTrajectory);
490 trajectoryStart, wpts, goal, velocitiesRespectingObstacles);
495 std::optional<core::GlobalTrajectory> resampledTrajectory;
498 ScopedStageTimer timer(timings.resample);
501 resampledTrajectory =
trajectory.resample(200);
502 ARMARX_DEBUG <<
"Terminal velocity: " << resampledTrajectory->points().back().velocity;
510 ARMARX_VERBOSE <<
"Resampled trajectory contains " << resampledTrajectory->points().size()
513 resampledTrajectory->setMaxVelocity(generalConfig_.maxVel.linear);
514 ARMARX_DEBUG <<
"Terminal velocity: " << resampledTrajectory->points().back().velocity;
517 if (resampledTrajectory->points().size() == 2)
519 ARMARX_VERBOSE <<
"Only start and goal provided. Not optimizing orientation";
523 .helperTrajectory = std::nullopt};
530 ScopedStageTimer timer(timings.smoothPositions);
532 const auto smoothingParams =
535 resampledTrajectory.value(), costmap_.value(), smoothingParams);
536 const auto smoothingResult = smoother.
optimize();
538 if (smoothingResult.trajectory && smoothingResult.isCollisionFree)
540 resampledTrajectory = smoothingResult.trajectory.value();
541 ARMARX_INFO <<
"SPFA position smoothing succeeded.";
545 ARMARX_WARNING <<
"SPFA position smoothing did not produce a collision-free "
546 "result; using original resampled SPFA path.";
553 ScopedStageTimer timer(timings.recomputeVelocities);
556 smoothedPositions.reserve(resampledTrajectory->points().size());
557 for (
const auto& point : resampledTrajectory->points())
559 smoothedPositions.emplace_back(point.waypoint.pose.translation());
562 const std::vector<float> recomputedVelocities =
563 computeObstacleAwareVelocities(smoothedPositions);
565 ARMARX_CHECK(recomputedVelocities.size() == resampledTrajectory->points().size());
567 auto& mutablePoints = resampledTrajectory->mutablePoints();
568 for (
size_t i = 0; i < mutablePoints.size(); ++i)
570 mutablePoints[i].velocity = recomputedVelocities[i];
575 size_t subTrajectoryStartIndex = 0;
576 std::optional<core::GlobalTrajectory> trajectoryToOptimize;
577 float recoveryDistance = 0.0f;
579 ScopedStageTimer timer(timings.recoveryHandling);
580 const auto computeRecoveryDistance = [&]() ->
float
582 if (!recovery.has_value())
584 return (recovery->recoveryPosition -
585 recovery->originalStartPose.translation().head<2>())
588 recoveryDistance = computeRecoveryDistance();
591 if (recoveryDistance > 0)
593 auto& pts = resampledTrajectory->mutablePoints();
594 const auto startOrientation = pts.front().waypoint.pose.linear();
597 for (
size_t i = 0; i < pts.size(); i++)
600 (pts[i].waypoint.pose.translation() - pts.front().waypoint.pose.translation())
602 if (dist <= recoveryDistance)
604 pts[i].waypoint.pose.linear() = startOrientation;
609 subTrajectoryStartIndex = i;
610 if (subTrajectoryStartIndex < pts.size())
612 pts[subTrajectoryStartIndex].waypoint.pose.linear() = startOrientation;
617 ARMARX_INFO <<
"Recovery distance: " << recoveryDistance <<
" mm, fixed "
618 << subTrajectoryStartIndex
619 <<
" collision segment points, sub-trajectory starts at index "
620 << subTrajectoryStartIndex;
624 if (subTrajectoryStartIndex > 0)
626 trajectoryToOptimize = resampledTrajectory->getSubTrajectory(
627 subTrajectoryStartIndex, resampledTrajectory->points().size());
631 trajectoryToOptimize = resampledTrajectory;
637 ScopedStageTimer timer(timings.loadOrientationConfig);
639 "config/global_planning/OrientationOptimizer.json");
643 <<
"OrientationOptimizer config file does not exist: " << filename;
645 ARMARX_INFO <<
"Loading config from file `" << filename <<
"`.";
646 std::ifstream ifs{filename};
648 nlohmann::json jsonConfig;
656 armarx::navigation::global_planning::arondto::OrientationOptimizerParams dto;
659 dto.read(reader, jsonConfig);
665 auto result = [&]() {
666 ScopedStageTimer timer(timings.optimizeOrientation);
680 ScopedStageTimer timer(timings.stitchTrajectory);
681 if (recoveryDistance > 0 && subTrajectoryStartIndex > 0)
684 auto& resampledPts = resampledTrajectory->mutablePoints();
685 const auto& optimizedPts = result.trajectory->points();
688 if (subTrajectoryStartIndex + optimizedPts.size() == resampledPts.size())
691 for (
size_t i = 0; i < optimizedPts.size(); i++)
693 resampledPts[subTrajectoryStartIndex + i].waypoint.pose.linear() =
694 optimizedPts[i].waypoint.pose.linear();
695 resampledPts[subTrajectoryStartIndex + i].velocity = optimizedPts[i].velocity;
699 finalTrajectory = resampledTrajectory.value();
701 ARMARX_INFO <<
"Combined fixed collision segment (" << subTrajectoryStartIndex
702 <<
" points) with optimized trajectory (" << optimizedPts.size()
707 ARMARX_WARNING <<
"Trajectory size mismatch in collision recovery stitching. "
708 <<
"Using optimized trajectory only.";
720 const auto& costmap = costmap_.value();
723 ScopedStageTimer timer(timings.finalVelocityClamp);
726 const float distance = std::min<float>(
728 costmap.value(Eigen::Vector2f{point.waypoint.pose.translation().head<2>()})
733 const float obstacleBasedVelocity =
734 generalConfig_.maxVel.linear /
740 point.velocity = std::min(obstacleBasedVelocity, point.velocity);
749 return GlobalPlannerResult{.trajectory = finalTrajectory, .helperTrajectory = std::nullopt};