ROS 程序框架

基本思路(以C++为例)

  • 使用catkin_create_pkg创建一个软件包
  • 在软件包的src文件夹下创建一个节点的cpp源码文件
  • 在节点的源码文件中include包含ROS的头文件
  • 构建一个main函数,并在函数的开头执行ros::init()
  • 构建while循环,循环条件为ros::ok()
  • 在CMakeLists.txt中设置节点源码的编译规则
  • 编译运行

代码框架

ROS(Robot Operating System)程序的代码框架主要由以下几个核心部分组成,无论是使用 C++ 还是 Python 编写。下面是 ROS 程序的一般代码框架的概述:

1. 包(Package)的创建

  • 每个 ROS 程序都位于一个包内。包是 ROS 程序的基本组织单位,包含代码、节点、库、配置文件等。
  • 使用 catkin_create_pkg 创建一个新包,指定依赖项:
    catkin_create_pkg <package_name> [dependencies]
    

2. 包的配置文件

  • package.xml:定义包的元数据和依赖关系。

    <package>
      <name>my_package</name>
      <version>0.0.0</version>
      <description>A simple ROS package</description>
      <depend>roscpp</depend>
      <depend>std_msgs</depend>
      ...
    </package>
    
  • CMakeLists.txt(仅限 C++):定义如何构建代码和安装目标。

    cmake_minimum_required(VERSION 3.0.2)
    project(my_package)
    
    find_package(catkin REQUIRED COMPONENTS
      roscpp
      std_msgs
      ...
    )
    
    catkin_package()
    
    include_directories(${catkin_INCLUDE_DIRS})
    
    add_executable(${PROJECT_NAME}_node src/my_node.cpp)
    target_link_libraries(${PROJECT_NAME}_node ${catkin_LIBRARIES})
    

3. 节点(Node)的实现

  • 节点是 ROS 网络中的一个独立运行的实体,负责特定的功能(比如数据处理、传感器控制等)。
C++ 节点示例
#include "ros/ros.h"
#include "std_msgs/String.h"

int main(int argc, char **argv) {
  ros::init(argc, argv, "node_name");
  ros::NodeHandle nh;

  ros::Publisher pub = nh.advertise<std_msgs::String>("topic_name", 1000);
  ros::Rate loop_rate(10);

  while (ros::ok()) {
    std_msgs::String msg;
    msg.data = "hello world";
    pub.publish(msg);
    ros::spinOnce();
    loop_rate.sleep();
  }
  return 0;
}
Python 节点示例
#!/usr/bin/env python
import rospy
from std_msgs.msg import String

def talker():
    pub = rospy.Publisher('chatter', String, queue_size=10)
    rospy.init_node('talker', anonymous=True)
    rate = rospy.Rate(10) # 10hz
    while not rospy.is_shutdown():
        hello_str = "hello world %s" % rospy.get_time()
        rospy.loginfo(hello_str)
        pub.publish(hello_str)
        rate.sleep()

if __name__ == '__main__':
    talker()

4. 构建和运行

  • 使用 catkin_make 在 ROS 工作空间中构建包。
  • 运行节点:
    • C++:rosrun my_package my_node
    • Python:rosrun my_package my_script.py

5. 其他文件和目录

  • scripts:用于存放 Python 脚本。
  • src:存放 C++ 源代码。
  • msgsrv:定义自定义消息和服务类型。
  • launch:包含启动多个节点的 ROS launch 文件。

这个框架提供了一个标准的方法来组织和构建 ROS 程序,确保代码的模块化和易于管理。每个节点在 ROS 网络中执行特定的任务,并通过消息、服务和动作与其他节点通信。

C++版本分析

这段代码是一个使用 C++ 编写的简单 ROS 节点示例,它展示了如何使用 roscpp(ROS 的 C++ 客户端库)创建一个发布消息的节点。以下是对代码的详细解释及 ROS 程序的基本框架概述。

