fix: prevent duplicate NAV2 planning stacks

This commit is contained in:
2026-08-04 15:31:38 +09:00
parent 374a21a6d9
commit 0b2e68ab8a
@@ -6,6 +6,8 @@ from std_msgs.msg import String
import subprocess import subprocess
import json import json
import os
import signal
class PlanningManager(Node): class PlanningManager(Node):
def __init__(self): def __init__(self):
@@ -18,26 +20,59 @@ class PlanningManager(Node):
reliability=ReliabilityPolicy.RELIABLE reliability=ReliabilityPolicy.RELIABLE
) )
self.planning_process = None self.planning_process = None
self.planning_config = None
self.create_subscription(String, '/vehicle_config', self.handle_config, qos_profile=qos) self.create_subscription(String, '/vehicle_config', self.handle_config, qos_profile=qos)
def handle_config(self, msg:String): def handle_config(self, msg:String):
self.get_logger().info(f"SLAM Received config: {msg.data}") try:
config_data = json.loads(msg.data) config_data = json.loads(msg.data)
except json.JSONDecodeError:
self.get_logger().error('Invalid /vehicle_config payload.')
return
self.planning_process = subprocess.Popen(['ros2', 'launch', config = {
'planning_pkg', 'sim_mode': config_data.get('sim_mode'),
'nav2_planning.launch.py', 'robot': config_data.get('robot'),
f'sim_mode:={config_data.get("sim_mode")}', 'level': config_data.get('level'),
f'robot:={config_data.get("robot")}', }
f'level:={config_data.get("level")}']) if self.planning_process and self.planning_process.poll() is None:
if config == self.planning_config:
self.get_logger().info('Planning stack is already running for this vehicle configuration.')
return
self.get_logger().warn('Vehicle configuration changed. Restarting the planning stack.')
self.stop_planning_process()
self.get_logger().info(f"Received vehicle config: {config}")
self.planning_process = subprocess.Popen(
[
'ros2', 'launch', 'planning_pkg', 'nav2_planning.launch.py',
f'sim_mode:={config["sim_mode"]}',
f'robot:={config["robot"]}',
f'level:={config["level"]}',
],
start_new_session=True,
)
self.planning_config = config
def stop_planning_process(self):
if not self.planning_process or self.planning_process.poll() is not None:
self.planning_process = None
return
try:
os.killpg(os.getpgid(self.planning_process.pid), signal.SIGTERM)
self.planning_process.wait(timeout=5)
except subprocess.TimeoutExpired:
os.killpg(os.getpgid(self.planning_process.pid), signal.SIGKILL)
self.planning_process.wait()
except ProcessLookupError:
pass pass
finally:
self.planning_process = None
def destroy_node(self): def destroy_node(self):
if self.planning_process: self.stop_planning_process()
self.planning_process.terminate()
self.planning_process.wait()
return super().destroy_node() return super().destroy_node()
def main(args=None): def main(args=None):
@@ -54,4 +89,3 @@ def main(args=None):
if __name__ == '__main__': if __name__ == '__main__':
main() main()