ROS2 学习笔记4--launch

launch 基本使用流程

配置

launch 在使用 python 时要引用很多头文件,非常不方便,所以建议先配置代码片段

	"ros2 launch py":{
		"prefix": "ros2_launch_py",
		"body": [
			"from launch import LaunchDescription",
			"from launch_ros.actions import Node",
			"# 封装终端指令相关类--------------",
			"# from launch.actions import ExecuteProcess",
			"# from launch.substitutions import FindExecutable",
			"# 参数声明与获取-----------------",
			"# from launch.actions import DeclareLaunchArgument",
			"# from launch.substitutions import LaunchConfiguration",
			"# 文件包含相关-------------------",
			"# from launch.actions import IncludeLaunchDescription",
			"# from launch.launch_description_sources import PythonLaunchDescriptionSource",
			"# 分组相关----------------------",
			"# from launch_ros.actions import PushRosNamespace",
			"# from launch.actions import GroupAction",
			"# 事件相关----------------------",
			"# from launch.event_handlers import OnProcessStart, OnProcessExit",
			"# from launch.actions import ExecuteProcess, RegisterEventHandler,LogInfo",
			"# 获取功能包下 share 目录路径-------",
			"# from ament_index_python.packages import get_package_share_directory",
			"",
			"def generate_launch_description():",
			"    ",
			"    return LaunchDescription([])",
		],
		"description": "ros2 launch"
	}

新建 launch 文件夹
下有 py yaml xml 三个文件夹
在Cmake里配置

install(DIRECTORY launch DESTINATION share/${PROJECT_NAME})

最终效果

cmake_minimum_required(VERSION 3.8)
project(cpp01_launch)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)


install(DIRECTORY launch DESTINATION share/${PROJECT_NAME})
if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # the following line skips the linter which checks for copyrights
  # comment the line when a copyright and license is added to all source files
  set(ament_cmake_copyright_FOUND TRUE)
  # the following line skips cpplint (only works in a git repo)
  # comment the line when this package is in a git repo and when
  # a copyright and license is added to all source files
  set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

ament_package()

对于带有启动文件的功能包,最好在功能包的 package.xml 中添加对包 ros2launch 的执行时依赖:

  <exec_depend>ros2launch</exec_depend>

这有助于确保在构建功能包后 ros2 launch 命令可用。它还确保可以识别不同格式的 launch 文件。

最终效果

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>cpp01_launch</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="herrscher@todo.todo">herrscher</maintainer>
  <license>TODO: License declaration</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

  <depend>rclcpp</depend>
  <exec_depend>ros2launch</exec_depend>

  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

C++

python

from launch import LaunchDescription
from launch_ros.actions import Node
# 封装终端指令相关类--------------
# from launch.actions import ExecuteProcess
# from launch.substitutions import FindExecutable
# 参数声明与获取-----------------
# from launch.actions import DeclareLaunchArgument
# from launch.substitutions import LaunchConfiguration
# 文件包含相关-------------------
# from launch.actions import IncludeLaunchDescription
# from launch.launch_description_sources import PythonLaunchDescriptionSource
# 分组相关----------------------
# from launch_ros.actions import PushRosNamespace
# from launch.actions import GroupAction
# 事件相关----------------------
# from launch.event_handlers import OnProcessStart, OnProcessExit
# from launch.actions import ExecuteProcess, RegisterEventHandler,LogInfo
# 获取功能包下 share 目录路径-------
# from ament_index_python.packages import get_package_share_directory

def generate_launch_description():
    t1 = Node(package="turtlesim",executable="turtlesim_node",name="t1")
    t2 = Node(package="turtlesim",executable="turtlesim_node",name="t2")
    return LaunchDescription([t1,t2])

xml

<launch>
    <node pkg="turtlesim" exec="turtlesim_node" name="t1"/>
    <node pkg="turtlesim" exec="turtlesim_node" name="t2"/>
</launch>

yaml

launch:
- node:
    pkg: "turtlesim"
    exec: "turtlesim_node"
    name: "t1"
- node:
    pkg: "turtlesim"
    exec: "turtlesim_node"
    name: "t2"

