Export to ROS Nav2
ROS in general and ROS Nav2 in particular are very popular in robotics. They take care of many aspects of robot control, including navigation, localization, mapping, and more.
Forester provides a way to export a tree to ROS Nav2. The intermediate format is Nav2 XML format.
The transformation format for now is pretty straightforward.
Control nodes
The control nodes are mapped to the nav2 control nodes directly:
PipelineSequence
tosequence
RoundRobin
tofallback
ReactiveFallback
tor_fallback
If the control node has a name, it is used as the name of the control node in the nav2 tree.
sequence FollowPathWithFallback{
..
}
will become
<PipelineSequence name="FollowPathWithFallback">
</PipelineSequence>
Actions
The actions can be mapped straightforwardly to the nav2 actions.
Every action can implicitly take a name
parameter, which is used as the name of the action in the nav2 tree.
But the name parameter can be omitted also.
Some of the actions take the subtree as a parameter. The parameter has a name sub
Retry
Retry is represented in two options:
Retry
- the number of retries is specified without a name likeretry(3)
RecoveryNode
- the number of retries is specified with the default way likeRecoveryNode(number_of_retries = 3)
. This becomes only way to convey the name of the node.
RecoveryNode(
number_of_retries = 1,
name = "ComputePathToPose", // it allows to convey the name
sub = ComputePathWithFallback()
)
retry(1) ComputePathWithFallback() // it is not possible to convey the name
// but everything else is the same
Example
import "ros::nav2"
root MainTree RecoveryNode(number_of_retries = 6, name = "NavigateRecovery", sub = NavigateWithReplanning())
sequence NavigateWithReplanning {
RateController(
hz = 1.0,
sub = RecoveryNode(
number_of_retries = 1,
name = "ComputePathToPose",
sub = retry(1) ComputePathWithFallback()
)
)
retry(1) FollowPathWithFallback()
}
sequence ComputePathWithFallback{
ComputePathToPose(goal = goal,path = path,planner_id = "GridBased")
ComputePathToPoseRecoveryFallback()
}
sequence FollowPathWithFallback{
FollowPath(path = path,controller_id = "FollowPath")
FollowPathRecoveryFallback()
}
r_fallback ComputePathToPoseRecoveryFallback {
GoalUpdated()
ClearEntireCostmap(name = "ClearGlobalCostmap-Context", service_name = "global_costmap/clear_entirely_global_costmap")
}
r_fallback FollowPathRecoveryFallback {
GoalUpdated()
ClearEntireCostmap(name = "ClearLocalCostmap-Context", service_name = "local_costmap/clear_entirely_local_costmap")
}
will be transformed into
<root main_tree_to_execute="MainTree">
<BehaviorTree ID="MainTree">
<RecoveryNode number_of_retries="6" name="NavigateRecovery">
<PipelineSequence name="NavigateWithReplanning">
<RateController hz="1">
<RecoveryNode number_of_retries="1" name="ComputePathToPose">
<RecoveryNode number_of_retries="1">
<PipelineSequence name="ComputePathWithFallback">
<ComputePathToPose goal="{goal}" path="{path}" planner_id="GridBased"/>
<ReactiveFallback name="ComputePathToPoseRecoveryFallback">
<GoalUpdated/>
<ClearEntireCostmap name="ClearGlobalCostmap-Context" service_name="global_costmap/clear_entirely_global_costmap"/>
</ReactiveFallback>
</PipelineSequence>
</RecoveryNode>
</RecoveryNode>
</RateController>
<RecoveryNode number_of_retries="1">
<PipelineSequence name="FollowPathWithFallback">
<FollowPath path="{path}" controller_id="FollowPath"/>
<ReactiveFallback name="FollowPathRecoveryFallback">
<GoalUpdated/>
<ClearEntireCostmap name="ClearLocalCostmap-Context" service_name="local_costmap/clear_entirely_local_costmap"/>
</ReactiveFallback>
</PipelineSequence>
</RecoveryNode>
</PipelineSequence>
</RecoveryNode>
</BehaviorTree>
</root>
Tools
The changes arrived in the latest version of f-tree, therefore better to update f-tree
cargo install f-tree
Headers
To have headers for nav2 actions, you need to import the ros::nav2
module in your project.
To see the content of the file, run
f-tree -d print-ros-nav2
Export from console
To export the tree from the console, run
f-tree.exe nav2
Export from Intellij plugin
Run the task Export to ROS Nav2
Export from code
#![allow(unused)] fn main() { #[test] fn smoke() { let mut root_path = test_folder("ros/nav/smoke"); let project = Project::build("main.tree".to_string(), root_path.clone()).unwrap(); let tree = RuntimeTree::build(project).unwrap().tree; fb.push("test.xml"); tree.to_ros_nav(root_path.clone()).unwrap(); } }