ROS实践笔记13

ROS运行管理

ROS元功能包

在ROS中,提供了一种方式可以将不同的功能包打包成一个功能包,当安装某个功能模块时,直接调用打包后的功能包即可,该包又称之为元功能包(metapackage)。

概念:

MetaPackage是Linux的一个文件管理系统的概念。是ROS中的一个虚包,里面没有实质性的内容,但是它依赖了其他的软件包,通过这种方法可以把其他包组合起来,我们可以认为它是一本书的目录索引,告诉我们这个包集合中有哪些子包,并且该去哪里下载。

作用:

方便用户的安装,我们只需要这一个包就可以把其他相关的软件包组织到一起安装了。

实现:

创建元功能包,可以不添加任何依赖

修改package.xml ,内容如下:

<exec_depend>plumbing_pub_sub</exec_depend>
<exec_depend>plumbing_server_client</exec_depend>
<exec_depend>plumbing_param_server</exec_depend>
<!-- 说明依赖于plumbing_pub_sub,plumbing_server_client,plumbing_param_server三个功能包建立元功能包 -->


 <export>
   <metapackage />
 </export>
 <!-- 在文件末尾 -->

修改 CMakeLists.txt,内容如下:

# 首先将CMakeLists.txt文件仅保留前三行有意义的代码,其余均删除

cmake_minimum_required(VERSION 3.0.2)
project(plumbing_pkg)
find_package(catkin REQUIRED)
# plumbing_pkg是元功能包创建时的名称
# 写入下面的代码即可
# 代码之间可能不能有空行或者注释,没有检验
catkin_metapackage()

