ROS学习

1.ROS工作空间

存放项目开发相关文件的文件夹;

  • src:代码空间(Source Space)
  • install:安装空间(Install Space)
  • build:编译空间(Build Space)
  • log:日志空间(Log Space)

2.colcon是一个包构建工具

复制代码
sudo apt install python3-colcon-ros

sudo apt install python3-colcon-ros

3.source命令

在当前bash环境下读取并执行FileName中的命令。

source命令(从 C Shell 而来)是bash shell的内置命令。点命令,就是个点符号,(从Bourne Shell而来)是source的另一名称。

source(或点)命令通常用于重新执行刚修改的初始化文档,如 .bash_profile 和 .profile 这些配置文件。

4.功能包

将不同的功能代码放在不同的功能包中,降低耦合,提高软件复用率

5.节点:机器人的工作细胞

第一个节点(面向过程编程)

复制代码
import rclpy                                     # ROS2 Python接口库
from rclpy.node import Node                      # ROS2 节点类
import time

def main(args=None):                             # ROS2节点主入口main函数
    rclpy.init(args=args)                        # ROS2 Python接口初始化
    node = Node("node_helloworld")               # 创建ROS2节点对象并进行初始化
    
    while rclpy.ok():                            # ROS2系统是否正常运行
        node.get_logger().info("Hello World")    # ROS2日志输出
        time.sleep(0.5)                          # 休眠控制循环时间
    
    node.destroy_node()                          # 销毁节点对象    
    rclpy.shutdown()                             # 关闭ROS2 Python接口

面向对象编程

复制代码
import rclpy                                     # ROS2 Python接口库
from rclpy.node import Node                      # ROS2 节点类
import time

"""
创建一个HelloWorld节点, 初始化时输出"hello world"日志
"""
class HelloWorldNode(Node):
    def __init__(self, name):                        # 构造函数
        # 调用父类 Node 的构造函数来初始化父类部分。super() 函数返回父类对象的实例,super().__init__(name) 调用父类 Node 的初始化方法,将 name 参数传递给父类的构造函数
        # super().__init__() 是 Python 中的一个方法,常用于调用父类的构造函数(__init__())。它的作用是让子类能够继承并正确初始化父类的属性和行为。
        super().__init__(name)                       # ROS2节点父类初始化
        while rclpy.ok():                            # ROS2系统是否正常运行
            self.get_logger().info("Hello World")    # ROS2日志输出
            time.sleep(0.5)                          # 休眠控制循环时间

def main(args=None):                                 # ROS2节点主入口main函数
    rclpy.init(args=args)                            # ROS2 Python接口初始化
    node = HelloWorldNode("node_helloworld_class")   # 创建ROS2节点对象并进行初始化
    node.destroy_node()                              # 销毁节点对象
    rclpy.shutdown()                                 # 关闭ROS2 Python接口

在编写程序之后或者更改程序之后一定要进行重新编译

复制代码
colcon build

实际运行的程序位于

复制代码
~/dev_ws/install/learning_node/lib/python3.10/site-packages/learning_node

6.话题:节点间传递数据的桥梁(特性:单向传输,无法满足所有数据传输需求)

安装ROS2中针对相机的标准驱动

复制代码
sudo apt install ros-humble-usb-cam

ros2 run usb_cam usb_cam_node_exe

ros2 topic list

