如何在Unity场景中显示ROS2传输的JPEG压缩图像数据流
Unity 展示ROS2压缩图像流实现思路
基础实现步骤
- 第一步:准备UI显示载体
在Unity场景中创建UGUI Canvas,在Canvas下添加RawImage组件,调整该组件的大小、位置到你需要展示的屏幕区域即可,无需额外创建精灵或者3D物体。 - 第二步:数据接收与线程安全处理
ROS2的消息接收回调通常运行在非Unity主线程,不能直接在回调中操作纹理相关API,需要先做线程安全的数据缓存:- 定义一个线程安全的字节数组队列用于存储接收到的JPEG压缩帧数据
- 每次在ROS2回调中拿到
array('B', [xxx])格式的uint8字节数组后,直接存入队列即可
- 第三步:主线程纹理更新
在Unity主线程的Update生命周期中取出队列里的帧数据,复用同一个Texture2D对象解码更新,避免频繁创建对象导致GC卡顿:注意不要每次更新都新建Texture2D,复用单例纹理可以大幅提升性能
参考代码片段:using UnityEngine; using UnityEngine.UI; using System.Collections.Generic; public class ROSImageDisplay : MonoBehaviour { // Inspector面板赋值提前创建好的RawImage组件 public RawImage displayArea; private Texture2D _frameTexture; private readonly Queue<byte[]> _frameQueue = new Queue<byte[]>(); private readonly object _queueLock = new object(); void Start() { // 初始化纹理,初始宽高可以随便填,LoadImage会自动适配JPEG实际分辨率 _frameTexture = new Texture2D(2, 2, TextureFormat.RGB24, false); displayArea.texture = _frameTexture; } // ROS2消息回调里调用这个方法存入帧数据 public void OnReceiveCompressedFrame(byte[] jpegData) { lock (_queueLock) { // 如果只需要显示最新帧,可先清空队列里的旧数据 while (_frameQueue.Count > 1) { _frameQueue.Dequeue(); } _frameQueue.Enqueue(jpegData); } } void Update() { lock (_queueLock) { if (_frameQueue.Count == 0) return; byte[] latestFrame = _frameQueue.Dequeue(); // 自动解码JPEG数据到纹理 _frameTexture.LoadImage(latestFrame); // 因为原始数据是BGR8格式,如果加载后发现红蓝通道反转,放开下面注释做通道交换 /* Color32[] pixels = _frameTexture.GetPixels32(); for (int i = 0; i < pixels.Length; i++) { (pixels[i].r, pixels[i].b) = (pixels[i].b, pixels[i].r); } _frameTexture.SetPixels32(pixels); */ // 应用纹理修改完成更新 _frameTexture.Apply(); } } }
可选替代方案
如果你需要把图像展示在3D场景中而非UI层,可以直接创建一个Plane面片,把上面的_frameTexture赋值给Plane材质的mainTexture属性即可,调整Plane的大小位置对准相机就能得到同样的显示效果。
内容的提问来源于stack exchange,提问作者curious_electron
相关产品推荐
相关产品推荐

