You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Unity场景中显示ROS2传输的JPEG压缩图像数据流

Unity 展示ROS2压缩图像流实现思路

基础实现步骤

  • 第一步:准备UI显示载体
    在Unity场景中创建UGUI Canvas,在Canvas下添加RawImage组件,调整该组件的大小、位置到你需要展示的屏幕区域即可,无需额外创建精灵或者3D物体。
  • 第二步:数据接收与线程安全处理
    ROS2的消息接收回调通常运行在非Unity主线程,不能直接在回调中操作纹理相关API,需要先做线程安全的数据缓存:
    1. 定义一个线程安全的字节数组队列用于存储接收到的JPEG压缩帧数据
    2. 每次在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 04:36:01