usb_cam_node_exe 会从连接的 USB 摄像头(通常是 /dev/video0 设备)读取图像数据。它会定期获取摄像头帧并将图像转换为 ROS 2 的消息格式。

  • 节点初始化

    • 在 ROS 2 中,usb_cam_node_exe 通过继承 ROS 2 的 Node 类来创建一个节点。这个节点负责捕获图像数据并将其发布到 ROS 话题上。
    • 当启动 usb_cam_node_exe 时,它会初始化 ROS 2 接口,并创建一个节点对象。节点会使用相机驱动库(例如 v4l2)来获取摄像头的图像数据。
  • 图像数据捕获

    • usb_cam_node_exe 会从连接的 USB 摄像头(通常是 /dev/video0 设备)读取图像数据。它会定期获取摄像头帧并将图像转换为 ROS 2 的消息格式。
  • 图像消息发布

    • 一旦图像数据被获取,usb_cam_node_exe 会将图像数据包装到一个 ROS 2 消息中,通常是 sensor_msgs/msg/Image 消息类型。
    • 该节点通过 ROS 2 的 发布者 (Publisher)机制,将 sensor_msgs/msg/Image 类型的消息发布到相应的话题上(如 /usb_cam/image_raw)。
    • 消息的内容包含图像的像素数据以及一些附加的元数据,如图像的大小、编码格式、时间戳等。
  • 摄像头信息发布

    • 除了图像数据外,usb_cam_node_exe 还会发布相机的相关信息,通常是通过 sensor_msgs/msg/CameraInfo 消息。这些信息包括相机的内参、畸变系数、投影矩阵等。
    • 这些数据通过 sensor_msgs/msg/CameraInfo 类型的消息发布到 /usb_cam/camera_info 话题。
  • 循环和定时发布

    • 通常,图像捕获和发布操作会在一个循环中进行,确保定期地从摄像头获取图像数据并发布。为了避免过度占用 CPU,通常会添加一定的延时(例如,每帧捕获后暂停一定时间)。

    • 发布间隔通常取决于相机的帧率和节点的配置。

      #include "rclcpp/rclcpp.hpp"
      #include "sensor_msgs/msg/image.hpp"
      #include "sensor_msgs/msg/camera_info.hpp"
      #include "cv_bridge/cv_bridge.h"
      #include "usb_cam/usb_cam.h" // USB摄像头的驱动

      class UsbCamNode : public rclcpp::Node
      {
      public:
      UsbCamNode()
      : Node("usb_cam_node")
      {
      // 创建一个发布者来发布图像数据
      image_pub_ = this->create_publisher<sensor_msgs::msg::Image>("/usb_cam/image_raw", 10);

      复制代码
          // 创建一个发布者来发布相机信息
          camera_info_pub_ = this->create_publisher<sensor_msgs::msg::CameraInfo>("/usb_cam/camera_info", 10);
          
          // 初始化摄像头(例如:设置设备文件、分辨率等)
          usb_cam_ = std::make_shared<UsbCam>("video0", 640, 480);
          
          // 循环捕获并发布图像
          timer_ = this->create_wall_timer(
              std::chrono::milliseconds(100),  // 定时器:每100ms执行一次
              std::bind(&UsbCamNode::publish_image, this)
          );
      }

      private:
      void publish_image()
      {
      // 从USB摄像头捕获一帧图像
      auto frame = usb_cam_->capture_frame();

      复制代码
          // 将图像转换为ROS消息(cv_bridge帮助进行转换)
          sensor_msgs::msg::Image::SharedPtr msg = cv_bridge::CvImage(std_msgs::msg::Header(), "bgr8", frame).toImageMsg();
          
          // 设置时间戳和其他信息
          msg->header.stamp = this->get_clock()->now();
          
          // 发布图像消息
          image_pub_->publish(*msg);
      
          // 发布摄像头信息(如果需要)
          sensor_msgs::msg::CameraInfo camera_info_msg;
          camera_info_msg.header.stamp = msg->header.stamp;
          camera_info_pub_->publish(camera_info_msg);
      }
      
      rclcpp::Publisher<sensor_msgs::msg::Image>::SharedPtr image_pub_;
      rclcpp::Publisher<sensor_msgs::msg::CameraInfo>::SharedPtr camera_info_pub_;
      std::shared_ptr<UsbCam> usb_cam_;  // 用于处理USB摄像头
      rclcpp::TimerBase::SharedPtr timer_;  // 定时器,用于定期捕获和发布

      };

      int main(int argc, char **argv)
      {
      rclcpp::init(argc, argv);
      rclcpp::spin(std::make_shared<UsbCamNode>());
      rclcpp::shutdown();
      return 0;
      }