ROS节点运行管理launch文件

  1. launch文件标签之launch

    launch 标签是所有 launch 文件的根标签,充当其他标签的容器。

    1. 属性

      deprecated = "弃用声明"

      使用方法:

      <launch deprecated="此文件已经弃用">
          <node pkg="turtlesim" type="turtlesim_node"     name="myTurtle" output="screen" />
          <node pkg="turtlesim" type="turtle_teleop_key"  name="myTurtleContro" output="screen" />
      </launch>
      

      继续使用该launch文件会引起警告

    2. 子级标签

      所有其它标签都是launch的子级标签

  2. launch文件标签之node

    node 标签用于指定 ROS 节点,是最常见的标签,需要注意的是: roslaunch 命令不能保证按照 node 的声明顺序来启动节点(节点的启动是多进程的)

    1. 属性

      1. pkg="包名"

        节点所属的功能包。

      2. type="nodeType"

        节点在CMakeList.txt文件中映射的名称。

      3. name="nodeName"

        节点启动后在rosnode list中显示的名字,不需要与初始化时相同

      4. args="xxx xxx xxx"(可选)

        将参数传送给节点,相当于argc和argv

      5. machine="机器名" (可选)

        在指定机器启动节点

      6. respawn="true | false" (可选)

        如果节点退出,是否自动重启,默认为false

      7. respawn_delay=" N" (可选)

        如果 respawn 为 true, 那么延迟 N 秒后启动节点

      8. required="true | false" (可选)

        该节点是否必须,如果为 true,那么如果该节点退出,将杀死整个 roslaunch,默认为false

      9. ns="xxx" (可选)

        在指定命名空间 xxx 中启动节点,可以避免重名问题

      10. clear_params="true | false" (可选)

        在启动前,删除节点的私有空间的所有参数 (慎用)

      11. output="log | screen" (可选)

        日志发送目标,可以设置为 log 日志文件,或 screen 屏幕,默认是 log

    2. 子级标签

      1. env 环境变量设置
      2. remap 重映射节点名称
      3. rosparam 参数设置
      4. rosparam 参数设置
  3. launch文件标签之include

    include 标签用于将另一个 xml 格式的 launch 文件导入到当前文件

    1. 属性

      1. file="$(find 包名)/xxx/xxx.launch"

        表示需要导入launch文件的位置

        用法:

        <launch>
            <include file = "$(find plumbing_test)/launch/plumbing_launch.launch"/>
            <!--plumbing_test是包含需要导入的launch的功能包的包名,plumbing_launch.launch 是需要导入的launch文件名-->
        </launch>
        
      2. ns="xxx" (可选)

        表示从指定命名空间导入launch文件

    2. 子级标签

      1. env 环境变量设置
      2. arg 将参数传递给被包含的文件
  4. launch文件标签之remap

    用于话题重命名

    1. 属性

      1. from="xxx"

        原始的话题名称

      2. to="yyy"

        目标名称

      用法:

      <node pkg = "turtlesim" type = "turtlesim_node" name = "turtle1" output = "screen">
          <remap from = "/turtle1/cmd_vel" to = "/cmd_vel" />
      </node>
      <!--注意要将第一个node标签最后的'/'删去 -->
      
    2. 子级标签

  5. launch文件标签之param

    param 标签主要用于在参数服务器上设置参数,参数源可以在标签中通过 value 指定,也可以通过外部文件加载,在 node 标签中时,相当于私有命名空间。

    1. 属性

      1. name="命名空间/参数名"

        参数名称,可以包含命名空间

      2. value="xxx" (可选)

        定义参数值,如果此处省略,必须指定外部文件作为参数源

      3. type="str | int | double | bool | yaml" (可选)

        指定参数类型,如果未指定,roslaunch 会尝试确定参数类型,规则如下:

        • 包含 '.' 的数字解析为浮点型,否则为整型
        • "true" 和 "false" 是 bool 值(不区分大小写)
        • 其他是字符串
          用法:
      <!--格式一:在node外使用 -->
      <param name = "param_A" type = "int" value = "100"/>
      <!--格式二:在node内使用 -->
      <node pkg = "turtlesim" type = "turtlesim_node" name = "turtle1" output = "screen">
          <param name = "param_A" type = "int" value = "100"/>
      </node>
      <!--创建出来的节点会有一个与 turtle1 相同的前缀-->
      
    2. 子级标签

  6. launch文件标签之rosparam

    rosparam 标签可以从 YAML 文件导入参数,或将参数导出到 YAML 文件,也可以用来删除参数,rosparam 标签在 node 标签中时被视为私有。

    1. 属性

      1. command="load | dump | delete" (可选,默认 load)

        对应加载,导出,删除

      2. file="$(find xxxxx)/xxx/yyy...."

        加载或者导出到yaml文件

      3. param="参数名称"

      4. ns="命名空间" (可选)

      用法:

      <!--格式一:在node外使用 -->
      <!--加载参数 -->
      <rosparam command = "load" file = "$(find xxxxx)/xxx/yyy.yaml"/>
      <!--导出参数,launch文件执行时,会优先执行导出操作,建议放在其他launch文件中 -->
      <rosparam command = "dump" file = "$(find xxxxx)/xxx/yyy.yaml"/>
      <!--删除参数 -->
      <rosparam command = "delete" param = "xxx"/>        
      <!--格式二:在node内使用 -->
      <node pkg = "turtlesim" type = "turtlesim_node" name = "turtle1" output = "screen">
          <rosparam command = "load" file = "$(find xxxxx)/xxx/yyy.yaml"/>
      </node>
      <!--创建出来的参数名会有一个与 turtle1 相同的前缀-->
      
    2. 子级标签

  7. launch文件标签之group

    group 标签可以对节点分组,具有 ns 属性,可以让节点归属某个命名空间

    1. 属性

      1. ns="名称空间" (可选)
      2. clear_params="true | false" (可选)
        启动前,是否删除组名称空间的所有参数(慎用....此功能危险)

      用法:

      <group ns = "first_group">
          <node pkg = "turtlesim" type = "turtlesim_node" name = "turtle1" output = "screen">
      </group>
      
    2. 子级标签
      除了launch 标签外的其他标签

  8. launch文件标签之arg
    arg 标签是用于动态传参,类似于函数的参数,可以增强launch文件的灵活性

    1. 属性
      1. name="参数名称"
      2. default="默认值" (可选)
      3. value="数值" (可选)
      4. doc="描述"

    用法:

    <arg name = "car_length" default = "0.5">
    <param name = "param_A"  value = "$(car_length)"/>
    <param name = "param_B"  value = "$(car_length)"/>
    <param name = "param_C"  value = "$(car_length)"/>
    
    1. 子级标签