yaml 卡缩进

python

node

配置

Cmake 和 C++配置相同

setup.py 配置

        ('share/' + package_name, ['launch/py/py01_hello_world.launch.py']),
        ('share/' + package_name, ['launch/xml/xml01_hello_world.launch.xml']),
        ('share/' + package_name, ['launch/yaml/yaml01_hello_world.launch.yaml']),

最终效果

from setuptools import find_packages, setup

package_name = 'py01_launch'

setup(
    name=package_name,
    version='0.0.0',
    packages=find_packages(exclude=['test']),
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
        ('share/' + package_name, ['launch/py/py01_hello_world.launch.py']),
        ('share/' + package_name, ['launch/xml/xml01_hello_world.launch.xml']),
        ('share/' + package_name, ['launch/yaml/yaml01_hello_world.launch.yaml']),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='herrscher',
    maintainer_email='herrscher@todo.todo',
    description='TODO: Package description',
    license='TODO: License declaration',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
        ],
    },
)

但是酱紫配置非常麻烦,可以通过 glob 直接对全体符合条件的进行配置

from glob import glob
···
        ('share/' + package_name, glob("launch/py/*.launch.py")),
        ('share/' + package_name, glob("launch/xml/*.launch.xml")),
        ('share/' + package_name, glob("launch/yaml/*.launch.yaml")),


python 实现
from launch import LaunchDescription
from launch_ros.actions import Node
# 封装终端指令相关类--------------
# from launch.actions import ExecuteProcess
# from launch.substitutions import FindExecutable
# 参数声明与获取-----------------
# from launch.actions import DeclareLaunchArgument
# from launch.substitutions import LaunchConfiguration
# 文件包含相关-------------------
# from launch.actions import IncludeLaunchDescription
# from launch.launch_description_sources import PythonLaunchDescriptionSource
# 分组相关----------------------
# from launch_ros.actions import PushRosNamespace
# from launch.actions import GroupAction
# 事件相关----------------------
# from launch.event_handlers import OnProcessStart, OnProcessExit
# from launch.actions import ExecuteProcess, RegisterEventHandler,LogInfo
# 获取功能包下 share 目录路径-------
from ament_index_python.packages import get_package_share_directory
import os

"""
    节点参数:
        package: 被执行的程序所在的功能包
        executable: 被执行的程序
        name: 节点名称
        namespace: 节点命名空间
        remappings: 话题重映射
        arguments: 传参
        ros_arguments: 传参
        exec_name: 设置程序标签
        parameters: 节点参数
        respawn: 是否重启
"""
def generate_launch_description():
    turtle1 = Node(
        package="turtlesim",
        executable="turtlesim_node",
        exec_name="my_label",
        ros_arguments=["--remap","__ns:=/t2"]
        # ros2 run turtlesim turtlesim_node --ros-args --remap __ns:=/t2
    )
    turtle2 = Node(
        package="turtlesim",
        executable="turtlesim_node",
        name="haha",
        # 方式1
        # parameters=[{"background_r": 255, "background_g": 0, "background_b": 0}]
        # 方式2,通过 yaml 文件绝对路径传参
        # parameters=["/home/herrscher/ws02_tools/install/cpp01_launch/share/cpp01_launch/config/haha.yaml"]
        # 方式3,动态获取
        parameters=[os.path.join(get_package_share_directory("cpp01_launch"), "config", "haha.yaml")],
    )
    return LaunchDescription([turtle2])

用系统工具创建 yaml 文件

ros2 param dump haha --output-dir src/cpp01_launch/config

配置 Cmake
在 install 中加入config

install(DIRECTORY launch config DESTINATION share/${PROJECT_NAME})

获取 yaml 在 install 下的路径
~/工作空间/install/功能包/share/功能包/config/xx.yaml

执行指令

