ROS笔记-基于melodic版本

ROS笔记

一、基础概念和环境设置

  • ROS安装

    • melodic安装教程

    • //打开三个终端,分别运行以下命令以测试ROS是否安装成功
      roscore
      rosrun turtlesim turtlesim_node
      rosrun turtlesim turtle_teleop_key
  • 创建工作空间

    • mkdir -p catkin_ws/src
      cd catkin_ws
      catkin_make
      source devel/setup.bash
      ​
      // 永久添加环境变量
      gedit ~/.bashrc
      //在打开的文件最下面添加下面代码
      source ~/catkin_ws/devel/setup.bash
      //返回终端重新source
      source ~/.bashrc
  • 创建功能包

    • cd /path/to/your/catkin_ws/src      //进入src空间
      ​
      catkin_create_pkg <package_name> [depend1] [depend2] [depend3]     //创建功能包和添加依赖
      ​
      ​
      cd /path/to/your/catkin_ws     //编译
      catkin_make
  • 第一个程序Hello World

    • catkin_create_pkg test roscpp rospy std_msgs //创建名为test的功能包
    • cd test   //进入test功能包
      mkdir scripts //创建文件夹,用来放python文件
      cd scripts
      touch hello.py  //创建python文件,代码在下方
      cd ..  //回到catkin_ws
      catkin_make  //编译
    • #! /usr/bin/env python
      ​
      import rospy
      ​
      if __name__ == "__main__":
          rospy.init_node("Hello")
          rospy.loginfo("Hello World!")
    • roscore 
      rosrun test hello.py   //在两个终端打开,分别运行
  • launch文件

    • launch文件帮助我们一次启动多个节点

    • 在test功能包下,建立launch文件夹,建立turtle.launch文件

    • <launch>
        <node name="turtlesim_node" pkg="turtlesim" type="turtlesim_node" />
        <node name="turtle_teleop_key" pkg="turtlesim" type="turtle_teleop_key" output="screen"/>
      </launch>
    • roslaunch test turtle.launch

      即可一次完成小乌龟控制程序,launch文件会自动启动roscore节点

  • 计算图

    • rqt_graph能够创建一个显示当前系统运行情况的动态图形。ROS 分布式系统中不同进程需要进行数据交互,计算图可以以点对点的网络形式表现数据交互过程

    • roslaunch test turtle.launch
      rosrun rqt_graph rqt_graph

