从gazebo启动的world发布的topic中,获取图像数据并保存到本地

文章描述了在ROS2环境下,通过gazebo模拟器加载带有图像传感器的场景,检查image_raw话题,然后使用rclpy库订阅该话题,将接收到的图像数据转换为OpenCV格式并每隔0.5秒保存至本地。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1.  首先, xxx.world 里有图像传感器。

2. gazebo启动场景

3. 查看image的topic是否存在

4. 查看 /camera/image_raw 详细信息

5. 使用 ROS2的客户端库(python) rclpy 获取图像数据, 并每隔0.5秒,保存一张到本地.


1.  首先, xxx.world 里有图像传感器。

  <sensor name='camera' type='depth'>
	<visualize>0</visualize>
	<update_rate>15</update_rate>
	<camera>
	  <image>
		<format>B8G8R8</format>
		<width>640</width>
		<height>480</height>
	  </image>
	  <clip>
		<near>0.28</near>
		<far>10</far>
	  </clip>
	  <horizontal_fov>1.047</horizontal_fov>
	</camera>
	<plugin name='camera_controller' filename='libgazebo_ros_camera.so'>
	  <frame_name>camera_link_optical</frame_name>
	  <min_depth>0.28</min_depth>
	  <max_depth>6.0</max_depth>
	</plugin>
	<pose>0.21 0 0.1 0 -0 0</pose>
  </sensor>
</link>

2. gazebo启动场景

$ gazebo xxx.world

3. 查看image的topic是否存在

$ ros2 topic list -t
# result:/camera/image_raw [sensor_msgs/msg/Image] 存在 

运行结果:

/camera/camera_info [sensor_msgs/msg/CameraInfo]
/camera/depth/camera_info [sensor_msgs/msg/CameraInfo]
/camera/depth/image_raw [sensor_msgs/msg/Image]
/camera/image_raw [sensor_msgs/msg/Image]
/camera/points [sensor_msgs/msg/PointCloud2]
...

4. 查看 /camera/image_raw 详细信息

$ ros2 topic echo /camera/image_raw
# result: height: 480 width: 640 encoding: bgr8

运行结果:  

header:
  stamp:
    sec: 2077
    nanosec: 790000000
  frame_id: camera_link_optical
height: 480
width: 640
encoding: bgr8
is_bigendian: 0
step: 1920
data:
- 178
...

5. 使用 ROS2的客户端库(python) rclpy 获取图像数据, 并每隔0.5秒,保存一张到本地.

import os
import sys
import time
# ROS2的客户端库(python) rclpy
import rclpy
from rclpy.qos import qos_profile_sensor_data
from rclpy.node import Node
# image-----below----
# topic sensor_msgs/msg/Image to cv2
import cv2
from sensor_msgs.msg import Image
from cv_bridge import CvBridge

# import snoop


def cv2_imshow(win_name: str, img_mat) -> None:
    """
    显示接受到的图像数据
    :param win_name:
    :param img_mat:
    :return:
    """
    cv2.imshow(win_name, img_mat)
    # cv2.waitKey(0)  # delay – Delay in milliseconds. 0 is the special value that means “forever”.
    if cv2.waitKey(1) & 0xFF == ord('q'):  # 当你按下q键时,显示图片的窗口被关掉,并打印出I'm done,结束程序
        print("I'm done")
        sys.exit(0)


class ImageNode(Node):
    # Node constructor
    def __init__(self) -> None:
        super().__init__("image_node")

        self.print_count = 1
        self.img_count = 1
        self.cv_bridge = CvBridge()

        # get from topic /camera/image_raw [sensor_msgs/msg/Image]
        self._image_sub = self.create_subscription(
            Image,                   # topic_type : sensor_msgs/msg/Image
            "/camera/image_raw",     # topic_name :  /camera/image_raw
            self.image_cb,           # image_listener_callback
            qos_profile_sensor_data  # “queue size” is 10. Queue size is a required QoS
        )

    # @snoop
    def image_cb(self, msg: Image) -> None:
        """
        image listener callback
        :param msg:
        :return:
        """
        if self.print_count:
            print("msg.header :  {}".format(
                msg.header))  # msg.header: std_msgs.msg.Header(stamp=builtin_interfaces.msg.Time(sec=23495, nanosec=888000000), frame_id='camera_link_optical')
            print("msg.encoding :  {}".format(msg.encoding))  # msg.encoding: bgr8
            # print("msg.data : \n {}".format(msg.data))
            print("msg.height :  {}".format(msg.height))  # msg.height: 480
            print("msg.width : {}".format(msg.width))  # msg.width: 640
            self.print_count = 0

        # record start time
        # start_time = time.perf_counter()

        # convert to cv image & predict
        cv_image = self.cv_bridge.imgmsg_to_cv2(msg, "bgr8")
        save_dir = "save"
        img_path = os.path.join(save_dir, str(self.img_count).zfill(4) + ".jpg")
        cv2.imwrite(filename=img_path, img=cv_image)
        print(img_path)
        self.img_count += 1
        time.sleep(0.5)  # 0.5s

        # end_time = time.perf_counter()
        # print(f"{end_time - start_time = }")

        # display
        # cv2_imshow("image_raw", cv_image)