代码解释

  1. 包含 ROS 头文件

    #include "ros/ros.h"
    #include "std_msgs/String.h"
    

    这里包含了 ROS 的主要头文件和用于发布字符串消息的标准消息类型。

  2. 主函数

    int main(int argc, char **argv) {
      ...
    }
    

    定义了程序的入口点。

  3. 初始化 ROS 节点

    ros::init(argc, argv, "node_name");
    

    初始化 ROS 并命名节点为 "node_name"argcargv 参数用于处理任何 ROS 参数和重映射。

  4. 创建节点句柄

    ros::NodeHandle nh;
    

    节点句柄是与 ROS 系统通信的主要接入点。

  5. 创建发布者

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

    通过节点句柄 nh,创建一个发布者 pub。这个发布者将会向 "topic_name" 话题发布 std_msgs::String 类型的消息。1000 是发布队列的大小。

  6. 设置循环频率

    ros::Rate loop_rate(10);
    

    设置循环频率为 10Hz。

  7. 节点的主循环

    while (ros::ok()) {
      ...
    }
    

    在 ROS 运行正常时持续循环。

  8. 创建消息并发布

    std_msgs::String msg;
    msg.data = "hello world";
    pub.publish(msg);
    

    在每次循环中,创建一个 std_msgs::String 消息,填充数据,并通过发布者 pub 发布。

  9. 调用 ros::spinOnce()

    ros::spinOnce();
    

    允许 ROS 处理任何待处理的回调函数,适用于需要同时发布和订阅的节点。

  10. 睡眠以维持循环频率

    loop_rate.sleep();
    

    控制循环频率,使其保持在设定的 10Hz。

  11. 返回

    return 0;
    

    当 ROS 关闭或节点被终止时,程序退出。

ROS 程序的基本框架

从这个示例中,我们可以归纳出 ROS 程序的基本框架:

  1. 包含必要的头文件:包括 ROS 的核心头文件和任何消息类型的头文件。
  2. 初始化 ROS 节点:设置节点的名称,并处理任何 ROS 命令行参数。
  3. 创建节点句柄:用于管理 ROS 资源和通信。
  4. 创建通信接口:如发布者、订阅者或服务。
  5. 设置循环频率(可选):对于需要定时执行任务的节点。
  6. 实现节点功能:在循环中进行消息的发布、接收或其他操作。
  7. 维护循环频率和响应回调:通过 ros::spinOnce() 和循环睡眠来实现。

这种结构为编写 ROS 节点提供了一个清晰的模板,无论是发布者、订阅者还是服务的实现。通过这个框架,可以确保 ROS 节点能够正确地与 ROS 网络交互,执行其预定的任务。

Python版本分析

这段代码是一个 ROS 节点的 Python 实现示例,它演示了如何使用 rospy 创建一个简单的发布者(Publisher)。让我们逐步解释这段代码,并从中归纳 ROS 程序的基本框架。

代码解释

  1. 脚本启动行

    #!/usr/bin/env python
    

    这行告诉你的系统如何找到 Python 解释器。它是 Unix 和类 Unix 系统中的标准技巧,用于指定解释器路径。

  2. 导入模块

    import rospy
    from std_msgs.msg import String
    

    这里导入了 rospy 模块,它是 ROS 的 Python 客户端库,以及从 std_msgs 包中导入 String 消息类型。

  3. 定义 talker 函数

    def talker():
        ...
    

    定义了一个名为 talker 的函数,它是节点的主体。

  4. 创建发布者

    pub = rospy.Publisher('chatter', String, queue_size=10)
    

    创建了一个发布者对象 pub,它将发布 String 类型的消息到名为 chatter 的话题上。

  5. 初始化节点

    rospy.init_node('talker', anonymous=True)
    

    初始化一个名为 talker 的 ROS 节点。设置 anonymous=True 可以确保节点有一个唯一的名称,避免名称冲突。

  6. 设置循环频率

    rate = rospy.Rate(10) # 10hz
    

    设置一个循环率为 10Hz 的 Rate 对象。

  7. 节点的主循环

    while not rospy.is_shutdown():
        ...
    

    在 ROS 没有关闭的情况下循环执行以下操作。

  8. 创建和发布消息

    hello_str = "hello world %s" % rospy.get_time()
    rospy.loginfo(hello_str)
    pub.publish(hello_str)
    

    在每次循环中,创建一个含有当前时间的字符串消息,记录这个消息,并将其发布到 chatter 话题。

  9. 维持循环率

    rate.sleep()
    

    使循环以设定的 10Hz 频率运行。

  10. 脚本主入口

    if __name__ == '__main__':
        talker()
    

    当脚本直接运行时,调用 talker 函数。

ROS 程序的基本框架

从这个示例中,我们可以归纳出 ROS 程序的基本框架:

  1. 导入必要的 ROS 模块和消息类型。
  2. 定义节点的主要功能(如发布或订阅)。
  3. 初始化 ROS 节点。
  4. 创建 ROS 通信接口的实例(发布者、订阅者、服务服务器或客户端)。
  5. 使用循环(对于持续运行的任务)来执行节点的功能,比如发布消息。
  6. 在脚本的主部分调用这个功能,以确保当脚本被执行时,节点开始运行。

这种结构提供了 ROS 程序开发的标准方法,可以应用于创建各种类型的节点,无论是发布者、订阅者、服务的提供者还是客户端。

CmakeLists.txt