rt LaunchDescription
from launch_ros.actions import Node
# 封装终端指令相关类--------------
from launch.actions import ExecuteProcess
from launch.substitutions import FindExecutable
# 参数声明与获取-----------------
# from launch.actions import DeclareLaunchArgument
# from launch.substitutions import LaunchConfiguration
# 文件包含相关-------------------
# from launch.actions import IncludeLaunchDescription
# from launch.launch_description_sources import PythonLaunchDescriptionSource
# 分组相关----------------------
# from launch_ros.actions import PushRosNamespace
# from launch.actions import GroupAction
# 事件相关----------------------
# from launch.event_handlers import OnProcessStart, OnProcessExit
# from launch.actions import ExecuteProcess, RegisterEventHandler,LogInfo
# 获取功能包下 share 目录路径-------
# from ament_index_python.packages import get_package_share_directory

def generate_launch_description():
    
    turtle = Node(
        package="turtlesim",
        executable="turtlesim_node"
    )

    # 封装指令
    cmd = ExecuteProcess(
        # cmd=["ros2 topic echo /turtle1/pose"],
        # cmd=["ros2 topic","echo","/turtle1/pose"],
        cmd=[FindExecutable(name="ros2"),"topic","echo","/turtle1/pose"],
        output="both",
        shell=True
    )

    return LaunchDescription([turtle, cmd])

参数设置

from launch import LaunchDescription
from launch_ros.actions import Node
# 封装终端指令相关类--------------
# from launch.actions import ExecuteProcess
# from launch.substitutions import FindExecutable
# 参数声明与获取-----------------
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
# 文件包含相关-------------------
# from launch.actions import IncludeLaunchDescription
# from launch.launch_description_sources import PythonLaunchDescriptionSource
# 分组相关----------------------
# from launch_ros.actions import PushRosNamespace
# from launch.actions import GroupAction
# 事件相关----------------------
# from launch.event_handlers import OnProcessStart, OnProcessExit
# from launch.actions import ExecuteProcess, RegisterEventHandler,LogInfo
# 获取功能包下 share 目录路径-------
# from ament_index_python.packages import get_package_share_directory

def generate_launch_description():
    # 声明参数
    bg_r = DeclareLaunchArgument(name="backg_r",default_value="255")
    bg_g = DeclareLaunchArgument(name="backg_g",default_value="255")
    bg_b = DeclareLaunchArgument(name="backg_b",default_value="255")
    turtle = Node(
        package="turtlesim",
        executable="turtlesim_node",
        parameters=[{"background_r": LaunchConfiguration("backg_r"),
                     "background_g": LaunchConfiguration("backg_g"),
                     "background_b": LaunchConfiguration("backg_b")}],
    )
    return LaunchDescription([bg_r, bg_g, bg_b, turtle])

启动时可以直接对声明的参数调参

ros2 launch cpp01_launch py04_args.launch.py backg_r:=255 backg_g:=0 backg_b:=0

文件包含

from launch import LaunchDescription
from launch_ros.actions import Node
# 封装终端指令相关类--------------
# from launch.actions import ExecuteProcess
# from launch.substitutions import FindExecutable
# 参数声明与获取-----------------
# from launch.actions import DeclareLaunchArgument
# from launch.substitutions import LaunchConfiguration
# 文件包含相关-------------------
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
# 分组相关----------------------
# from launch_ros.actions import PushRosNamespace
# from launch.actions import GroupAction
# 事件相关----------------------
# from launch.event_handlers import OnProcessStart, OnProcessExit
# from launch.actions import ExecuteProcess, RegisterEventHandler,LogInfo
# 获取功能包下 share 目录路径-------
from ament_index_python.packages import get_package_share_directory
import os

def generate_launch_description():
    include = IncludeLaunchDescription(
        launch_description_source=PythonLaunchDescriptionSource(
            launch_file_path=os.path.join(
                get_package_share_directory("cpp01_launch"),
                "launch/py",
                "py04_args.launch.py"
            )
        ),launch_arguments=[("backg_r","255"),
                             ("backg_g","100"),
                             ("backg_b","200")
                            ]
    )
    return LaunchDescription([include])

分组实现