二、ROS核心概念

  • 文件架构

    • img

    •     WorkSpace --- 自定义的工作空间
      ​
          |--- build:编译空间,用于存放CMake和catkin的缓存信息、配置信息和其他中间文件。
      ​
          |--- devel:开发空间,用于存放编译后生成的目标文件,包括头文件、动态&静态链接库、可执行文件等。
      ​
          |--- src: 源码
      ​
              |-- package:功能包(ROS基本单元)包含多个节点、库与配置文件,包名所有字母小写,只能由字母、数字与下划线组成
      ​
                  |-- CMakeLists.txt 配置编译规则,比如源文件、依赖项、目标文件
      ​
                  |-- package.xml 包信息,比如:包名、版本、作者、依赖项...(以前版本是 manifest.xml)
      ​
                  |-- scripts 存储python文件
      ​
                  |-- src 存储C++源文件
      ​
                  |-- include 头文件
      ​
                  |-- msg 消息通信格式文件
      ​
                  |-- srv 服务通信格式文件
      ​
                  |-- action 动作格式文件
      ​
                  |-- launch 可一次性运行多个节点 
      ​
                  |-- config 配置信息
      ​
              |-- CMakeLists.txt: 编译的基本配置
  • CMakeLists.txt

    • 在ROS中,每个功能包都包含一个名为 CMakeLists.txt 的文件,用于定义如何构建功能包中的节点、库和其他资源。这个文件使用CMake构建系统的语法来配置编译选项、依赖关系和其他构建信息

    • cmake_minimum_required(VERSION 2.8.3)
      project(my_package)
      ​
      ## 找到catkin并包含它的项目配置
      find_package(catkin REQUIRED COMPONENTS
        roscpp
        std_msgs
      )
      ​
      ## 声明这是一个catkin包
      catkin_package()
      ​
      ## 指定包含的头文件路径
      include_directories(
        ${catkin_INCLUDE_DIRS}
      )
      ​
      ## 编译并链接一个ROS节点
      add_executable(hello_node src/hello.cpp)
      target_link_libraries(hello_node ${catkin_LIBRARIES})
      ​
      ## 编译一个ROS库
      add_library(my_library src/my_library.cpp)
      target_link_libraries(my_library ${catkin_LIBRARIES})
      ​
      1. cmake_minimum_required:指定需要的最低CMake版本。

      2. project:定义项目名称。

      3. find_package:查找并引入所需的ROS软件包(依赖)。

      4. catkin_package:声明这是一个catkin包。

      5. include_directories:指定包含的头文件路径。

      6. add_executable:定义一个ROS节点的可执行文件。

      7. add_library:定义一个ROS库。

      可以在 add_executableadd_library 部分添加更多的源文件,配置编译选项,指定依赖关系等。在修改完 CMakeLists.txt 后,需要重新构建你的ROS工作空间,以使更改生效

  • package.xml

    • 在ROS中,每个功能包都包含一个名为 package.xml 的文件,用于描述功能包的信息、依赖关系以及其他元数据。这个文件提供了功能包的元信息,使其能够被其他ROS工具和节点正确识别和使用。

    • <?xml version="1.0"?>
      <package>
        <name>my_package</name>
        <version>0.1.0</version>
        <description>My ROS package</description>
      ​
        <maintainer email="your.email@example.com">Your Name</maintainer>
        <license>MIT</license>
      ​
        <buildtool_depend>catkin</buildtool_depend>
      ​
        <build_depend>roscpp</build_depend>
        <build_depend>std_msgs</build_depend>
      ​
        <run_depend>roscpp</run_depend>
        <run_depend>std_msgs</run_depend>
      </package>
      1. <name>:指定功能包的名称。

      2. <version>:指定功能包的版本号。

      3. <description>:提供功能包的描述信息。

      4. <maintainer>:指定维护者的姓名和电子邮件地址。

      5. <license>:指定功能包的许可证类型。

      6. <buildtool_depend>:指定构建工具的依赖关系,通常是catkin

      7. <build_depend>:指定在构建时所需的其他功能包依赖。

      8. <run_depend>:指定在运行时所需的其他功能包依赖。

      可以在 package.xml 文件中添加更多的信息,如节点的启动文件、节点参数等。在修改完 package.xml 后,确保重新构建你的ROS工作空间,以使更改生效

  • 话题通信

    • 假设我们要创建两个节点:一个发布者节点和一个订阅者节点,它们通过一个名为 "sensor_data" 的话题进行通信。发布者节点会模拟传感器数据,而订阅者节点会接收并打印这些数据

    • catkin_create_pkg communication roscpp rospy std_msgs   //新建通信功能包

      src目录下建立两个文件publisher.cpp以及subscriber.cpp

    • #include <ros/ros.h>
      #include <std_msgs/Int32.h>  // 导入需要使用的消息类型
      ​
      int main(int argc, char **argv) {
          ros::init(argc, argv, "publisher_node");  // 初始化节点
          ros::NodeHandle nh;
      ​
          ros::Publisher pub = nh.advertise<std_msgs::Int32>("sensor_data", 10);  // 创建话题发布者,发布Int32类型的消息
          ros::Rate rate(1);  // 设置发布频率为1Hz
      ​
          while (ros::ok()) {
              std_msgs::Int32 sensor_msg;
              sensor_msg.data = 42;  // 模拟传感器数据
              ROS_INFO("Publishing: %d", sensor_msg.data);
              pub.publish(sensor_msg);  // 发布传感器数据
              rate.sleep();
          }
      ​
          return 0;
      }
    • #include <ros/ros.h>
      #include <std_msgs/Int32.h>  // 导入需要使用的消息类型
      ​
      void callback(const std_msgs::Int32::ConstPtr& msg) {
          ROS_INFO("Received: %d", msg->data);  // 打印接收到的数据
      }
      ​
      int main(int argc, char **argv) {
          ros::init(argc, argv, "subscriber_node");  // 初始化节点
          ros::NodeHandle nh;
      ​
          ros::Subscriber sub = nh.subscribe("sensor_data", 10, callback);  // 创建话题订阅者,订阅Int32类型的消息
          ros::spin();  // 进入循环,等待消息到达
      ​
          return 0;
      }
  • 服务通信

    • 自定义服务消息类型

      • 进入communication功能包,创建srv文件夹以存放自定义服务通信数据类型,创建文件AddTwoInts.srv

        • int32 num1   //客户端数据
          int32 num2
          ---
          int32 sum   //服务端数据
      • 编辑配置文件

      • package.xml

        • <build_depend>message_generation</build_depend>
          <exec_depend>message_runtime</exec_depend>
      • CMakeLists.txt

        • find_package(catkin REQUIRED COMPONENTS
            roscpp
            rospy
            std_msgs
            message_generation
          )
          # 需要加入 message_generation,必须有 std_msgs
          ​
          add_service_files(
            FILES
            AddTwoInts.srv
          )  #这里的AddTwoInts.srv要和你创建的srv文件名称一致,且必须时在srv目录下,否则编译会出现问题,生成消息时依赖于 std_msgs
          ​
          generate_messages(
            DEPENDENCIES
            std_msgs
          )  #这个配置的作用是添加生成消息的依赖,默认的时候要添加std_msgs
          ​
          catkin_package(
          #  INCLUDE_DIRS include
          #  LIBRARIES demo_msg
             CATKIN_DEPENDS roscpp rosmsg rospy message_runtime
          #  DEPENDS system_lib
          )    #为catkin编译提供了依赖 message_runtime
      • 返回工作空间,catkin_make

      • 查看头文件

        • ../catkin_ws/devel/include/communication/AddTwoInts.h   //C++头文件
          ../catkin_ws/devel/lib/python3/dist-packages/communication/srv   //python文件
      • 导入

        • #include "communication/AddTwoInts.h"     //C++
          from communication.srv import AddTwoInts,AddTwoIntsRequest,AddTwoIntsResponse   //python
    • 服务通信C++实现,在communication功能包src文件夹下新建server.cpp以及client.cpp

      • #include "ros/ros.h"
        #include "communication/AddTwoInts.h" 
        ​
        bool addCallback(communication::AddTwoInts::Request& req,
                         communication::AddTwoInts::Response& res)
        {
            res.sum = req.num1 + req.num2;
            ROS_INFO("Adding %ld + %ld = %ld", (long int)req.num1, (long int)req.num2, (long int)res.sum);
            return true;
        }
        ​
        int main(int argc, char** argv)
        {
            ros::init(argc, argv, "server");
            ros::NodeHandle nh;
        ​
            ros::ServiceServer service = nh.advertiseService("AddTwoInts", addCallback);
            ROS_INFO("Ready to add two ints.");
            ros::spin();
        ​
            return 0;
        }
      • #include "ros/ros.h"
        #include "communication/AddTwoInts.h" // 根据实际路径修改
        ​
        int main(int argc, char** argv)
        {
            ros::init(argc, argv, "client");
            if (argc != 3)
            {
                ROS_INFO("Usage: add_two_ints_client X Y");
                return 1;
            }
        ​
            ros::NodeHandle nh;
            ros::ServiceClient client = nh.serviceClient<communication::AddTwoInts>("AddTwoInts");
            communication::AddTwoInts srv;  //创建请求对象
            srv.request.num1 = atoll(argv[1]);
            srv.request.num2 = atoll(argv[2]);
        ​
            if (client.call(srv))
            {
                ROS_INFO("Sum: %ld", (long int)srv.response.sum);
            }
            else
            {
                ROS_ERROR("Failed to call service add_two_ints");
                return 1;
            }
        ​
            return 0;
        }
      • 修改配置文件CMakeLists.txt

        • add_executable(server src/server.cpp)
          target_link_libraries(server ${catkin_LIBRARIES})
          ​
          add_executable(client src/client.cpp)
          target_link_libraries(client ${catkin_LIBRARIES})
          ​
          add_dependencies(server ${PROJECT_NAME}_gencpp)
          add_dependencies(client ${PROJECT_NAME}_gencpp)
      • 编译、运行节点

  • 参数服务器

    • 参数服务器是可通过网络访问的共享的多变量字典。它是节点存储参数的地方、用于配置参数、全局共享参数。参数服务器使用互联网传输,在节点管理器中运行,实现整个通信过程

    • 可以通过setparam()来上传参数

      • ros::init(argc, argv, "set_param_example");   //创建节点
        ros::NodeHandle nh;                           //创建句柄
        nh.setParam(param_name, param_value);         //上传参数
            //param_name:参数的名称,它是一个字符串,用于标识唯一的参数。通常情况下,参数名称会包括命名空间以防止冲突。
            //param_value:参数的值,可以是整数、浮点数、字符串、列表、字典等不同类型的数据。
      • nh.setParam("integer_param", 42);
        nh.setParam("float_param", 3.14);
        nh.setParam("string_param", "Hello, ROS!");
        std::vector<int> list_param = {1, 2, 3, 4, 5};
        nh.setParam("list_param", list_param);
        std::map<std::string, std::string> dict_param = {{"key1", "value1"}, {"key2", "value2"}};
        nh.setParam("dict_param", dict_param);
      • Python实现

      • rospy.init_node('set_param_example')
        rospy.set_param(param_name, param_value)
      • rospy.set_param('integer_param', 42)
        rospy.set_param('float_param', 3.14)
        rospy.set_param('string_param', 'Hello, ROS!')
        rospy.set_param('list_param', [1, 2, 3, 4, 5])
        rospy.set_param('dict_param', {'key1': 'value1', 'key2': 'value2'})
    • 通过getparam()获取参数

      • bool success = nh.getParam(param_name, param_value);
      • param_name:要获取的参数的名称,它是一个字符串,用于标识参数的唯一标识符。通常情况下,参数名称会包括命名空间以防止冲突。

        param_value:获取到的参数值,可以是整数、浮点数、字符串、列表、字典等不同类型的数据。

        success(仅C++中):用于指示是否成功获取参数的布尔值。如果获取成功,successtrue,否则为 false

      • int integer_param;    定义变量以保存参数值
        nh.getParam("integer_param", integer_param);   // 获取整数类型参数
      • param_value = rospy.get_param(param_name)   #python用法

