怎么做能上谷歌网站吗,法国注册公司流程和费用,成都学校网站制作公司,网站开发与维护难吗前言
本系列教程旨在使用UE5配置一个具备激光雷达深度摄像机的仿真小车#xff0c;并使用通过跨平台的方式进行ROS2和UE5仿真的通讯#xff0c;达到小车自主导航的目的。本教程默认有ROS2导航及其gazebo仿真相关方面基础#xff0c;Nav2相关的学习教程可以参考本人的其他博…前言
本系列教程旨在使用UE5配置一个具备激光雷达深度摄像机的仿真小车并使用通过跨平台的方式进行ROS2和UE5仿真的通讯达到小车自主导航的目的。本教程默认有ROS2导航及其gazebo仿真相关方面基础Nav2相关的学习教程可以参考本人的其他博客Nav2代价地图实现和原理–Nav2源码解读之CostMap2D(上)-CSDN博客UE5系列教程UE5-C入门教程(一)使用代码创建一个指定目标的移动小球-CSDN博客第一期基于UE5和ROS2的激光雷达深度RGBD相机小车的仿真指南(一)—UnrealCV获取深度分割图像-CSDN博客本教程环境支持 UE5.43ubuntu 22.04 ros2 humble 上一节我们以及获取到了深度和分割图像的图像数据本节我们来看看如何使用rosbridge进行图像传输 ROSbrige-suite rosbridge 是一个用于在 ROS (Robot Operating System) 和其他编程语言或框架之间进行通信的桥梁。它允许开发者使用不同的编程语言如 Python、JavaScript、Java、MATLAB 等来与 ROS 系统进行交互而无需直接使用 ROS 的 C API。rosbridge 主要由两部分组成 ROS 端运行在 ROS 系统上的服务器负责与 ROS 系统进行交互。它可以将 ROS 消息、服务、动作等转换为可以通过网络传输的格式。客户端运行在非 ROS 系统上的客户端负责与 ROS 端通信并将接收到的数据转换为客户端语言或框架可以理解的形式。 rosbridge 支持多种通信协议包括 WebSocket、TCP 和 UDP。这使得它可以在不同的网络环境中工作无论是本地网络还是互联网。
安装与基础使用
这里推荐使用humble版本安装
sudo apt-get install ros-humble-rosbridge-suiterosbridge的使用也是非常方便
source /opt/ros/humble/setup.bash
ros2 launch rosbridge_server rosbridge_websocket_launch.xml 运行成功后终端会输出如下内容这里rosbridge默认会打开9090端口进行监听一会我们发送信息也只需要发送到这里即可 尝试使用发送ROSbrige一张图片
由于我们的UE5仿真及其数据捕获程序运行在windows11而我们的只要Nav2导航处理程序在ubuntu端这里我们就需要使用ROSbrige进行通讯
1. ip查询
在进行ROSbrige的websocket通讯之前在保证win11和ubuntu处于同一局域网的前提下我们需要知道ubuntu端的ip地址在ubuntu终端输入ip adrr show查看ip地址,这里我的虚拟机ip是192.168.137.129
2. 消息类型确认
在查询好ip地址后我们需要确定传输的消息类型这里我们传输的是图像类型那我们就选择最常见的sensor_msgs/msg/image类型的图片我们来查询这个消息下有什么
ros2 interface show sensor_msgs/msg/Image我们会得到以下输出 玩过ROS2的朋友都不陌生吧那我简单说明一下 uint32 height:图像的高度uint32 width图像的宽度string encoding像素的编码方式包括通道的含义、顺序和大小。uint8 is_bigendian表示图像数据是否使用大端字节序。在大多数现代系统中y一般是 0小端字节序。uint32 step图像的完整行长度以字节为单位。这通常是 width * channels * bytes_per_channel。例如对于宽度为 640 像素、3 个通道如 RGB 图像的图像步长将是 640 * 3 1920 字节。uint8[] data实际的图像数据矩阵。其大小是 step * rows即步长乘以行数。
编写Win11发送端
那我们我们就来根据上述sensor_msgs/msg/image所需要的消息类型我们来编写发送端
import websocket
import cv2
import base64
import json
import numpy as np
# Ubuntu的IP地址
ubuntu_ip 192.168.137.129 # 创建WebSockets连接
ws websocket.create_connection(fws://{ubuntu_ip}:9090) image_path rC:\Users\lzh\Desktop\UE5_ROS2_project\camera\lit.png
imgcv2.imread(image_path)
print(img.shape)
imgimg.astype(np.uint8)
# 将图像数据转换为Base64编码的字符串
encoded_string base64.b64encode(img).decode(utf-8)
# sensor_msgs/msg/image的JSON表示
msg { op: publish, topic: /image, msg: { data: encoded_string, height: 480, width: 640, step: 640 * 3 , encoding: bgr8 }
}
while True:
# 发送消息 ws.send(json.dumps(msg)) # 关闭连接
ws.close()上述代码很简单相信大家都能看得懂需要注意的是data需要是json可迭代对象所以这里转换为base64
编写ubuntu接受端data
接收端我采用cpp创建了一个ament_cmake的功能包那么接收端就更简单了这里我们只创建一个订阅用于广播image的类型我们把显示交给rviz2直接看代码
#include rclcpp/rclcpp.hpp
#include sensor_msgs/msg/image.hppclass ImageSubscriber : public rclcpp::Node
{
public:ImageSubscriber(): Node(image_subscriber){subscription_ this-create_subscriptionsensor_msgs::msg::Image(image, 10, std::bind(ImageSubscriber::image_callback, this, std::placeholders::_1));}private:void image_callback(const sensor_msgs::msg::Image::SharedPtr msg) const{RCLCPP_INFO(this-get_logger(), Received image);}rclcpp::Subscriptionsensor_msgs::msg::Image::SharedPtr subscription_;
};int main(int argc, char * argv[])
{rclcpp::init(argc, argv);auto node std::make_sharedImageSubscriber();rclcpp::spin(node);rclcpp::shutdown();return 0;
}
运行与展示 值得一提的是如果你只运行了win11的发送端和ubuntu的rosbridge不运行接收端那么你可能会得到一下错误 这是由于在 ROS 中主题必须先被广告然后才能接收消息。这意味着在尝试发布消息之前需要确保有一个节点正在监听 /image 主题并且已经广告了该主题。 我们打开rviz2选择Add,并根据话题选择图像显示插件 我们就得到了以下画面 UE5核心类创建及实时数据传输
为了更好的数据传输我们这里进行封装,这里我们写一个发布者Publisher基类connection将传入外部的websocket对象非常常见的设计模式运用这里就不多说明了
class Publisher: def __init__(self,connection,topic): self.connectionconnection self.topic topic def publish(self,msg): pub_msg { op: publish, topic: self.topic, msg:msg } self.connection.send(json.dumps(msg))
class ImagePublisher(Publisher): def __init__(self,connection,topic,compressed_scale): self.connectionconnection self.topic topic self.compressed_scalecompressed_scaledef publish(self,image): if image is None: print(image is None!) return h,wimage.shape[0],image.shape[1] new_hself.compressed_scale*hnew_wself.compressed_scale*wimagecv2.resize(image,(new_w,new_h))print(h:,new_h,,w:,new_w,image is publishing) image image.astype(np.uint8) # 将图像数据转换为Base64编码的字符串 encoded_string base64.b64encode(image).decode(utf-8) msg { op: publish, topic: self.topic, msg: { data: encoded_string, height: new_h, width: new_w, step: new_w * 3, encoding: bgr8 } } self.connection.send(json.dumps(msg))同时我们继续书写整个Win11端的核心类UE5MsgCenter这里我们先测试一下功能
class UE5MsgCenter: def __init__(self,ubuntu_remote_ip_): self.ws websocket.create_connection(fws://{ubuntu_remote_ip_}:9090) self.ue5_cam_centerUE5CameraCenter() self.image_pubImagePublisher(self.ws,topic/image,compressed_scale0.5) self.object_mask_image_pubImagePublisher(self.ws,topic/object_mask_image,compressed_scale0.5) def __del__(self): self.ws.close() def run(self): while True: self.image_pub.publish(self.ue5_cam_center.get_camera_data(lit)) self.object_mask_image_pub.publish(self.ue5_cam_center.get_camera_data(object_mask))然后我们再次修改ubuntu订阅端的cpp代码
#include rclcpp/rclcpp.hpp
#include sensor_msgs/msg/image.hppclass ImageSubscriber : public rclcpp::Node
{
public:ImageSubscriber(): Node(image_subscriber){rawImageSub this-create_subscriptionsensor_msgs::msg::Image(image, 10, std::bind(ImageSubscriber::rawImageCB, this, std::placeholders::_1));objMaskImageSub this-create_subscriptionsensor_msgs::msg::Image(object_mask_image, 10, std::bind(ImageSubscriber::objMaskImageCB, this, std::placeholders::_1));}private:void rawImageCB(const sensor_msgs::msg::Image::SharedPtr msg) const{RCLCPP_INFO(this-get_logger(), Received image);}void objMaskImageCB(const sensor_msgs::msg::Image::SharedPtr msg) const{RCLCPP_INFO(this-get_logger(), Received image);}rclcpp::Subscriptionsensor_msgs::msg::Image::SharedPtr rawImageSub;rclcpp::Subscriptionsensor_msgs::msg::Image::SharedPtr objMaskImageSub;
};int main(int argc, char * argv[])
{rclcpp::init(argc, argv);auto node std::make_sharedImageSubscriber();rclcpp::spin(node);rclcpp::shutdown();return 0;
}
启动顺序
这里有必要说明一下各个平台程序和软件的开启顺序 Win11的UE5记得按下开始仿真本关卡!!!ubuntu的cpp接收端(用于广播消息类型)ubuntu的rosbridge服务器Win11的python的UE5MsgCenterubuntu的rviz2
结果展示 移动我们在UE5中的观测者小球同样我们收到消息
深度图像特殊处理
这里深度图像Win11发送端我们需要进行特殊处理我们重新写一个新的类深度图像为灰度图像是单通道故设置 step: new_w,encoding: “mono8” # 8位灰度图的编码
class DepthImagePublisher(Publisher): def __init__(self,connection,topic,compressed_scale): self.connectionconnection self.topic topic self.compressed_scalecompressed_scale def publish(self,image): if image is None: print(image is None!) return h,wimage.shape[0],image.shape[1] new_hint(self.compressed_scale*h) new_wint(self.compressed_scale*w) imagecv2.resize(image,(new_w,new_h)) print(h:,new_h,,w:,new_w,image is publishing) image image.astype(np.uint8) # 将图像数据转换为Base64编码的字符串 encoded_string base64.b64encode(image).decode(utf-8) msg { op: publish, topic: self.topic, msg: { data: encoded_string, height: new_h, width: new_w, step: new_w, encoding: mono8 } } self.connection.send(json.dumps(msg))我们得到如下 完整代码
UE5MsgCenter.py
import websocket
import cv2
import base64
import json
import numpy as np
from UE5CameraCenter import UE5CameraCenter
class Publisher: def __init__(self,connection,topic): self.connectionconnection self.topic topic def publish(self,msg): pub_msg { op: publish, topic: self.topic, msg:msg } self.connection.send(json.dumps(msg))
class DepthImagePublisher(Publisher): def __init__(self,connection,topic,compressed_scale): self.connectionconnection self.topic topic self.compressed_scalecompressed_scale def publish(self,image): if image is None: print(image is None!) return h,wimage.shape[0],image.shape[1] new_hint(self.compressed_scale*h) new_wint(self.compressed_scale*w) imagecv2.resize(image,(new_w,new_h)) print(h:,new_h,,w:,new_w,image is publishing) image image.astype(np.uint8) # 将图像数据转换为Base64编码的字符串 encoded_string base64.b64encode(image).decode(utf-8) msg { op: publish, topic: self.topic, msg: { data: encoded_string, height: new_h, width: new_w, step: new_w, encoding: mono8 } } self.connection.send(json.dumps(msg))
class ImagePublisher(Publisher): def __init__(self,connection,topic,compressed_scale): self.connectionconnection self.topic topic self.compressed_scalecompressed_scale def publish(self,image): if image is None: print(image is None!) return h,wimage.shape[0],image.shape[1] new_hint(self.compressed_scale*h) new_wint(self.compressed_scale*w) imagecv2.resize(image,(new_w,new_h)) print(h:,new_h,,w:,new_w,image is publishing) image image.astype(np.uint8) # 将图像数据转换为Base64编码的字符串 encoded_string base64.b64encode(image).decode(utf-8) msg { op: publish, topic: self.topic, msg: { data: encoded_string, height: new_h, width: new_w, step: new_w * 3, encoding: bgr8 } } self.connection.send(json.dumps(msg)) class UE5MsgCenter: def __init__(self,ubuntu_remote_ip_): self.ws websocket.create_connection(fws://{ubuntu_remote_ip_}:9090) self.ue5_cam_centerUE5CameraCenter() self.image_pubImagePublisher(self.ws,topic/image,compressed_scale0.5) self.object_mask_image_pubImagePublisher(self.ws,topic/object_mask_image,compressed_scale0.5) self.depth_image_pubDepthImagePublisher(self.ws,topic/depth_image,compressed_scale0.5) def __del__(self): self.ws.close() def run(self): while True: self.image_pub.publish(self.ue5_cam_center.get_camera_data(lit)) self.object_mask_image_pub.publish(self.ue5_cam_center.get_camera_data(object_mask)) self.depth_image_pub.publish(self.ue5_cam_center.get_camera_data(depth)) def main(): webs_server UE5MsgCenter(192.168.137.129) webs_server.run()
if __name__ __main__: main()小结
本节我们介绍了如何使用rosbridge对UE5和ROS2进行通讯下一小节我们将谈谈UE5激光雷达的仿真如有错误欢迎指出~