在ROS(机器人操作系统)中,CMakeLists.txt 文件是一个非常重要的组成部分,用于定义构建ROS包的过程。这个文件是基于CMake,一个跨平台的安装(构建)系统。下面我将详细解释如何修改 CMakeLists.txt 文件,并给出一个例子。

CMakeLists.txt 文件的基本结构

一个典型的 CMakeLists.txt 文件包括以下部分:

  1. 基本的CMake命令:指定CMake的最低版本要求和项目名称。
  2. 查找依赖项:使用 find_package() 命令查找编译和运行所需的包。
  3. 设置全局属性:例如,包含目录和C++标准。
  4. 定义消息、服务和动作:如果你的包定义了自定义的消息、服务或动作。
  5. 声明ROS节点和库:使用 add_executable()add_library() 声明你的可执行文件和库。
  6. 添加依赖关系:使用 add_dependencies() 确保所有消息和服务在你的节点之前生成。
  7. 指定链接库:使用 target_link_libraries() 指定链接到你的可执行文件或库的库。

当然,我会将这些详细的代码解释整合到之前关于 CMakeLists.txt 文件的解释中。

CMakeLists.txt 文件的基本结构和详细解释

在ROS(机器人操作系统)中,CMakeLists.txt 文件用于定义构建ROS包的过程。它是基于CMake,一个跨平台的安装(构建)系统。这个文件的基本结构包括以下部分,我将逐一解释每一部分的作用:

  1. 基本的CMake命令

    cmake_minimum_required(VERSION 3.0.2)
    project(my_ros_package)
    
    • cmake_minimum_required 指定了CMake的最低版本要求,确保构建过程的兼容性。
    • project 设置了项目的名称,用于在后续的脚本中引用整个项目。
  2. 查找依赖项

    find_package(catkin REQUIRED COMPONENTS
      roscpp
      rospy
      std_msgs
    )
    
    • find_package 用于查找并设置用于项目构建的外部项目(包),这里是 catkin 和一些基本的ROS组件。
  3. 设置全局属性

    catkin_package(
      CATKIN_DEPENDS roscpp rospy std_msgs
    )
    
    • catkin_package 定义了关于包依赖的元数据,如其他catkin项目依赖的组件。
  4. 包含目录

    include_directories(
      include
      ${catkin_INCLUDE_DIRS}
    )
    
    • include_directories 添加包含目录,确保编译时可以找到项目和依赖的头文件。
  5. 定义消息、服务和动作:如果你的包定义了自定义的消息、服务或动作,你需要在这里指定它们。这个例子中没有涉及。

  6. 声明ROS节点和库

    add_executable(publisher_node src/publisher.cpp)
    target_link_libraries(publisher_node ${catkin_LIBRARIES})
    
    add_executable(subscriber_node src/subscriber.cpp)
    target_link_libraries(subscriber_node ${catkin_LIBRARIES})
    
    • add_executable 定义了可执行文件(节点),如发布者和订阅者。
    • target_link_libraries 指定了链接到你的可执行文件的库。
  7. 添加依赖关系和指定链接库:通常这些步骤是通过 add_dependenciestarget_link_libraries 实现的,确保所有消息和服务在你的节点之前生成,并且正确链接必要的库。

修改 CMakeLists.txt 的例子

假设你需要添加一个新的依赖,如 sensor_msgs,你可以这样修改:

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  sensor_msgs  # 新增依赖
)

catkin_package(
  CATKIN_DEPENDS roscpp rospy std_msgs sensor_msgs  # 新增依赖
)

如果你要添加一个新的节点:

add_executable(another_node src/another_node.cpp)
target_link_libraries(another_node ${catkin_LIBRARIES})

通过这些步骤,你可以根据需要修改你的 CMakeLists.txt 文件来适应你的ROS项目。每当你添加新文件或修改依赖关系时,都需要更新这个文件。

ros::init()

在 ROS(Robot Operating System,机器人操作系统)中,ros::init() 函数是初始化ROS节点的重要步骤。这个函数负责设置节点的名称,并且处理ROS与命令行参数有关的所有事务。下面我将详细解释 ros::init() 的功能,并提供一个实例。

ros::init() 函数的作用

  1. 初始化节点: 在ROS中,每个可执行文件都被视为一个节点。ros::init() 初始化这个节点,并允许它与ROS Master进行通信。

  2. 处理命令行参数: 这个函数解析传递给ROS节点的命令行参数(例如,节点名称、ROS Master的URI等)。

  3. 设置节点名称: 它为节点设置一个唯一的名称,这是节点在ROS网络中被识别的方式。

  4. 初始化节点的通信功能: 设置节点与ROS系统其他部分的通信功能,包括与ROS Master的通信。

