带恢复的路线图导航行为树
该行为树采用了一种与本节其他行为树不同的导航方式。它不使用自由空间规划器 ComputePathToPose 直接规划到目标的完整路径,而是通过 Route Server 在预定义的导航图上查找通往目标的路线。这种方式在大型环境中尤为实用,例如长距离自由空间实时规划的计算量过大、无法在整幅地图范围内规划,或者需要确定性导航行为以及受限导航区域、车道或路线。
该行为树使用 ComputeRoute 节点计算穿越环境的路线。该节点在初始化时执行一次,并在以下任一条件满足时重新执行:目标因抢占而更新(GlobalUpdatedGoal),或当前路线因碰撞而失效(ValidatePath)。随后,如果机器人起始位姿距离路线第一个节点太远,会使用自由空间规划器将当前位姿连接到第一个节点。这一步称为「首英里」(first mile),由 ComputePathToPose 节点完成。如果只需在导航图上导航,且机器人始终位于图上或附近,可以移除该步骤。
与之对应的是「末英里」(last mile)操作:机器人使用自由空间规划器将路线的最后一个节点连接到目标位姿。这同样由 ComputePathToPose 节点完成,可按需移除。计算出的路径(包括首英里和末英里路径)随后在平滑器(smoother)中进行平滑处理,使转角更自然流畅。最后使用 FollowPath 跟随该路径。
有关选择器节点、恢复行为以及回退(fallback)机制的详细说明,请参阅本节中的其他行为树文档。
<root BTCPP_format="4" main_tree_to_execute="MainTree"> <BehaviorTree ID="MainTree"> <RecoveryNode number_of_retries="6" name="NavigateRecovery"> <PipelineSequence name="NavigateWithReplanning"> <ControllerSelector selected_controller="{selected_controller}" default_controller="FollowPath" topic_name="controller_selector"/> <PlannerSelector selected_planner="{selected_planner}" default_planner="GridBased" topic_name="planner_selector"/> <RecoveryNode number_of_retries="1" name="ComputeRoute"> <RateController hz="0.5" name="ComputeRouteRateController"> <Fallback> <!-- Compute a new route if a new goal is found or the path is no longer valid --> <ReactiveSequence> <Inverter> <GlobalUpdatedGoal/> </Inverter> <ValidatePath path="{path}"/> <!-- Base it on the complete connected 'path', not simply the 'route_path' --> </ReactiveSequence> <Sequence name="ComputeAndSmoothRoute"> <!-- Compute the route --> <ComputeRoute goal="{goal}" path="{route_path}" route="{route}" use_poses="true" error_code_id="{compute_route_error_code}" error_msg="{compute_route_error_msg}"/>
<!-- Find if the route start node is far from the robot's current pose; if so, connect them for 'first mile'. --> <ReactiveSequence> <GetCurrentPose current_pose="{current_pose}"/> <GetPoseFromPath path="{route_path}" index="0" pose="{route_start_pose}"/> <Inverter> <ArePosesNear ref_pose="{current_pose}" target_pose="{route_start_pose}" tolerance="0.3"/> </Inverter> <ComputePathToPose goal="{route_start_pose}" path="{first_mile_path}" planner_id="{selected_planner}" error_code_id="{compute_path_error_code}" error_msg="{compute_path_error_msg}"/> <ConcatenatePaths input_path1="{first_mile_path}" input_path2="{route_path}" output_path="{route_path}"/> </ReactiveSequence>
<!-- Find if the route end node is far from the goal pose; if so, connect them for 'last mile'. --> <ReactiveSequence> <GetPoseFromPath path="{route_path}" index="-1" pose="{route_end_pose}"/> <Inverter> <ArePosesNear ref_pose="{goal}" target_pose="{route_end_pose}" tolerance="0.1"/> </Inverter> <ComputePathToPose start="{route_end_pose}" goal="{goal}" path="{last_mile_path}" planner_id="{selected_planner}" error_code_id="{compute_path_error_code}" error_msg="{compute_path_error_msg}"/> <ConcatenatePaths input_path1="{route_path}" input_path2="{last_mile_path}" output_path="{route_path}"/> </ReactiveSequence>
<!-- Smooth the completed route --> <SmoothPath unsmoothed_path="{route_path}" smoothed_path="{path}" smoother_id="route_smoother" error_code_id="{smoother_error_code}" error_msg="{smoother_error_msg}"/> </Sequence> </Fallback> </RateController> <Sequence> <Fallback> <WouldARouteRecoveryHelp error_code="{compute_route_error_code}"/> <WouldAPlannerRecoveryHelp error_code="{compute_path_error_code}"/> </Fallback> <ClearEntireCostmap name="ClearGlobalCostmap-Context" service_name="global_costmap/clear_entirely_global_costmap"/> </Sequence> </RecoveryNode> <RecoveryNode number_of_retries="1" name="FollowPath"> <FollowPath path="{path}" controller_id="{selected_controller}" error_code_id="{follow_path_error_code}" error_msg="{follow_path_error_msg}"/> <Sequence> <WouldAControllerRecoveryHelp error_code="{follow_path_error_code}"/> <ClearEntireCostmap name="ClearLocalCostmap-Context" service_name="local_costmap/clear_entirely_local_costmap"/> </Sequence> </RecoveryNode> </PipelineSequence> <Sequence> <Fallback> <WouldAControllerRecoveryHelp error_code="{follow_path_error_code}"/> <WouldAPlannerRecoveryHelp error_code="{compute_route_error_code}"/> </Fallback> <ReactiveFallback name="RecoveryFallback"> <GoalUpdated/> <RoundRobin name="RecoveryActions"> <Sequence name="ClearingActions"> <ClearEntireCostmap name="ClearLocalCostmap-Subtree" service_name="local_costmap/clear_entirely_local_costmap"/> <ClearEntireCostmap name="ClearGlobalCostmap-Subtree" service_name="global_costmap/clear_entirely_global_costmap"/> </Sequence> <Wait wait_duration="5.0" error_code_id="{wait_error_code}" error_msg="{wait_error_msg}"/> <BackUp backup_dist="0.30" backup_speed="0.15" error_code_id="{backup_error_code}" error_msg="{backup_error_msg}"/> </RoundRobin> </ReactiveFallback> </Sequence> </RecoveryNode> </BehaviorTree> </root>