ROS重名问题

  • ROS工作空间覆盖

    所谓工作空间覆盖,是指不同工作空间中,存在重名的功能包的情形。

    ROS 开发中,会自定义工作空间且自定义工作空间可以同时存在多个,可能会出现一种情况: 虽然特定工作空间内的功能包不能重名,但是自定义工作空间的功能包与内置的功能包可以重名或者不同的自定义的工作空间中也可以出现重名的功能包,那么调用该名称功能包时,会调用哪一个呢?比如:自定义工作空间A存在功能包 turtlesim,自定义工作空间B也存在功能包 turtlesim,当然系统内置空间也存在turtlesim,如果调用turtlesim包,会调用哪个工作空间中的呢?

    设置:在任意文件夹下都可以启动demo01_ws下的功能包

    在主目录下按Ctrl+H,发现bashrc文件,在~/.bashrc文件下追加

    source /home/用户/路径/工作空间A/devel/setup.bash
    

    ROS中如果在配置文件中刷新了多个环境变量,后刷新的优先级更高。

  • 节点名称重名

场景:ROS 中创建的节点是有名称的,C++初始化节点时通过API:ros::init(argc,argv,"xxxx");来定义节点名称,在Python中初始化节点则通过 rospy.init_node("yyyy") 来定义节点名称。在ROS的网络拓扑中,是不可以出现重名的节点的,因为假设可以重名存在,那么调用时会产生混淆,这也就意味着,不可以启动重名节点或者同一个节点启动多次,的确,在ROS中如果启动重名节点的话,之前已经存在的节点会被直接关闭,但是如果有这种需求的话,怎么优化呢?

在ROS中给出的解决策略是使用命名空间或名称重映射。

命名空间就是为名称添加前缀,名称重映射是为名称起别名。这两种策略都可以解决节点重名问题,两种策略的实现途径有多种:

  1. rosrun 命令

    1. rosrun设置命名空间

      语法: rosrun 包名 节点名 __ns:=新名称

      rosrun turtlesim turtlesim_node __ns:=/xxx
      # 节点名后有空格
      
    2. rosrun名称重映射

      语法: rosrun 包名 节点名 __name:=新名称

      rosrun turtlesim  turtlesim_node __name:=t1
      
    3. rosrun命名空间与名称重映射叠加

      语法: rosrun 包名 节点名 __ns:=新名称 __name:=新名称

      rosrun turtlesim turtlesim_node __ns:=/xxx __name:=tn
      
  2. launch 文件
    实现

      <launch>
          <!--原来的 -->
          <node pkg="turtlesim" type="turtlesim_node" name="turtlesim"/>
          <!--设置命名空间 -->
          <node pkg="turtlesim" type="turtlesim_node" name="turtlesim" ns="hello"/>
          <!--名称重映射 -->
          <node pkg="turtlesim" type="turtlesim_node" name="t1" />
          <!--命名空间与名称重映射叠加 -->
          <node pkg="turtlesim" type="turtlesim_node" name="t1" ns="hello"/>
      </launch>
    
  3. 编码实现

    1. c++名称重映射
      ros::init(argc,argv,"plumbing_node",ros::init_options::AnonymousName);
      
    2. c++命名空间
      std::map<std::string, std::string> map;
      map["__ns"] = "name_space";
      ros::init(map,"plumbing_node");
      
    3. python名称重映射
      rospy.init_node("plumbing_node",anonymous=True)
      
  • 话题名称重名