三、ROS常用工具及函数

  • rosnode

    • rosnode list                #列出所有节点
      rosnode info node_name      #获取节点信息
      rosnode kill node_name      #终止节点
      rosnode log node_name       #节点日志
      rqt_graph                   #节点图
      rosnode ping node_name      #节点通信检查
      rosnode cleanup             #清除不可连接的节点
  • rostopic

    • rostopic bw                 #显示主题使用的带宽
      rostopic delay              #显示带有 header 的主题延迟
      rostopic echo topic_name    #打印消息到屏幕
      rostopic find               #根据类型查找主题
      rostopic hz topic_name      #显示主题的发布频率
      rostopic info topic_name    #显示主题相关信息
      rostopic list               #显示所有活动状态下的主题
      rostopic pub                #将数据发布到主题
                                  #用法:rostopic pub /主题名称 消息类型 消息内容   
                                  #rostopic pub /chatter std_msgs gagaxixi
      rostopic type               #打印主题类型
  • rosmsg

    • rosmsg show         #显示消息描述
      rosmsg info         #显示消息信息
      rosmsg list         #列出所有消息
      rosmsg md5          #显示 md5 加密后的消息
      rosmsg package      #显示某个功能包下的所有消息
      rosmsg packages     #列出包含消息的功能包
  • rosparam

    • rosparam set parameter_name parameter_value             #将参数设置到参数服务器上
      rosparam get parameter_name                             #获取参数
      rosparam load file.yaml namespace                       #file.yaml 是参数文件的路径,namespace 是一个可选的命名空间,加载到参数服务器上
      rosparam dump file.yaml namespace                       #将参数写出到外部文件
      rosparam delete parameter_name                          #删除参数
      rosparam list                                           #列出所有参数
      rosparam set namespace/parameter_name parameter_value   #参数命名空间
  • ROS初始化,rospy.init_node()ros::init()

    • ros::init(argc, argv, name, options)
      /*  argc(必需):命令行参数数量。
          argv(必需):命令行参数数组。
          name(必需):指定节点的名称。这个名称必须在整个 ROS 系统中是唯一的。
          options(可选,ros::init_options 枚举,默认为 ros::init_options::NoSigintHandler):用于指定初始化选项。可以是 ros::init_options::NoSigintHandler(禁用 SIGINT 信号处理程序)或 ros::init_options::AnonymousName(匿名节点,可以使一个节点启动多次,而不会杀死之前的节点)   */ 
    • #include <ros/ros.h>
      ​
      int main(int argc, char** argv) {
          // 初始化ROS节点
          ros::init(argc, argv, "my_node");
      ​
          // 创建ROS节点句柄
          ros::NodeHandle nh;
      ​
          // 在这里添加你的ROS节点逻辑
      ​
          // 循环等待节点结束
          ros::spin();
      ​
          return 0;
      }
  • 节点句柄ros::NodeHandle

    • ros::NodeHandle nh;  // 创建NodeHandle对象,可以使用这个 `nh` 对象来访问 ROS 系统的各种功能
  • 话题对象

    • 发布方对象

    • ros::Publisher advertise(const std::string& topic, uint32_t queue_size, bool latch = false);
      • topic:要发布消息的话题名称。

      • queue_size:发布队列的大小,用于缓存待发布的消息。当缓冲区满时,旧消息会被丢弃。

      • latch(可选,默认为 false):如果设置为 true,则会将最新发布的消息保持在话题中,新的订阅者会立即收到该消息。

      • advertise 函数返回一个 ros::Publisher 类型的对象,你可以使用这个对象来发布消息到指定的话题。

    • ros::Publisher pub = nh.advertise<std_msgs::String>("my_topic", 10);   //定义
      • ros::Publisher: 这是 ros::Publisher 类的类型,表示发布者对象的类型。ros::Publisher 用于管理发布消息到特定话题的操作。

      • pub: 这是我们给发布者对象起的名称,你可以根据需要自定义。通过 pub,我们可以在代码中引用这个发布者对象。`

      • nh: 这是 ros::NodeHandle 类的一个对象,用于与ROS系统进行交互。我们使用它来创建发布者对象。`

      • advertise<std_msgs::String>: 这是 ros::NodeHandle 类的成员函数 advertise,用于创建发布者对象。在尖括号中,我们指定了消息类型 std_msgs::String,这表示我们将发布的消息类型为字符串。

      • "my_topic": 这是话题名称,我们将在该话题上发布消息。你可以替换为你想要发布消息的话题名称。

      • 10: 这是发布队列的大小,用于在缓冲区中存储待发布的消息。当缓冲区满时,旧消息会被丢弃。这个值可以根据你的需要进行调整。

    • 订阅方对象

    • Subscriber subscribe(const std::string& topic, uint32_t queue_size,
                           void(T::*fp)(const boost::shared_ptr<M const>&), T* obj,
                           const TransportHints& transport_hints = TransportHints());
      • topic:要订阅的话题名称。

      • queue_size:订阅队列的大小,用于缓存待处理的消息。当队列满时,旧消息会被丢弃。

      • fp:指向回调函数的指针,该函数将在订阅者接收到消息时被调用。

      • obj:指向包含回调函数的对象的指针。

      • transport_hints(可选,默认为空):传输选项,用于配置订阅的传输特性。

      • subscribe 函数返回一个 ros::Subscriber 类型的对象,你可以使用这个对象来管理和操作订阅者。

    • ros::Subscriber sub = nh.subscribe("my_topic", 10, callback);
      • void callback(const std_msgs::String::ConstPtr& msg):这是回调函数,它会在订阅者接收到消息时被调用。我们使用 ROS_INFO 打印接收到的消息(或者其他处理方式)。

    • 发布消息

      • void publish(const boost::shared_ptr<M>& message, uint32_t queue_size = 0, bool latch = false);
        • message:要发布的消息,通常使用消息类型的指针,例如 boost::shared_ptr<std_msgs::String>

        • queue_size:(可选)发布队列的大小,用于缓存待发布的消息。当队列满时,旧消息会被丢弃。

        • latch:(可选,默认为 false)是否将消息保持在话题中,使新的订阅者在连接时立即接收到最新的消息

      • ros::Rate rate(10);  // 10Hz,创建一个循环,以指定的频率发布消息
        while (ros::ok()) {
                // 创建一个std_msgs::String类型的消息
                std_msgs::String msg;
                msg.data = "Hello, ROS!";
        ​
                // 发布消息
                pub.publish(msg);
        ​
                // 休眠以维持发布频率
                rate.sleep();
            }
  • 服务对象

    • 服务器对象

    • ServiceServer advertiseService(const std::string& service, bool(T::*srv_func)(MReq&, MRes&), T* obj);
      • service:要提供的服务的名称。

      • srv_func:指向服务回调函数的指针,该函数将在接收到服务请求时被调用。

      • obj:指向包含服务回调函数的对象的指针。

      • advertiseService 函数返回一个 ros::ServiceServer 类型的对象,你可以使用这个对象来管理和操作服务器。

    • ros::ServiceServer server = nh.advertiseService("my_service", callback);
    • 客户端对象

    • ServiceClient serviceClient(const std::string& service, const bool persistent = false, const M_string& header_values = M_string(), const ros::AdvertiseOptions& ops = ros::AdvertiseOptions());
      • service:要调用的服务的名称。

      • persistent:是否创建一个持久化的服务客户端。如果设置为 true,则客户端会在每次调用服务时保持连接。如果设置为 false,则每次调用服务都会创建一个新的连接。

      • header_values:可选,头部信息的键值对,用于设置服务的消息头部。

      • ops:可选,用于配置服务客户端的 ros::AdvertiseOptions

    • ros::ServiceClient client = nh.serviceClient<your_package::YourService>("my_service");
    • 服务通信

    •     your_package::YourService::Request req;
          req.input_data = "Requesting service";     //客户端组织请求数据
      ​
          your_package::YourService::Response res;
              // 发送请求并等待响应,call函数调用成功返回true
          if (client.call(req, res)) {
              ROS_INFO("Received response: %s", res.output_result.c_str());
          } else {
              ROS_ERROR("Failed to call service");
          }
      ​
          //服务器端处理请求
          bool callback(your_package::YourService::Request& req,
                        your_package::YourService::Response& res) {
              // 在这里编写服务回调函数的逻辑
              res.output_result = "Processed: " + req.input_data;
              return true;  // 返回 true 表示服务调用成功
          }
  • 回调函数

    • ros::spin();

      ros::spin 函数是一个阻塞调用,会一直运行,直到节点被关闭。它用于等待并处理回调函数中的事件,例如接收到的消息或服务请求。通常情况下,你会在节点的主循环中使用 ros::spin,以便持续地监听和响应事件。

    • ros::spinOnce();

      ros::spinOnce 函数用于处理一次回调函数中的事件。它通常在一个循环中调用,以便节点可以在等待事件的同时执行其他操作。调用 ros::spinOnce 会检查是否有事件需要处理,然后执行相应的回调函数。

  • 时间

    • ros::Time

    • ros::init(argc,argv,"hello_time");
      ros::NodeHandle nh;//必须创建句柄,否则时间没有初始化,导致后续API调用失败
      ros::Time right_now = ros::Time::now();//将当前时刻封装成对象
      ROS_INFO("当前时刻:%.2f",right_now.toSec());//获取距离 1970年01月01日 00:00:00 的秒数
      ROS_INFO("当前时刻:%d",right_now.sec);//获取距离 1970年01月01日 00:00:00 的秒数
      ​
      ros::Time someTime(100,100000000);// 参数1:秒数  参数2:纳秒
      ROS_INFO("时刻:%.2f",someTime.toSec()); //100.10
      ros::Time someTime2(100.3);//直接传入 double 类型的秒数
      ROS_INFO("时刻:%.2f",someTime2.toSec()); //100.30
      ​
    • //时间比较
      ​
      ros::Time start_time = ros::Time::now();
      // 执行一些操作...
      ros::Time end_time = ros::Time::now();
      ​
      ros::Duration elapsed_time = end_time - start_time;
      ROS_INFO("Time elapsed: %.2f seconds", elapsed_time.toSec());
    • //时间同步,确保节点在分布式系统中保持一致的时间标记。
      ​
      // 在节点 A 中
      ros::Time event_time = ros::Time::now();
      your_message.header.stamp = event_time;
      your_publisher.publish(your_message);
      ​
      // 在节点 B 中
      void callback(const YourMessageType::ConstPtr& msg) {
          ros::Time event_time = msg->header.stamp;
          // 使用 event_time 进行处理
      }
    • ros::Duration

    • //使用构造函数来创建 ros::Duration 对象,指定秒数和纳秒数作为参数
      ros::Duration duration(2.5);  // 2.5 seconds
      ros::Duration another_duration(1, 500000000);  // 1.5 seconds
      ​
      //支持与其他时间间隔的比较和计算,例如加法、减法和乘法等
      ros::Duration duration1(2.5);  // 2.5 seconds
      ros::Duration duration2(1.5);  // 1.5 seconds
      ros::Duration sum = duration1 + duration2;  // 4 seconds
      ros::Duration difference = duration1 - duration2;  // 1 second
      ​
      //与 ros::Time 一起使用,用于计算时间间隔和延迟
      ros::Time start_time = ros::Time::now();
      // 执行一些操作...
      ros::Time end_time = ros::Time::now();
      ​
      ros::Duration elapsed_time = end_time - start_time;
      ROS_INFO("Time elapsed: %.2f seconds", elapsed_time.toSec());
    • 循环频率

    • ros::Rate rate(1);//指定频率
      while (true)
      {
          ROS_INFO("-----------code----------");
          rate.sleep(); //休眠,休眠时间 = 1 / 频率。
      }
  • 日志消息

    • ROS_INFO,向终端输出信息

    • ROS_INFO("Your log message here");
      • ROS_INFO:输出一般信息。

      • ROS_WARN:输出警告信息。

      • ROS_ERROR:输出错误信息。

  • ros::ok():检查节点是否仍在运行