内部结构详解

  • 节点创建UsbCamNode 继承自 rclcpp::Node,通过调用 this->create_publisher<sensor_msgs::msg::Image> 创建图像发布者。

  • USB 摄像头初始化 :通过 UsbCam 类(假设是自定义的摄像头驱动类)初始化摄像头设备(例如 /dev/video0)。

  • 定时器create_wall_timer 用于设置一个定时器,每 100 毫秒调用一次 publish_image 方法。该方法从摄像头捕获一帧图像,并将其转换为 ROS 2 消息后发布。

  • 图像数据转换 :使用 cv_bridge 库将 OpenCV 图像转换为 ROS 消息。cv_bridge::CvImage 会将 OpenCV 格式的图像(cv::Mat)转换为 ROS 图像消息(sensor_msgs::msg::Image)。

  • 消息发布 :每次捕获一帧图像后,图像消息通过 image_pub_ 发布到 /usb_cam/image_raw,并通过 camera_info_pub_ 发布相机信息到 /usb_cam/camera_info

    rqt_graph # 查看节点间的关系

7.服务:节点间的你问我答

复制代码
ros2 service

ros2 service list

# 通过命令行发送请求
ros2 service call /get_target_position learning_interface/srv/GetObjectPosition "get: True"

8.通信接口:数据传递的标准结构

复制代码
/opt/ros/humble/share目录下有.msg/.srv/.action文件格式的定义

#查看ros下所有的标准接口定义
ros2 interface list

# 查看某个指定格式的定义
ros2 interface show sensor_msgs/msg/Image 

新增一个 msg/Point3D.msg 文件

步骤1:在包的 msg 目录中新增消息文件
  1. 创建消息文件 :首先在你的 ROS 2 包中创建一个新的消息文件。例如,在 msg 目录下创建 Point3D.msg 文件。

    • 路径:your_package/msg/Point3D.msg

    • 内容示例:

      float64 x
      float64 y
      float64 z

    该文件定义了一个 Point3D 消息,包含 3 个浮动变量 xyz,通常用于表示三维坐标。

步骤2:修改 CMakeLists.txt
  1. 修改 CMakeLists.txt 文件 :你需要在 CMakeLists.txt 文件中增加新增的消息定义,并重新生成接口代码。

    假设你已经有了类似以下的代码:

    rosidl_generate_interfaces(${PROJECT_NAME}
    "msg/ObjectPosition.msg"
    "srv/AddTwoInts.srv"
    "srv/GetObjectPosition.srv"
    "action/MoveCircle.action"
    )

现在,新增你刚才创建的 Point3D.msg 文件:

复制代码
rosidl_generate_interfaces(${PROJECT_NAME}
  "msg/ObjectPosition.msg"
  "msg/Point3D.msg"  # 新增的消息文件
  "srv/AddTwoInts.srv"
  "srv/GetObjectPosition.srv"
  "action/MoveCircle.action"
)

这告诉 CMake 在编译时生成 Point3D.msg 的相关代码。

步骤3:修改 package.xml 文件
  1. 修改 package.xml 文件 :确保你已经在 package.xml 文件中声明了对消息生成的依赖。你需要确保已包含以下依赖项:

    <build_depend>rosidl_default_generators</build_depend>
    <exec_depend>rosidl_default_runtime</exec_depend>

步骤4:重新编译包
  1. 重新编译项目:完成上述修改后,运行以下命令来重新构建你的 ROS 2 包:

    colcon build --packages-select your_package

这将重新编译包,并根据你在 CMake 文件中指定的接口生成相应的代码(例如 C++ 和 Python 代码)。

步骤5:验证生成的文件
  1. 验证生成的文件 :在构建完成后,你可以检查生成的文件。在 build 目录下的相应包中,检查是否生成了 Point3D 的相关代码。

    • 如果是 C++,会生成 Point3D.hppPoint3D.cpp 文件。
    • 如果是 Python,会生成相应的 Python 文件。
相关推荐
西岸行者6 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意6 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码6 天前
嵌入式学习路线
学习
毛小茛6 天前
计算机系统概论——校验码
学习
babe小鑫6 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms6 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下6 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。6 天前
2026.2.25监控学习
学习
im_AMBER6 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J6 天前
从“Hello World“ 开始 C++
c语言·c++·学习