import os import yaml import logging from launch import LaunchDescription from launch.actions import DeclareLaunchArgument, OpaqueFunction from launch.substitutions import LaunchConfiguration from launch_ros.actions import Node from launch_ros.descriptions import ComposableNode from launch.conditions import UnlessCondition from launch_ros.actions import LoadComposableNodes from ament_index_python.packages import get_package_share_directory import sys sys.path.append(os.path.dirname(os.path.realpath(__file__))) from utils_yaml import * logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) default_package_name = 'orbbec_camera' default_camera_model = 'gemini330_series' default_common_yaml = 'common.yaml' default_component_container_name = 'orbbec_container' def generate_launch_arguments(): launch_arguments = [ #general config DeclareLaunchArgument('camera_model', default_value=default_camera_model), DeclareLaunchArgument('config_file_path', default_value=''), #multi-device sync param DeclareLaunchArgument('camera_name', default_value='camera'), DeclareLaunchArgument('usb_port', default_value=''), DeclareLaunchArgument('device_num', default_value='1'), DeclareLaunchArgument('sync_mode', default_value='standalone'), DeclareLaunchArgument('trigger_out_enabled', default_value='true'), #instra-process demo set DeclareLaunchArgument('use_intra_process_comms', default_value='false'), DeclareLaunchArgument('attach_component_container_enable', default_value='false'), DeclareLaunchArgument('attach_component_container_name', default_value=default_component_container_name), ] return launch_arguments def load_common_yaml_params(): common_yaml_path = os.path.join( get_package_share_directory(default_package_name), 'config', default_common_yaml ) common_yaml_path = get_absolute_path(common_yaml_path, package_name=default_package_name) logger.info(f"load convert Absolute path: {common_yaml_path}") return load_and_parse_yaml(common_yaml_path) def load_config_yaml_params(config_file_path, camera_model): config_yaml_path = config_file_path or os.path.join( get_package_share_directory(default_package_name), 'config', f"{camera_model}.yaml" ) config_yaml_path = get_absolute_path(config_yaml_path, package_name=default_package_name) print(f"load convert Absolute config_yaml_path: {config_yaml_path}") return load_and_parse_yaml(config_yaml_path) def load_parameters(context, args): camera_model = LaunchConfiguration("camera_model").perform(context) config_file_path = LaunchConfiguration("config_file_path").perform(context) usb_port = LaunchConfiguration("usb_port").perform(context) print(f"camera_model: {camera_model}", f"config_file_path: {config_file_path}", f"usb_port: {usb_port}") try: #1.load ymal params yaml_common_params = load_common_yaml_params() yaml_params = load_config_yaml_params(config_file_path,camera_model) #print_params(yaml_common_params) #print_params(yaml_params) yaml_params = update_params(yaml_common_params,yaml_params) #print_params(yaml_params) #2.load args params args_params = {arg.name: LaunchConfiguration(arg.name).perform(context) for arg in args} #print_params(args_params) #3.meger params args if usb_port is None or usb_port == "": #meger yaml->args params print("usb_port param null") default_params = update_params(args_params, yaml_params) else: print("usb_port param not null value:", usb_port) #meger args->yaml params default_params = update_params(yaml_params, args_params) #print_params(default_params) #4.load cmd params & meger params cmd->yaml cmd_params = dict([a for a in [a.split(':=') for a in sys.argv] if len(a) == 2]) default_params = update_params(default_params,cmd_params) #print_params(args_params) except (FileNotFoundError, yaml.YAMLError) as e: logger.error(f"Error loading YAML file: {e}") return {} #5.Skip conversion for specific parameters SKIP_CONVERT = {'config_file_path', 'usb_port', 'serial_number', 'attach_component_container_name'} return { key: (value if key in SKIP_CONVERT else convert_value(value)) for key, value in default_params.items() } def find_camera_name(params): return find_param(params, 'camera_name', 'camera') def find_use_intra_process_comms(params): return find_param(params, 'use_intra_process_comms', False) def generate_launch_description(): args = generate_launch_arguments() def get_params(context, args): return [load_parameters(context, args)] def create_composable_node(camera_name, params, use_intra_process): common_arguments = [{'use_intra_process_comms': use_intra_process}] composable_node = ComposableNode( namespace=camera_name, name=camera_name, package=default_package_name, plugin='orbbec_camera::OBCameraNodeDriver', parameters=params, #extra_arguments=[{'use_intra_process_comms': LaunchConfiguration("use_intra_process_comms")}], extra_arguments=common_arguments, ) return composable_node def create_orbbec_container(attach_component_container_name, attach_component_container_enable): orbbec_container = Node( name=attach_component_container_name, package='rclcpp_components', executable='component_container_mt', output='screen', condition=UnlessCondition(attach_component_container_enable) ) return orbbec_container def create_node_action(context, args): params = get_params(context, args) camera_name = find_camera_name(params) use_intra_process = find_use_intra_process_comms(params) #use_intra_process = LaunchConfiguration("use_intra_process_comms", default=False).perform(context) print(f"camera_name: {camera_name}", f"use_intra_process: {use_intra_process}") attach_enable_condition = LaunchConfiguration("attach_component_container_enable", default=False).perform(context) attach_component_container_name = LaunchConfiguration("attach_component_container_name", default='orbbec_container').perform(context) print(f"attach_enable_condition: {attach_enable_condition}", f"attach_component_container_name: {attach_component_container_name}") ros_distro = os.environ.get("ROS_DISTRO", "humble") if ros_distro == "foxy": return [ Node( package=default_package_name, executable="orbbec_camera_node", name="ob_camera_node", namespace=camera_name, parameters=params, output="screen", #condition=UnlessCondition(attach_enable_condition) #prefix=['xterm -e gdb -ex run --args'], ), ] else: composable_node = create_composable_node(camera_name, params, use_intra_process) orbbec_container= create_orbbec_container(attach_component_container_name, attach_enable_condition) return [ orbbec_container, LoadComposableNodes( target_container=attach_component_container_name, composable_node_descriptions=[composable_node] ), ] return LaunchDescription( args + [ OpaqueFunction(function=lambda context: create_node_action(context, args)) ] )