from launch import LaunchDescription
from launch_ros.actions import Node
# 封装终端指令相关类--------------
# from launch.actions import ExecuteProcess
# from launch.substitutions import FindExecutable
# 参数声明与获取-----------------
# from launch.actions import DeclareLaunchArgument
# from launch.substitutions import LaunchConfiguration
# 文件包含相关-------------------
# from launch.actions import IncludeLaunchDescription
# from launch.launch_description_sources import PythonLaunchDescriptionSource
# 分组相关----------------------
from launch_ros.actions import PushRosNamespace
from launch.actions import GroupAction
# 事件相关----------------------
# from launch.event_handlers import OnProcessStart, OnProcessExit
# from launch.actions import ExecuteProcess, RegisterEventHandler,LogInfo
# 获取功能包下 share 目录路径-------
# from ament_index_python.packages import get_package_share_directory

def generate_launch_description():
    t1 = Node(package="turtlesim",
              executable="turtlesim_node",
              name="t1")
    t2 = Node(package="turtlesim",
              executable="turtlesim_node",
              name="t2")
    t3 = Node(package="turtlesim",
              executable="turtlesim_node",
              name="t3")
    
    g1 = GroupAction(actions=[PushRosNamespace("g1"),t1,t2])
    g2 = GroupAction(actions=[PushRosNamespace("g2"),t3])

    return LaunchDescription([g1,g2])

xml,yaml

<launch>
    <!-- <node
        pkg="turtlesim"
        exec="turtlesim_node"
        name="t1"
        namespace="ns_1"
        exec_name="my_label_xxxx"
        respawn="True"
    /> -->
    <!-- <node pkg="turtlesim" exec="turtlesim_node" name="t1">
        <param name="background_r" value="100"/>
        <param name="background_g" value="50"/>
        <param name="background_b" value="90"/>
        <param from="$(find-pkg-share cpp01_launch)/config/xixi.yaml"/>
    </node>  -->

    <node pkg="turtlesim" exec="turtlesim_node" name="t1" ros_args="--remap __ns:=/xxx/yyy"/>
    
</launch>
launch:
- node:
    pkg: "turtlesim"
    exec: "turtlesim_node"
    name: "t1"
    namespace: "ns"
    exec_name: "my_yaml"
    respawn: "true"
    param: 
    # - 
    #     name: "background_r"
    #     value: 100
    # -
    #     name: "background_g"
    #     value: 10
    # -
    #     name: "background_b"
    #     value: 10
    -
        from: "$(find-pkg-share cpp01_launch)/config/gaga.yaml"
    ros_args: "--remap __ns:=/ns"
    # args: "--ros-args --remap __ns:=/ns"

这里设置了命名空间,在导入 yaml 时需要加上命名空间

/ns/t1:
  ros__parameters:
    background_b: 0
    background_g: 0
    background_r: 255
    qos_overrides:
      /parameter_events:
        publisher:
          depth: 1000
          durability: volatile
          history: keep_last
          reliability: reliable
    use_sim_time: false

执行指令

<launch>
    <!-- 执行终端指令 -->
     <executable cmd="ros2 run turtlesim turtlesim_node" output="both"/>
</launch>
launch:
- executable:
    cmd: "ros2 run turtlesim turtlesim_node"
    output: "both"

参数设置

<launch>
    <arg name="bg_r" default="100"/>
    <node pkg="turtlesim" exec="turtlesim_node">
        <param name="background_r" value="$(var bg_r)" />
    </node>
</launch>
launch:
- arg:
    name: "bg_b"
    default: "0"
- node:
    pkg: "turtlesim"
    exec: "turtlesim_node"
    param: 
    - 
      name: "background_b"
      value: "$(var bg_b)"

分组设置

<launch>
    <group>
        <push-ros-namespace namespace="g1"/>
        
        <!-- 设置被包含节点 -->
         <node pkg="turtlesim" exec="turtlesim_node" name="t1"/>
         <node pkg="turtlesim" exec="turtlesim_node" name="t2"/>
    </group>

    <group>
        <push-ros-namespace namespace="g2"/>
        
        <!-- 设置被包含节点 -->
         <node pkg="turtlesim" exec="turtlesim_node" name="t3"/>
    </group>