ros::init() 函数的使用

ros::init() 有几种不同的重载形式。最常见的形式是接受命令行参数和节点名称。例如:

int main(int argc, char **argv) {
    ros::init(argc, argv, "my_node_name");
    // ...
}

在这个例子中:

  • argcargv 是传递给 main 函数的标准命令行参数。
  • "my_node_name" 是你要为这个节点指定的名称。

这样,当你的节点启动时,它会注册到ROS Master,并被指定为 my_node_name

实例

假设我们要创建一个简单的ROS节点,该节点定期打印一条消息。我们的程序将包括以下几个步骤:

  1. 初始化ROS节点
  2. 创建节点句柄
  3. 设置循环频率
  4. 在循环中发布消息

实现代码如下:

#include <ros/ros.h>

int main(int argc, char **argv) {
    // Step 1: Initialize the ROS node
    ros::init(argc, argv, "talker");

    // Step 2: Create a node handle
    ros::NodeHandle nh;

    // Step 3: Set loop frequency
    ros::Rate loop_rate(10); // 10 Hz

    while (ros::ok()) {
        // Step 4: Publish a message (e.g., to a topic)
        ROS_INFO("Hello, ROS!");

        // ...
        // Here you might publish a message to a topic
        // ...

        ros::spinOnce();
        loop_rate.sleep();
    }

    return 0;
}

在这个例子中:

  • 我们使用 ros::init 初始化了一个名为 “talker” 的节点。
  • 创建了一个 ros::NodeHandle 对象,这是与ROS系统交互的主要途径。
  • 使用 ros::Rate 设置了循环频率。
  • while 循环中,使用 ROS_INFO 打印消息,并可能向某个主题发布消息。
  • ros::spinOnce() 允许ROS处理任何挂起的回调,loop_rate.sleep() 保持循环频率。

这是一个非常基础的ROS程序结构,演示了如何初始化节点并在循环中执行任务。在实际应用中,你可能会添加更多的功能,如订阅主题、提供服务等。

ros::ok()

在 ROS(Robot Operating System,机器人操作系统)中,ros::ok() 函数是一个非常重要的工具,用于检查ROS节点的状态。这个函数在ROS程序中经常被用来控制循环和确保优雅地关闭节点。下面我将详细解释 ros::ok() 的功能,并提供一个实例。

ros::ok() 函数的作用

  1. 检查节点状态ros::ok() 返回一个布尔值,表示ROS节点的状态。如果返回 true,表示节点正在正常运行。如果返回 false,则意味着节点应该被关闭。

  2. 响应外部中断:它能够检测到如 Ctrl+C 这样的用户中断,或者ROS系统要求节点关闭的信号。

  3. 处理异常和错误:如果ROS遇到异常或错误(例如,无法与ROS Master通信),ros::ok() 也会返回 false

使用 ros::ok() 的实例

让我们考虑一个简单的ROS节点,它在一个循环中执行任务,直到接收到关闭信号或遇到错误。以下是实现的基本结构:

#include <ros/ros.h>

int main(int argc, char **argv) {
    // 初始化ROS节点
    ros::init(argc, argv, "my_ros_node");
    
    // 创建节点句柄
    ros::NodeHandle nh;

    // 循环频率
    ros::Rate loop_rate(10); // 10 Hz

    while (ros::ok()) {
        // 执行一些任务
        // 例如,读取传感器数据、计算、发布消息等

        ROS_INFO("Node is running...");

        // 其他ROS相关操作
        // ...

        ros::spinOnce();  // 处理任何挂起的ROS事件
        loop_rate.sleep(); // 根据设定的频率休眠
    }

    // 如果ros::ok() 返回false,退出循环
    ROS_INFO("Node is shutting down...");

    return 0;
}

在这个例子中:

  • 使用 ros::init 初始化了一个名为 “my_ros_node” 的节点。
  • 创建了一个 ros::NodeHandle 对象。
  • 设置了循环频率为10 Hz。
  • while (ros::ok()) 循环确保只要节点正常运行,循环就会继续。
  • 在循环内部,你可以执行任何必要的任务,例如读取传感器数据、计算或发布ROS消息。
  • ros::spinOnce() 允许ROS处理任何挂起的回调,loop_rate.sleep() 保持循环频率。
  • 一旦 ros::ok() 返回 false,循环终止,程序可以优雅地关闭。

这个结构在ROS程序中非常常见,因为它提供了一种简单而有效的方法来持续执行任务,同时保持对系统状态的检查,确保在需要时能够正确地关闭节点。

while(ros::ok())和while(true)有什么区别

Logo

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。

更多推荐