Hi,
I felt boring to change every config file twice and when I imagined, that I may have 100 of robots, I gave up editing the files manually.
So I wrote this launch file:
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription, LaunchContext
from launch_ros.actions import Node
from nav2_common.launch import RewrittenYaml
import yaml
from pathlib import Path
from path_planner_server.helpers import write_config
def create_amcl_config(ns: str, path: str) -> str:
#print('Load file:' + path)
config = yaml.safe_load(Path(path).read_text())
#print(yaml.dump(config))
rp = config['amcl']['ros__parameters']
rp['base_frame_id'] = ns + rp['base_frame_id']
rp['odom_frame_id'] = ns + rp['odom_frame_id']
rp['scan_topic'] = '/' + ns + rp['scan_topic']
robot_num = int(ns.split('_')[1][:-1])
rp['initial_pose']['y'] = robot_num * 0.5
config[ns + 'amcl'] = config.pop('amcl')
return write_config(config)
def generate_launch_description():
shared_dir = get_package_share_directory('path_planner_server')
config_dir = os.path.join(shared_dir, 'config')
map_file = os.path.join(config_dir, 'turtlebot_area_two_robots.yaml')
nodes = []
managed_node_names = []
nodes.append(Node(
package='nav2_map_server',
executable='map_server',
name='map_server',
output='screen',
parameters=[{'use_sim_time': True},
{'topic_name':"map"},
{'frame_id':"map"},
{'yaml_filename': map_file}]
))
managed_node_names.append('map_server')
for ns_str in ['tb3_0', 'tb3_1']:
ns = ns_str + '/' if ns_str != '' else ''
amcl_config_file_name = create_amcl_config(
ns,
os.path.join(config_dir, 'amcl_config.yaml')
)
amcl_node = Node(
package='nav2_amcl',
executable='amcl',
name='amcl',
namespace=ns_str,
output='screen',
parameters=[amcl_config_file_name]
)
nodes.append(amcl_node)
managed_node_names.append(ns + 'amcl')
lifecycle_mgr_node = Node(
package='nav2_lifecycle_manager',
executable='lifecycle_manager',
name='lifecycle_manager_localization',
output='screen',
parameters=[{'use_sim_time': True},
{'autostart': True},
{'bond_timeout':0.0},
{'node_names': managed_node_names }
]
)
nodes.append(lifecycle_mgr_node)
return LaunchDescription(nodes)
Here is the helpers.py file, I had to put it into the path_planner_server/path_planner_server directory
to be able to use it:
from pathlib import Path
import tempfile
import yaml
def write_config(config: dict) -> str:
print('Write config')
fixed_config = yaml.dump(config).replace('//', '/')
print(fixed_config)
fp = tempfile.NamedTemporaryFile(delete=False)
fp.write(fixed_config.encode())
fp.close()
return fp.name
The trick is that I am loading the config files with yaml.safe_load. It gives me a dictionary object.
I modify this dictionary and saving it to a temporary file. Then I give this temporary file to the nodes as a configuration file.