def main(args=None):
    rclpy.init(args=args)
    node = ImageNode()
    rclpy.spin(node)
    rclpy.shutdown()
    node.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    main()

### 设置发布摄像头传感器话题 在 ROSGazebo 中设置发布摄像头传感器的话题涉及多个方面。为了实现这一目标,需按照特定流程操作。 #### 创建 URDF 文件中的摄像头模型 URDF (Unified Robot Description Format) 是一种 XML 格式的文件,用来描述机器人结构及其组件。对于摄像头而言,在 URDF 文件中应包含 `<sensor>` 标签来指定传感器类型为 `camera` 为其命名[^3]。例如: ```xml <robot name="my_robot"> <!-- 定义链接 --> <link name="base_link"/> <!-- 添加摄像头链节 --> <joint name="camera_joint" type="fixed"> <parent link="base_link"/> <child link="camera_link"/> <origin xyz="0 0 1.5" rpy="0 0 0"/> </joint> <link name="camera_link"/> <!-- 描述摄像头参数 --> <gazebo reference="camera_link"> <sensor type="camera" name="head_camera"> <always_on>1</always_on> <update_rate>30.0</update_rate> <camera name="head_camera"> <horizontal_fov>1.047</horizontal_fov> <image> <width>800</width> <height>600</height> <format>R8G8B8</format> </image> <clip> <near>0.1</near> <far>100.0</far> </clip> <noise> <type>gaussian</type> ... </noise> </camera> <!-- 加载插件 --> <plugin filename="libgazebo_ros_camera.so" name="gazebo_ros_head_camera_controller"> <frame_name>camera_link</frame_name> <image_topic_name>/camera/image_raw</image_topic_name> <camera_info_topic_name>/camera/camera_info</camera_info_topic_name> <updateRate>30.0</updateRate> </plugin> </sensor> </gazebo> </robot> ``` 上述代码片段展示了如何创建一个名为 "head_camera" 的摄像头,设置了它的基本属性以及所使用的插件名称和路径。这允许该设备能够被 Gazebo 正确识别ROS 进行交互。 #### 启动 GazeboROS 节点 完成 URDF 文件编写之后,可以通过启动脚本加载此模型到 Gazebo 当中。通常情况下会有一个 launch 文件负责调用必要的命令和服务以初始化整个系统。下面是一个简单的 Python Launch 文件例子: ```python from launch import LaunchDescription from launch.actions import IncludeLaunchDescription, DeclareLaunchArgument from launch.launch_description_sources import PythonLaunchDescriptionSource from ament_index_python.packages import get_package_share_directory import os def generate_launch_description(): pkg_gazebo_ros = get_package_share_directory('gazebo_ros') world_file_path = 'worlds/empty.world' urdf_model_path = 'path/to/my_robot.urdf' return LaunchDescription([ # 声明世界文件作为输入参数 DeclareLaunchArgument( 'world', default_value=[os.path.join(pkg_gazebo_ros, world_file_path), ''], description='SDF world file'), # 使用 gzserver 和 gzclient 来运行 Gazebo 模拟器 IncludeLaunchDescription( PythonLaunchDescriptionSource([pkg_gazebo_ros, '/launch/gzserver.launch.py']), launch_arguments={'world': world}.items() ), IncludeLaunchDescription( PythonLaunchDescriptionSource([pkg_gazebo_ros, '/launch/gzclient.launch.py']) ), # 发布机器人状态信息 Node(package='robot_state_publisher', executable='robot_state_publisher', output='screen', parameters=[{'use_sim_time': True, 'robot_description': Command(['xacro ', urdf_model_path])}]), # 将机器人放入仿真环境中 Node(package='gazebo_ros', executable='spawn_entity.py', arguments=['-topic', 'robot_description','-entity','my_robot'],output='screen'), ]) ``` 这段代码实现了启动 Gazebo 场景、加载自定义的机器人模型(即带有摄像头的),通过 `robot_state_publisher` 发布关节状态等基本信息给其他节点使用。 一旦成功执行以上步骤,则可以在 `/camera/image_raw` 或者由自己设定的主题上接收到来自仿真的图像消息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值