在ROS中节点名称可能出现重名的情况,同理话题名称也可能重名。

在 ROS 中节点终端,不同的节点之间通信都依赖于话题,话题名称也可能出现重复的情况,这种情况下,系统虽然不会抛出异常,但是可能导致订阅的消息非预期的,从而导致节点运行异常。这种情况下需要将两个节点的话题名称由相同修改为不同。

又或者,两个节点是可以通信的,两个节点之间使用了相同的消息类型,但是由于,话题名称不同,导致通信失败。这种情况下需要将两个节点的话题名称由不同修改为相同。

在实际应用中,按照逻辑,有些时候可能需要将相同的话题名称设置为不同,也有可能将不同的话题名设置为相同。在ROS中给出的解决策略与节点名称重命类似,也是使用名称重映射或为名称添加前缀。根据前缀不同,有全局、相对、和私有三种类型之分。

1. 全局(参数名称直接参考ROS系统,与节点命名空间平级)
2. 相对(参数名称参考的是节点的命名空间,与节点名称平级)
3. 私有(参数名称参考节点名称,是节点名称的子级)

名称重映射是为名称起别名,为名称添加前缀,该实现比节点重名更复杂些,不单是使用命名空间作为前缀、还可以使用节点名称最为前缀。两种策略的实现途径有多种:

  1. rosrun命令

    rosrun设置话题重映射

    语法: rorun 包名 节点名 话题名:=新话题名称

    rosrun teleop_twist_keyboard teleop_twist_keyboard.py /cmd_vel:=/turtle1/cmd_vel
    
  2. launch 文件

    launch 文件设置话题重映射语法:

    <node pkg="xxx" type="xxx" name="xxx">
    <remap from="原话题" to="新话题" />
    </node>
    
    <launch>
    <node pkg="turtlesim" type="turtlesim_node" name="t1" />
    <node pkg="teleop_twist_keyboard" type="teleop_twist_keyboard.py" name="key">
        <remap from="/cmd_vel" to="/turtle1/cmd_vel" />
    </node>
    </launch>
    
  3. 编码实现

    c++

    1. 1.初始化节点设置一个节点名称
    ros::init(argc,argv,"hello")
    
    1. 设置不同类型的话题
    2. 启动节点时,传递一个 __ns:= xxx
    3. 节点启动后,使用 rostopic 查看话题信息
    • 全局名称

      格式:以/开头的名称,和节点名称无关

      示例1:ros::Publisher pub = nh.advertise<std_msgs::String>("/chatter",1000);

      示例2:ros::Publisher pub = nh.advertise<std_msgs::String>("/chatter/money",1000);

    • 相对名称

      格式:非/开头的名称,参考命名空间(与节点名称平级)来确定话题名称

      示例1:ros::Publisher pub = nh.advertise<std_msgs::String>("chatter",1000);

      示例2:ros::Publisher pub = nh.advertise<std_msgs::String>("chatter/money",1000);

    • 私有名称

      格式:以~开头的名称

      示例1:

      ros::NodeHandle nh("~");
      
      ros::Publisher pub = nh.advertise<std_msgs::String>("chatter",1000);
      

      示例2:

      ros::NodeHandle nh("~");
      
      ros::Publisher pub = nh.advertise<std_msgs::String>("chatter/money",1000);
      
    • 注意:
      当使用~,而话题名称有时/开头时,那么话题名称是全局的

    python

    • 全局名称

      格式:以/开头的名称,和节点名称无关

      示例:pub = rospy.Publisher("/chatter",String,queue_size=1000)

    • 相对名称

      格式:非/开头的名称,参考命名空间(与节点名称平级)来确定话题名称

      示例:pub = rospy.Publisher("chatter",String,queue_size=1000)

    • 私有名称

      格式:以~开头的名称

      示例:pub = rospy.Publisher("~chatter",String,queue_size=1000)

  • 参数名称重名

    在ROS中节点名称话题名称可能出现重名的情况,同理参数名称也可能重名。

    在ROS中节点名称话题名称可能出现重名的情况,同理参数名称也可能重名。

    关于参数重名的处理,没有重映射实现,为了尽量的避免参数重名,都是使用为参数名添加前缀的方式,实现类似于话题名称,有全局、相对、和私有三种类型之分。

    设置参数的方式也有三种:

    1. rosrun 命令

      rosrun 在启动节点时,也可以设置参数:

      语法: rosrun 包名 节点名称 _参数名:=参数值

      例如:

      rosrun turtlesim turtlesim_node _A:=100
      

      得到:

      /turtlesim/A
      /turtlesim/background_b
      /turtlesim/background_g
      /turtlesim/background_r
      
      
    2. launch 文件
      例如:

    <launch>
        <!--全局-->
        <param name="p1" value="100" />
        <!--私有-->
        <node pkg="turtlesim" type="turtlesim_node" name="t1">
            <param name="p2" value="100" />
        </node>
    </launch>
    

    得到:

    /p1
    /t1/p1
    
    1. 编码实现

      c++

      ros::param::set("/set_A",100); //全局,和命名空间以及节点名称无关
      ros::param::set("set_B",100); //相对,参考命名空间
      ros::param::set("~set_C",100); //私有,参考命名空间与节点名称
      
      或者
      ros::NodeHandle nh;
      nh.setParam("/nh_A",100); //全局,和命名空间以及节点名称无关
      
      nh.setParam("nh_B",100); //相对,参考命名空间
      
      ros::NodeHandle nh_private("~");
      nh_private.setParam("nh_C",100);//私有,参考命名空间与节点名称
      
      

      python

      rospy.set_param("/py_A",100)  #全局,和命名空间以及节点名称无关
      rospy.set_param("py_B",100)  #相对,参考命名空间
      rospy.set_param("~py_C",100)  #私有,参考命名空间与节点名称
      