四、高级主题和应用

  • launch文件详细用法

    • launch文件是一种用来启动和配置ROS节点的XML文件。它可以帮助你在一个命令中启动多个节点、设置参数和配置节点之间的关系,其能简化节点的配置与启动,提高ROS程序的启动效率

    • launch文件标签

      • <launch>:这是launch文件的根标签,它包含了所有其他标签,定义了启动的节点和配置。

      • <node>:用于启动一个ROS节点的标签

        • <node
            name="node_name"          设置节点的名称,这必须是唯一的
            pkg="package_name"        指定节点所在的ROS包名称
            type="executable_name"    指定节点的可执行文件名称
            output="output_option"    指定节点的输出类型,可以是screen(显示输出在终端)、log(输出到日志文件)、                              logscreen(同时输出到终端和日志文件)
            ns="namespace"            设置节点的命名空间,用于隔离节点的名称
            respawn="true_or_false"   设置为true时,如果节点退出,它将被自动重新启动
            required="true_or_false"  设置为true时,如果节点退出,整个launch文件会被停止
            args="arg1 arg2"          指定传递给节点可执行文件的参数,以空格分隔
            clear_params="true_or_false"  设置为true时,节点启动前会清除与节点相关的参数
            launch-prefix="command"   指定在启动节点之前要添加的前缀命令,用于调整节点的启动方式
          >
            <!-- 子级标签 -->
          </node>
        • <node>可以包含子级标签,包括<param><rosparam><remap>等等

      • <param>:用于设置节点的参数值。它可以包含以下属性:name:参数的名称,value:参数的值。

        • name: 参数的名称,用于在参数服务器中唯一标识参数。示例:name="my_parameter"
          ​
          type: 参数的数据类型。可以是以下之一:string、bool、int、double、float、yaml 等。示例:type="double"
          ​
          value: 参数的初始值。根据参数的数据类型,设置相应的值。示例:value="3.14"
          ​
          command: 用于执行一个命令来设置参数的值。这在需要通过处理命令生成参数值时很有用。示例:command="$(find my_package)/scripts/set_parameter.py"
          ​
          textfile: 从文本文件中读取参数值。文本文件中的内容将被读取并作为参数的值。示例:textfile="$(find my_package)/config/my_parameter.txt"
          ​
          command: 用于在launch文件中执行一个命令,然后使用命令的输出值作为参数的值。示例:command="$(find my_package)/scripts/get_parameter_value.sh"
          ​
          command-args: 与 command 一起使用,用于指定命令的参数。示例:command="$(find my_package)/scripts/get_parameter_value.sh" command-args="param_name"
          ​
          rosparam: 使用rosparam命令来设置参数的值。示例:rosparam="my_parameter"
        • <node name="my_node" pkg="my_package" type="my_node_type">
            <param name="param_name" value="param_value" />
              
            <arg name="my_param" default="default_value" />
            <param name="param_name" value="$(arg my_param)" />
          </node>
        • 将一个参数param_name设置为param_value,并将它附加到名为my_node的节点上。节点启动时,它可以通过ROS参数服务器访问这个参数

        • 也可以使用$(arg arg_name)来从命令行参数中获取值,从而在运行时动态地设置参数

        • 在Python中,可以使用rospy.get_param("param_name")来获取参数的值。

      • <remap>:用于重映射ROS话题、服务、参数等的名称。它可以包含以下属性:from:源名称,to:目标名称

        • <node name="my_node" pkg="my_package" type="my_node_type">
            <remap from="old_topic" to="new_topic" />
          </node>
        • 将名为my_node的节点的一个话题从old_topic重映射为new_topic。这意味着在节点启动时,所有发布到old_topic的消息将被重定向到new_topic

      • <group>:用于创建一个节点组,允许你在同一个组中以特定的顺序启动节点

        • <group ns="robot">
            <node name="motion_controller" pkg="robot_control" type="motion_controller" output="screen" />
            <node name="sensor_driver" pkg="sensors" type="sensor_driver" output="screen" />
            <param name="robot_speed" value="0.5" />
            <remap from="camera_topic" to="robot/camera_topic" />
          </group>
        • 在这个示例中,我们创建了一个名为robot的命名空间,将一个运动控制节点、一个传感器驱动节点、一个参数和一个重映射放置在这个命名空间下。这样做可以确保节点和相关配置不会与其他部分发生冲突。

      • <include>:用于在launch文件中包含其他launch文件,以便组织和重用配置。

        • <launch>
            <include file="$(find my_package)/launch/included_launch_file.launch">
              <arg name="arg_name" value="arg_value" />
            </include>
          </launch>
        • 包含了my_package包中的included_launch_file.launch文件,并传递了一个名为arg_name的参数

      • <arg>:用于定义launch文件中的参数,这些参数可以在运行时被传递或设置。

        • <arg name="arg_name" default="default_value" />  
          ​
          <arg name="robot_name" default="my_robot" />
          <arg name="use_camera" default="true" />
          <arg name="target_speed" default="0.5" />
        • roslaunch my_package my_launch_file.launch robot_name:=my_custom_robot use_camera:=false target_speed:=1.0  #在命令行中传递参数,使用`roslaunch`命令的参数传递功能,我们通过命令行传递了参数值,覆盖了默认值
        • <arg><param>区别

          • 用途:

            • <arg>标签用于定义参数,这些参数可以在命令行传递给launch文件,也可以在包含标签内部传递给被包含的launch文件。它主要用于在不同上下文中自定义配置。

            • <param>标签用于设置节点的参数值。它主要用于在节点启动时为节点配置参数。

          • 传递方式:

            • <arg>标签通过命令行参数传递,可以在roslaunch命令中使用参数名来覆盖默认值。

            • <param>标签通过ROS参数服务器传递,节点在启动时从参数服务器加载参数的值。

          • 上下文:

            • <arg>标签通常用于整个launch文件的上下文,可以影响多个节点的配置。

            • <param>标签通常用于单个节点的上下文,它只影响一个节点的参数设置。

          • 使用场景:

            • 使用<arg>标签适合在不同情况下自定义配置,比如在不同机器、不同实验中使用不同的参数值。

            • 使用<param>标签适合在启动节点时设置其参数,以确保节点在启动时有正确的初始配置。

          • <launch>
              <!-- Using <arg> to define parameters that can be passed via command line -->
              <arg name="robot_name" default="my_robot" />
              <arg name="target_speed" default="0.5" />
            ​
              <!-- Using <param> to set parameters for a node -->
              <node name="my_node" pkg="my_package" type="my_node_type">
                <param name="robot_name" value="$(arg robot_name)" />
                <param name="speed" value="$(arg target_speed)" />
              </node>
            </launch>
          • 使用<arg>定义了robot_nametarget_speed参数,并在<node>标签内部使用<param>将这些参数的值传递给节点

      • <rosparam>:用于从ROS参数服务器加载参数值,<rosparam>标签可以从 YAML 文件导入参数,或将参数导出到 YAML 文件,也可以用来删除参数,<rosparam>标签在<node>标签中时被视为私有

        • <rosparam param="my_params" subst_value="true" command="load" file="$(find my_package)/config/my_params.yaml" />
        • my_params.yaml文件中的参数加载到名为my_params的参数名下。subst_value="true"允许在参数值中进行ROS参数替换,使参数可以引用其他参数

          • param:指定参数的名称,将在ROS参数服务器中创建或覆盖这个参数。这个属性是必需的。

          • subst_value:控制是否允许在参数值中进行ROS参数替换。如果设置为true,则可以在参数值中使用$(param param_name)来引用其他参数。默认值为false

          • command:指定要执行的命令,用于加载参数。常见的命令有load(从文件加载参数)和delete(从ROS参数服务器删除参数)。这个属性是必需的。

          • file:指定要加载的文件路径。这可以是绝对路径,也可以使用ROS软件包的$(find package_name)来获取包路径。该属性通常与command="load"一起使用,用于从文件加载参数值。

          • param_name:用于command="delete"时,指定要删除的参数名称

      • <env>:在ROS launch文件中用于设置节点运行时的环境变量。这些环境变量可以影响节点的行为、配置和功能

        • <node name="my_node" pkg="my_package" type="my_node_type">
            <env name="ROS_MASTER_URI" value="http://localhost:11311" />
            <env name="ROBOT_NAME" value="my_robot" />
          </node>
        • 为节点设置了两个环境变量,分别是ROS_MASTER_URIROBOT_NAME

      • <param if="$(arg condition)" />:条件语句,根据条件来设置参数

        • <launch>
            <arg name="use_param" default="false" />
          ​
            <node name="my_node" pkg="my_package" type="my_node_type">
              <param if="$(arg use_param)" name="param_name" value="param_value" />
            </node>
          </launch>
        • roslaunch my_package my_launch_file.launch use_param:=true
        • 因为use_param的值为true,所以参数param_name会被设置为param_value

      • <launch-prefix>:用于为节点设置前缀命令,可用于调整节点的启动方式

        • <node name="my_node" pkg="my_package" type="my_node_type">
            <launch-prefix cmd="gdb -ex run --args" />
          </node>
        • 为节点设置了一个前缀命令,以便在启动节点之前运行gdb -ex run --args命令。这对于调试节点或在特定环境中运行节点很有用

  • 节点重名

    • 节点(Node)是一个独立的进程,用于执行特定的任务或功能。每个节点都有一个唯一的名称,以便在ROS系统中进行识别和通信,如果启动重名节点的话,之前已经存在的节点会被直接关闭。如果确实需要使用同名节点,那么可以使用命名空间或名称重映射,命名空间就是为名称添加前缀,名称重映射是为名称起别名。

    • 实现策略包括rosrun 命令、launch 文件、编码实现

      • rosrun 命令 设置命名空间

        • rosrun turtlesim turtlesim_node __ns:=/xxx
          rosrun turtlesim turtlesim_node __ns:=/yyy

          使用rosnode list查看节点

        • /xxx/turtlesim
          /yyy/turtlesim
      • rosrun 命令 名称重映射

        • rosrun turtlesim  turtlesim_node __name:=t1 
          rosrun turtlesim  turtlesim_node __name:=t2
  • launch 文件,实现节点重命名

    • <launch>
      ​
          <node pkg="turtlesim" type="turtlesim_node" name="t1" />
          <node pkg="turtlesim" type="turtlesim_node" name="t2" />
          <node pkg="turtlesim" type="turtlesim_node" name="t1" ns="hello"/>
      ​
      </launch>
    • namens,二者分别是用于实现名称重映射与命名空间设置的,name 属性是必须的,ns 可选

    • /t1
      /t2
      /t1/hello
  • 编码实现节点重命名

    • ros::init(argc,argv,"zhangsan",ros::init_options::AnonymousName);  //C++重映射,会在名称后面添加时间戳
      rospy.init_node("lisi",anonymous=True)                             //Python重映射
    • std::map<std::string, std::string> map;
      map["__ns"] = "xxxx";
      ros::init(map,"wangqiang");   //使用命名空间
  • 话题名称设置

    • rosrun设置话题重映射,__name来为节点指定一个不同的名称,并且可以使用参数_remap来进行话题重映射

      • rosrun package_name node_name __name:=new_node_name _remap:=old_topic_name:=new_topic_name
      • package_name是包含要运行的节点的包的名称,node_name是要运行的节点的名称,new_node_name是你想要为节点重命名的名称,old_topic_name是原始话题的名称,new_topic_name是你想要为话题重命名的名称

    • launch文件设置话题重映射

      • <node pkg="xxx" type="xxx" name="xxx">
            <remap from="原话题" to="新话题" />
        </node>
    • 编码设置话题名称

      • 话题的名称与节点的命名空间、节点的名称是有一定关系的,话题名称大致可以分为三种类型:

        • 全局(话题参考ROS系统,与节点命名空间平级)

        • 相对(话题参考的是节点的命名空间,与节点名称平级)

        • 私有(话题参考节点名称,是节点名称的子级)

      • //前期准备
        1.初始化节点设置一个节点名称
        
        ros::init(argc,argv,"hello")
        
        2.设置不同类型的话题
        
        3.启动节点时,传递一个 __ns:= nihao
        
        4.节点启动后,使用 rostopic 查看话题信息
      • 全局名称,以/开头的名称,和节点名称无关

        • ros::Publisher pub = nh.advertise<std_msgs::String>("/chatter",1000);
          // 结果为:/chatter
          ros::Publisher pub = nh.advertise<std_msgs::String>("/chatter/money",1000);
          // 结果为:/chatter/money
      • 相对名称,非/开头的名称,参考命名空间(与节点名称平级)来确定话题名称

        • ros::Publisher pub = nh.advertise<std_msgs::String>("chatter",1000);
          //结果为:nihao/chatter
          ros::Publisher pub = nh.advertise<std_msgs::String>("chatter/money",1000);
          //结果为:nihao/chatter/money
      • 私有名称,以~开头的名称

        • ros::NodeHandle nh("~");
          ros::Publisher pub = nh.advertise<std_msgs::String>("chatter",1000);
          //结果为:/nihao/hello/chatter
  • ROS参数名称设置

    • rosrun设置参数

      • rosrun turtlesim turtlesim_node _A:=100   #添加一个A参数  
      • rosparam list查看节点信息,显示结果

      • /turtlesim/A
        /turtlesim/background_b
        /turtlesim/background_g
        /turtlesim/background_r
    • 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
    • 编码设置参数

      • 在 C++ 中,可以使用 ros::param 或者 ros::NodeHandle 来设置参数

        • // 使用ros::param
          ros::param::set("/set_A",100); //全局,和命名空间以及节点名称无关
          ros::param::set("set_B",100); //相对,参考命名空间
          ros::param::set("~set_C",100); //私有,参考命名空间与节点名称
        • 运行时,假设设置的 namespace 为 xxx,节点名称为 yyy,使用 rosparam list 查看

        • /set_A
          /xxx/set_B
          /xxx/yyy/set_C
        • //使用ros::NodeHandle
          ros::NodeHandle nh;
          nh.setParam("/nh_A",100); //全局,和命名空间以及节点名称无关
          ​
          nh.setParam("nh_B",100); //相对,参考命名空间
          ​
          ros::NodeHandle nh_private("~");
          nh_private.setParam("nh_C",100);//私有,参考命名空间与节点名称
        • /nh_A
          /xxx/nh_B
          /xxx/yyy/nh_C
      • 在python中,可以使用 rospy.set_param

        • rospy.set_param("/py_A",100)  #全局,和命名空间以及节点名称无关
          rospy.set_param("py_B",100)  #相对,参考命名空间
          rospy.set_param("~py_C",100)  #私有,参考命名空间与节点名称
        • /py_A
          /xxx/py_B
          /xxx/yyy/py_C
  • ROS分布式通信 #TODO

  • TF2坐标变换

    • 坐标消息

      • 在坐标转换实现中常用的 msg:geometry_msgs/TransformStampedgeometry_msgs/PointStamped

      • rosmsg info geometry_msgs/TransformStampedgeometry_msgs/TransformStamped 是ROS中用于传输坐标变换信息的消息类型。它包含了一个坐标变换的完整信息,包括变换的平移和旋转以及与之相关的时间戳、坐标系

      • std_msgs/Header header                     #头信息
          uint32 seq                                #|-- 序列号,确保消息的顺序传递以及检测丢失的消息
          time stamp                                #|-- 时间戳,数据同步和历史数据分析
          string frame_id                            #|-- 坐标 ID,正确处理数据和在不同坐标系
        string child_frame_id                    #子坐标系的 id
        geometry_msgs/Transform transform        #坐标信息
          geometry_msgs/Vector3 translation        #偏移量
            float64 x                                #|-- X 方向的偏移量
            float64 y                                #|-- Y 方向的偏移量
            float64 z                                #|-- Z 方向上的偏移量
          geometry_msgs/Quaternion rotation        #四元数,表示旋转
            float64 x                                
            float64 y                                
            float64 z                                
            float64 w
      • rosmsg info geometry_msgs/PointStampedgeometry_msgs/PointStamped 是ROS中用于传输带有时间戳的三维点信息的消息类型。它在 geometry_msgs 包中定义,用于表示一个带有时间戳的三维点

      • std_msgs/Header header                    #头
          uint32 seq                                #|-- 序号
          time stamp                                #|-- 时间戳
          string frame_id                            #|-- 所属坐标系的 id
        geometry_msgs/Point point                #点坐标
          float64 x                                    #|-- x y z 坐标
          float64 y
          float64 z
    • TF2基本使用方法,广播、监听和转换坐标变换的功能

      • //导入头文件
        #include <tf2_ros/transform_broadcaster.h>
        #include <tf2_ros/transform_listener.h>
        #include <tf2_geometry_msgs/tf2_geometry_msgs.h>
      • 广播坐标变换: 使用TransformBroadcaster可以广播坐标变换。通过广播,可以将坐标变换发送给ROS系统中的其他节点

        • //内置函数
          构造函数:
          tf2_ros::TransformBroadcaster()//创建一个广播器对象。
          广播坐标变换:
          sendTransform(const geometry_msgs::TransformStamped &transformStamped)//广播坐标变换消息。你需要创建一个 geometry_msgs::TransformStamped 消息,将坐标变换信息填入并传递给这个函数。
        • // 创建TransformBroadcaster实例
          tf2_ros::TransformBroadcaster tf_broadcaster;
          
          // 创建TransformStamped消息
          geometry_msgs::TransformStamped transformStamped;
          // 设置变换信息
          
          // 广播坐标变换消息
          tf_broadcaster.sendTransform(transformStamped);
      • 监听坐标变换: 使用TransformListener可以监听其他节点广播的坐标变换

        • //内置函数
          构造函数:
          tf2_ros::TransformListener(const tf2_ros::Buffer &buffer) //创建一个监听器对象,需要传入一个 tf2_ros::Buffer 对象,用于存储坐标变换信息。
          查询坐标变换:
          lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time)   //在指定时间查询从源坐标系到目标坐标系的坐标变换。如果查询成功,返回 geometry_msgs::TransformStamped 消息,如果失败,会抛出 tf2::TransformException 异常
        • // 创建TransformListener实例
          tf2_ros::TransformListener tf_listener(buffer);
          
          // 创建输入数据
          geometry_msgs::PointStamped input_point;
          // 设置输入数据信息
          
          // 转换点到目标坐标系
          geometry_msgs::PointStamped output_point;
          if (buffer.transform(input_point, output_point, "target_frame"))
          {
              // 处理转换后的点
          }
      • TransformBuffer(变换缓存): 变换缓存用于存储和管理坐标变换。在监听和查询坐标变换时,使用TransformBuffer可以避免频繁查询TF,提高性能

        • //内置函数
          构造函数:
          tf2_ros::TransformBuffer():创建一个空的变换缓存对象。
          查询坐标变换:
          lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time):在指定时间查询从源坐标系到目标坐标系的坐标变换。
          canTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, ros::Duration timeout):检查是否可以从源坐标系到目标坐标系的坐标变换。
          查询是否存在坐标变换信息:
          canTransform(const std::string &target_frame, const ros::Time &target_time, const std::string &source_frame, const ros::Time &source_time, const std::string &fixed_frame, ros::Duration timeout):检查是否可以从源坐标系到目标坐标系的坐标变换,并且指定了固定坐标系。
          清空变换缓存:
          clear():清空变换缓存中的所有坐标变换信息。
          设置使用独立线程:
          setUsingDedicatedThread(bool use_dedicated_thread):设置是否使用独立的线程来处理坐标变换查询。
          设置坐标变换信息过期时间:
          setTransformExpirationDuration(const ros::Duration &duration):设置坐标变换信息的过期时间。
          获取所有坐标系名称:
          getFrameStrings(std::string &frame_authority):获取所有存在的坐标系名称。
          插入坐标变换:
          setTransform(const geometry_msgs::TransformStamped &transform):将坐标变换信息插入到变换缓存中。
          插入多个坐标变换:
          setTransforms(const std::vector<geometry_msgs::TransformStamped> &transforms, const ros::Time &time, const std::string &authority):批量插入多个坐标变换信息。
          数据类型转换:
          transformStampedToEigen(const geometry_msgs::TransformStamped &transformStamped):将 geometry_msgs::TransformStamped 转换为 Eigen::Isometry3d。
        • // 创建TransformBuffer实例
          tf2_ros::Buffer buffer;
          tf2_ros::TransformListener tf_listener(buffer);
          ​
          // 查询坐标变换
          geometry_msgs::TransformStamped transformStamped;
          try {
              transformStamped = buffer.lookupTransform("target_frame", "source_frame", ros::Time(0));
              // 处理变换
          } catch (tf2::TransformException &ex) {
              ROS_WARN("Transform exception: %s", ex.what());
          }
      • 数据类型转换: TF2提供了一些方便的函数用于坐标变换的数据类型转换,如旋转矩阵到四元数、四元数到欧拉角等

        • #include <tf2/convert.h>
          ​
          tf2::Quaternion quaternion;
          quaternion.setRPY(0.1, 0.2, 0.3);  // 设置旋转角度
          ​
          tf2::Matrix3x3 rotation_matrix(quaternion);
          tf2::Quaternion converted_quaternion;
          rotation_matrix.getRotation(converted_quaternion);
          ​
          // 进行其他数据类型转换操作
      • 其他内置函数

        • 坐标变换函数:
          tf2::doTransform(const PointT &in, PointT &out, const geometry_msgs::TransformStamped &transformStamped):将输入数据从一个坐标系转换到另一个坐标系,并将结果存储在输出变量 out 中。
          坐标变换查询函数:
          tf2_ros::Buffer::lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time):在指定时间查询从源坐标系到目标坐标系的坐标变换。
          tf2_ros::Buffer::canTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, ros::Duration timeout):检查是否可以从源坐标系到目标坐标系的坐标变换。
          坐标变换缓存函数:
          tf2_ros::TransformBuffer::clear():清空变换缓存中的所有坐标变换信息。
          tf2_ros::TransformBuffer::setTransform(const geometry_msgs::TransformStamped &transform):将坐标变换信息插入到变换缓存中。
          数据类型转换函数:
          tf2::fromMsg(const geometry_msgs::TransformStamped &msg, tf2::Transform &bt):将 geometry_msgs::TransformStamped 转换为 tf2::Transform。
          tf2::toMsg(const tf2::Transform &bt, geometry_msgs::TransformStamped &msg):将 tf2::Transform 转换为 geometry_msgs::TransformStamped。
          四元数操作函数:
          tf2::Quaternion::setRPY(double roll, double pitch, double yaw):设置四元数的欧拉角。
          tf2::Quaternion::normalize():将四元数归一化。
          矩阵操作函数:
          tf2::Matrix3x3::getRPY(double &roll, double &pitch, double &yaw, unsigned int solution_number=1) const:从旋转矩阵中获取欧拉角