</launch>
launch:
- group:
    - push-ros-namespace:
        namespace: "g1"
    - node:
        pkg: "turtlesim"
        exec: "turtlesim_node"
        name: "txxx1"
    - node:
        pkg: "turtlesim"
        exec: "turtlesim_node"
        name: "txxx2"
- group:
    - push-ros-namespace:
        namespace: "g2"
    - node:
        pkg: "turtlesim"
        exec: "turtlesim_node"
        name: "txxx3"

文件包含

<launch>
    <!-- 动态导入参数 -->
     <let name="bg_r" value="100"/>
    <!-- 文件包含 -->
     <include file="$(find-pkg-share cpp01_launch)/launch/xml/xml04_args.launch.xml"/>
</launch>
launch:
- let:
    name: "bg_b"
    value: "100"
- include:
    file: "$(find-pkg-share cpp01_launch)/launch/yaml/yaml04_args.launch.yaml"

rosbag2

新建功能包
C++ 依赖rclcpp rosbag2_cpp geometry_msgs
Python 依赖rclpy rosbag2_py geometry_msgs

命令行工具

新建 bag 文件夹

ros2 bag record /turtle1/cmd_vel -o bag_cmd

录制 /turtle1/cmd_vel 话题,输出名称为 bag_cmd

ros2 bag play bag_cmd

回放 bag_cmd

ros2 bag info bag_cmd

获取相关消息

C++

#include "rclcpp/rclcpp.hpp"
#include "geometry_msgs/msg/twist.hpp"
#include "rosbag2_cpp/writer.hpp"
    
class SimpleBagRecorder: public rclcpp::Node{
public:
  SimpleBagRecorder():Node("simple_bag_recorder_node_cpp"){
    RCLCPP_INFO(this->get_logger(),"消息录制对象创建");
    // 创建录制对象
    writer_ = std::make_unique<rosbag2_cpp::Writer>();
    // 设置存储路径
    writer_->open("my_bag");
    // 写数据
    sub_ = this->create_subscription<geometry_msgs::msg::Twist>("/turtle1/cmd_vel",10,std::bind(&SimpleBagRecorder::do_write_msg,this,std::placeholders::_1));
  }
private:
void do_write_msg(std::shared_ptr<rclcpp::SerializedMessage> msg){
  //write(std::shared_ptr<rclcpp::SerializedMessage> message, 
  // const std::string &topic_name, 
  // const std::string &type_name, 
  // const rclcpp::Time &time)
  writer_->write(msg,"/turtle1/cmd_vel","geometry_msgs/msg/Twist",this->now());
  RCLCPP_INFO(this->get_logger(),"写入数据成功");
}
  std::unique_ptr<rosbag2_cpp::Writer> writer_;
  rclcpp::Subscription<geometry_msgs::msg::Twist>::SharedPtr sub_;
};
    
int main(int argc, char const *argv[])
{
  rclcpp::init(argc,argv);
  rclcpp::spin(std::make_shared<SimpleBagRecorder>());
  rclcpp::shutdown();
  return 0;
}

回放

#include "rclcpp/rclcpp.hpp"
#include "rosbag2_cpp/reader.hpp"
#include "geometry_msgs/msg/twist.hpp"
    
class SimpleBagPlayer: public rclcpp::Node{
public:
  SimpleBagPlayer():Node("simple_bag_player_node_cpp"){
    RCLCPP_INFO(this->get_logger(),"消息回放对象创建");
    // 创建回放对象
    reader_ = std::make_unique<rosbag2_cpp::Reader>();
    // 设置被读取文件
    reader_->open("my_bag");
    // 读取数据
    while ( reader_->has_next()){
      auto twist = reader_->read_next<geometry_msgs::msg::Twist>();
      RCLCPP_INFO(this->get_logger(),"线速度:%.2f,角速度:%.2f",twist.linear.x,twist.linear.z);
    }

    reader_->close();    
  }
private:
  std::unique_ptr<rosbag2_cpp::Reader> reader_;
};
    
int main(int argc, char const *argv[])
{
  rclcpp::init(argc,argv);
  rclcpp::spin(std::make_shared<SimpleBagPlayer>());
  rclcpp::shutdown();
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值