ROS分布式系统

ROS是一个分布式计算环境。一个运行中的ROS系统可以包含分布在多台计算机上多个节点。根据系统的配置方式,任何节点可能随时需要与任何其他节点进行通信。

因此,ROS对网络配置有某些要求:

  • 所有端口上的所有机器之间必须有完整的双向连接。
  • 每台计算机必须通过所有其他计算机都可以解析的名称来公告自己。

实现:

  1. 准备

    先要保证不同计算机处于同一网络中,最好分别设置固定IP,如果为虚拟机,需要将网络适配器改为桥接模式;

    固定IP需要进入网络设置把IPv4改为手动

    在终端中输入ifconfig,将找到的地址,子网掩码,网关分别填入对应位置

    如果ifconfig未安装

    sudo apt install net-tools
    
  2. 配置文件修改

    分别修改不同计算机的 /etc/hosts 文件,在该文件中加入对方的IP地址和计算机名:

     主机端:
    
    从机的IP    从机计算机名
    
     从机端:
    
    主机的IP    主机计算机名
    

    在终端中调用hostname可以得到主机名称

    修改host文件

    cd /etc
    
    sudo gedit host
    
  3. 配置主机IP

    ~/.bashrc 追加:

    export ROS_MASTER_URI=http://主机IP:11311
    export ROS_HOSTNAME=主机IP
    

    例如 :主机IP为192.168.216

    export ROS_MASTER_URI=http://192.168.216:11311
    export ROS_HOSTNAME=192.168.216
    
  4. 配置从机IP

    配置从机的 IP 地址,从机可以有多台,每台都做如下设置:

    ~/.bashrc 追加:

    export ROS_MASTER_URI=http://主机IP:11311
    export ROS_HOSTNAME=从机IP
    
  5. 测试

    1. 主机启动 roscore(必须)
    2. 主机启动订阅节点,从机启动发布节点,测试通信是否正常
    3. 反向测试,主机启动发布节点,从机启动订阅节点,测试通信是否正常
posted @   一心如旧  阅读(138)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示