五、项目和实践

  • URDF文件

    • URDF (Unified Robot Description Format) 是一种用于描述机器人模型的XML文件格式。它是ROS(机器人操作系统)中常用的一种文件格式,用于表示机器人的结构、关节、传感器、连接关系等信息。URDF文件可用于仿真、控制、运动规划等任务。

    • #urdf_to_graphiz: 将URDF文件转换为Graphviz可视化格式,用于可视化URDF模型的结构。
      rosrun urdfdom urdf_to_graphiz your_robot.urdf
      
      #check_urdf: 检查URDF文件的语法和结构是否正确
      check_urdf your_robot.urdf
      
      #rosparam get <param_name>: 获取在参数服务器上存储的URDF参数值
      rosparam get /robot_description
      
      #robot_state_publisher:一个ROS软件包,用于将URDF模型的状态发布到ROS的TF变换框架,使得可以在RViz中显示机械臂的运动
      rosrun robot_state_publisher robot_state_publisher
      
      #spawn_model: 在Gazebo仿真中加载URDF模型
      roslaunch gazebo_ros spawn_model.launch urdf:=/path/to/your_robot.urdf
    • <robot>:整个机器人模型的根标签,包含机器人的基本信息,子标签:name:机器人的名称

      • <robot name="my_robot">
          <!-- 在这里定义机器人的链接、关节、传感器等部分 -->
        </robot>
    • <link>:描述机器人的各个组成部分的属性。嵌套的标签:<visual><collision><inertial>

      • <link name="link_name">
          <!-- 可视化外观 -->
          <visual>
            <!-- 定义可视化外观的几何形状 -->
            <geometry>
              <!-- 几何形状的类型,如box、cylinder、mesh等 -->
            </geometry>
            <!-- 可选:定义材质的颜色、纹理等信息 -->
            <material name="material_name">
              <!-- 定义颜色或纹理 -->
            </material>
          </visual>
        
          <!-- 碰撞信息 -->
          <collision>
            <!-- 定义用于碰撞检测的几何形状 -->
            <geometry>
              <!-- 几何形状的类型,如box、cylinder、mesh等 -->
            </geometry>
          </collision>
        
          <!-- 惯性信息 -->
          <inertial>
            <!-- 链接的质量 -->
            <mass value="0.1"/>
            <!-- 链接的惯性矩阵 -->
            <inertia ixx="0.01" iyy="0.01" izz="0.01"/>
            <!-- 惯性坐标系的原点 -->
            <origin rpy="0 0 0" xyz="0 0 0"/>
          </inertial>
        </link>
    • <joint>:描述连接不同链接之间的关节属性,嵌套的标签:<parent><child><axis><limit><origin>

      • <joint name="joint_name" type="joint_type">
          <!--type有revolute(旋转关节)、continuous(连续旋转关节)、prismatic(移动关节)、fixed(固定关节)等-->
          <!-- 父链接 -->
          <parent link="parent_link_name"/>
          <!-- 子链接 -->
          <child link="child_link_name"/>
          <!-- 轴信息 -->
          <axis xyz="0 0 1"/>
          <!-- 关节的角度范围限制 -->
          <limit effort="10" velocity="1.0" lower="-1.57" upper="1.57"/>
         <!--effort:关节的最大扭矩。velocity:关节的最大运动速度。lower:关节的最小角度(或位移)限制。upper:关节的最大角度(或位移)限制-->
          <!-- 关节初始角度 -->
          <origin rpy="0 0 0" xyz="0 0 0"/>
        </joint>
    • <sensor>:描述机器人上的传感器属性。

      • <sensor name="camera_sensor" type="camera">
          <camera>
            <!-- 定义相机参数,如分辨率、视场等 -->
          </camera>
        </sensor>
        
        <sensor name="laser_sensor" type="ray">
          <ray>
            <!-- 定义激光雷达参数,如扫描角度、最大距离等 -->
          </ray>
        </sensor>
        
        <sensor name="imu_sensor" type="imu">
          <imu>
            <!-- 定义IMU参数,如噪声、偏置等 -->
          </imu>
        </sensor>
  • xacro文件

    • xacro 是 ROS 中一种XML格式的宏语言,用于更便捷地创建和维护URDF(Unified Robot Description Format)文件。它允许您在URDF文件中使用参数、条件、循环等功能,以减少冗余代码,提高代码可维护性,并支持更高级的模型构建。

      • 参数替换: 使用 xacro,您可以定义参数,并在URDF文件中使用这些参数。这使您能够轻松更改机器人模型的参数,而无需手动修改每个链接和关节。

      • 条件和循环: xacro 支持条件判断和循环,可以根据不同情况生成不同的URDF代码。这对于构建具有复杂结构和变化的机器人模型非常有用。

      • 包含其他文件: xacro 允许您在URDF文件中包含其他xacro文件,从而使代码模块化,易于组织和管理。

      • 宏定义: xacro 允许您定义宏,将常用的代码块封装为可重用的模块,从而减少代码重复。

      • 参数传递: 在 xacro 文件之间传递参数,使得您可以在多个URDF文件中共享相同的参数值。

    • #rosrun xacro xacro <input.xacro> > <output.urdf>: 将xacro文件转换为URDF文件
      rosrun xacro xacro your_input.xacro > output.urdf
      
      #将xacro文件转换为URDF文件,并传递参数值给xacro。
      rosrun xacro xacro your_input.xacro > output.urdf -param param_name:=param_value
      
      #将xacro文件转换为URDF文件并指定输出文件名。
      rosrun xacro xacro your_input.xacro -o output.urdf
      
      #获取在参数服务器上存储的xacro参数值。
      rosparam get /your_param_name
      
      #设置参数服务器上的xacro参数值
      rosparam set /your_param_name param_value
    • 语法详解

      • <xacro:property name="param_name" value="param_value"/>
        <!--参数定义-->
      • <link name="my_link">
          <geometry>
            <box size="${param_value} ${param_value} ${param_value}"/>
          </geometry>
        </link>
        <!--参数使用-->
      • <xacro:if value="${condition}">
          <!-- 仅当条件为真时生成这部分代码 -->
        </xacro:if>
        <!--条件判断-->
      • <xacro:foreach var="i" range="0 ${num_links}">
          <link name="link_${i}"/>
        </xacro:foreach>
        <!--循环生成-->
      • <xacro:include filename="path_to_sub_robot.xacro"/>
        <!--包含其他文件-->
      • <xacro:def name="my_macro" param="${param}">
          <!-- 宏的内容 -->
        </xacro:def>
        <!--宏定义-->
      • <xacro:call macro="my_macro" param="value"/>
        <!--宏使用-->
      • <xacro:macro name="sub_robot" param="${param}">
          <!-- 定义参数 -->
        </xacro:macro>
        <!--参数传递-->

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值