unity+hololens2+hl2ss 各种相机帧以及传感器数据传输

之前使用过 HoloLens2-Unity-ResearchModeStreamer进行过原始数据传输,稍微修改了一下传输,拉了一下各个灰度相机其他数据就发现hololens2渲染帧会变得只有十几帧,这对目前项目需要的帧率影响很大,有人推荐了HL2SS这个库,测试了一下发现需要的数据很全,虽然压缩了部分但是影响不大,并且多个视频流一起跑hololens渲染帧数还是很高,所以记录一下使用过程。

一、拉取代码

git上拉取一下代码就行,这个库依赖的东西不多,相比HoloLens2-Unity-ResearchModeStreamer需要的库麻烦少了很多,但是需要vs2022,因为用到了一些新的ARM代码,VS2019测过完全跑不成功。

说一下工程文件夹:
1.etc文件夹存放了一些作者拉取的一些数据和hl2ss_report 这个论文,整理的很详细。
2.extensions就是c++和matlab的客户端代码,但是我主要还是python客户端。还有的Python 的 Zdepth 包装器,这个我暂时没用到。
3.hl2ss这个就是核心代码模块了,直接vs2022打开hl2ss.sln就可以了,主要分了两个工程,一个是根据代码直接生成UWP应用,对于只需要数据的人就够用了,可以编译后直接在hololens2中安装就行。plugin编译后的dll文件,可以导入在unity中调用。
4.tool文件夹就不知道干嘛的了
5.unity文件夹就是一个unity示例工程了,里面有编译过的dll文件和MRTK环境。
6.viewer就是python客户端接收数据并显示的代码了

二、unity配置

配置和之前的HoloLens2-Unity-ResearchModeStreamer unity配置是一样的,贴一下链接: 主机端实时获取Hololens2的RGBD数据流

第一步就是把unity->assets->scripts文件夹中的Hololens2SensorStreaming.cs、RemoteUnityScene.cs、hl2ss.cs几个文件添加到,然后就是把plugin编译好的dll放入unity了,dll设置如图在这里插入图片描述
最后就是把Hololens2SensorStreaming.cs放到一个物体上就可以了,这个脚本提供一些传感器是否读取开关,看需求了在这里插入图片描述

三、python客户端接收数据

pycharm或者vscode打开view文件夹,pyhon安装好包,在hololens里启动放入了dll的程序,运行client_stream_rm_vlc.py即可接收数据,默认模式为hl2ss.StreamMode.MODE_1,即接收帧数据即相机外参。出现客户端连接失败可以先确认hololens程序正常,各项正常的话可以参考一下这个Unity+Hololens2 HoloLens2-Unity-ResearchModeStreamer-master打开RGB相机总结
ok,只是研究性质的话到这步就可以结束了,后面主要是一些我目前阅读并修改代码添加传输的数据的一点代码方面的只知识,主要还是相机部分。

四、代码解析

1. python hlss2 客户端

只说说我目前看完的,客户端包装的很麻烦,但是几个流接收数据和解析数据基本上都是一两个函数

a.StreamPort 流端口

左前、左左、右前、右右几个灰度可见光相机其实是同样的接收代码,所以复制client_stream_rm_vlc.py修改下在这里插入图片描述
prot就能同时跑就能同时接收这几个相机的数据了。其他的几个传感器就基本分的很清楚了,就不多说了。

b.StreamMode流模式

目前作者提供了三个模式在这里插入图片描述MODE_0目前来说就是只传输视频流,MODE_1目前就是在流之外添加了各个灰度相机的RIG2WROLD矩阵(灰度相机),PV RGB可见光相机就是相机的焦距和焦点,深度相机就是深度图和AB矩阵。MODE_2就是传递了相机的参数了,比如灰度相机的外参内参和其他的数据,但是不传流了,所以才想在MODE_1上修改添加一些数据传输,VideoProfile这些就不多说的,流的格式和原始数据传输,关系到压缩率的问题,这些自己研究下吧。

b._packet和_unpacker 解包类

主要说下MODE_1的解包方法,关系到添加自己需要数据后如何解包。解包函数unpack主要还是贴一下我的注释和修改后的代码

   def unpack(self):        
        length = len(self._buffer)
        
        while (True):
            if (self._state == 0):  # 当前状态为0
                if (length >= 12): # 如果缓冲区长度大于等于12字节
                    header = struct.unpack('<QI', self._buffer[:12]) # 解析前12字节的头部信息
                    self._timestamp = header[0] # 提取时间戳
                    self._size = 12 + header[1]  # 计算数据包总大小
                    if (self._mode == StreamMode.MODE_1):
                        if(self._isvlc):
                            self._size += 112 # 加上标定球位置信息12x4 # 如果是 MODE_1 模式,数据包总大小需要再加上64字节(姿态信息大小)
                        else:
                            self._size += 64

                    self._state = 1  # 切换到状态1
                    continue
            elif (self._state == 1):
                if (length >= self._size): # 如果缓冲区长度大于等于数据包总大小
                    if (self._mode == StreamMode.MODE_1): # 如果是 MODE_1 模式,负载数据的结束位置需要减去姿态信息的大小
                        if (self._isvlc):
                            payload_end = self._size - 112
                            self._pose = np.frombuffer(self._buffer[payload_end:self._size - 48],dtype=np.float32).reshape((4, 4))  # 解析姿态信息
                            print("pos:", self._pose)
                            print("sphere pos:",np.frombuffer(self._buffer[self._size - 48:self._size], dtype=np.float32).reshape((4, 3)))
                        else:
                            payload_end = self._size - 64
                            self._pose = np.frombuffer(self._buffer[payload_end:self._size],dtype=np.float32).reshape((4, 4))  # 解析姿态信息


                    else:
                        payload_end = self._size # 否则,负载数据的结束位置即为数据包总大小
                    self._payload = self._buffer[12:payload_end]
                    self._buffer = self._buffer[self._size:] # 从缓冲区中移除已解析的数据
                    self._state = 0 # 切换到状态0
                    return True
            return False

为什么做判断,因为深度相机解包也是这段代码,我只想改vlc相机的代码,所以做了个判断。
如果添加了数据需要计算一下添加后的数据长度做下切割就可以了,稍微看下代码和我的注释应该就理解了。

2.dll中的c++服务端

等我后面有时间了再写

猜你喜欢

转载自blog.csdn.net/banjuhuaxianduo/article/details/138575257