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 文件。
相关推荐
Dizzy.51719 分钟前
数据结构(查找)
数据结构·学习·算法
lalapanda43 分钟前
Unity学习part4
学习
啄缘之间2 小时前
4.6 学习UVM中的“report_phase“,将其应用到具体案例分为几步?
学习·verilog·uvm·sv
viperrrrrrrrrr75 小时前
大数据学习(49) - Flink按键分区状态(Keyed State)
大数据·学习·flink
red_redemption5 小时前
自由学习记录(36)
学习
黑金IT6 小时前
将Neo4j用于Python学习的创新方法
python·学习·neo4j
StickToForever6 小时前
第4章 信息系统架构(二)
经验分享·笔记·学习·职场和发展
19岁开始学习8 小时前
Go学习-入门
开发语言·学习·golang
ianozo8 小时前
CTF 代码学习日记 PHP
java·学习·php
大G哥9 小时前
用DeepSeek来帮助学习three.js加载3D太极模形
开发语言·前端·javascript·学习·ecmascript