教程-使用Unity进行可视分析(6)

关于三维可视化视图的系列教程其六,本节整理一些技术细节问题,如跨设备的实现、性能优化、测试等。

Posted by 秦浩凯(Haokai Qin) on 2022-03-01
Estimated Reading Time 6 Minutes
Words 1.6k In Total
Viewed Times

本系列教程将在2022年新年期间进行更新,敬请关注!欢迎批评指正!
目前前五章已经完成

目录

前    言:为什么、以及什么场景下要使用unity进行可视化?
第一章:现有的unity可视化工具包
第二章:自行设计与实现unity可视化视图
第三章:设计与实现一个可视分析系统(上)
第四章:设计与实现一个可视分析系统(下)
第五章:设计与实现一个可视分析系统(技术问题专题)

动机

应实验室需要,将一部分实现思路进行整理。

如何实现不同设备间的通信?

设计部分请参考第三章,我使用了消息服务器,将设备交互状态转发到其他设备上。

Hololens 端

在github上可选择的方法有很多,但未必好用,这里推荐PythonTCPToHololens这个项目。

程序在unity环境运行(C# standard),和hololens环境运行(UWP)时,在一些需要权限级别的部分具有区别。 使用#endif等代码

1
2
3
4
5

#if !UNITY_EDITOR && UNITY_METRO
using System.Threading.Tasks;
using Windows.Storage;
#endif

*控制环境和代码的对应关系。

Web 端

这里我使用了websocket,是对IMIL DRESDEN的MARVIS的模仿。

在python生成的.html文件中,srcipt段加入类似下面的语句实现从web到服务器的通信:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

function WebSocketTestMsg(msg)
{
if ("WebSocket" in window)
{
// alert("您的浏览器支持 WebSocket!");

// 打开一个 web socket
var ws = new WebSocket("ws://192.168.1.127:8181");
//var ws = new WebSocket("ws://echo.websocket.org");

ws.onopen = function()
{
// alert("数据发送中...");
// Web Socket 已连接上,使用 send() 方法发送数据
ws.send(msg);
// alert("数据发送中...完成");
};

ws.onmessage = function (evt)
{
var received_msg = evt.data;
// alert("数据已接收..."+received_msg);
};

ws.onclose = function()
{
// 关闭 websocket
// alert("连接已关闭...");
};
}

else
{
// 浏览器不支持 WebSocket
alert("您的浏览器不支持 WebSocket!");
}
}

// 这一长串是pythonecharts生成的图表的id
chart_699a070a6c414030ab0fcfe0c1b3be82.on('click', function(params) {

// 需要一点WEB基础知识,可以在RUNOOB上,或者echarts官方文档里先练习一下
// WEB交互控制,以及交互参数的获取

//alert( chart_5965cd9bd1084800846ed7fc47c8e511.convertFromPixel('geo', [params.event.offsetX, params.event.offsetY]));
// alert(params.name);

//
WebSocketTestMsg("Example" + params.name[6] + params.name[7]);
});

服务器端

我使用python作为消息服务器。
由于一些技术原因,hololens和服务器的websocket连接并不稳定,不得不使用TCP通信。

参考了 https://zhuanlan.zhihu.com/p/101586682 ,设置了多线程处理的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

from websocket_server import WebsocketServer
import asyncio

# 新连接
def new_client(client, server):
print("New client connected and was given id %d" % client['id'])
server.send_message_to_all("Hey all, a new client has joined us")

# 断开连接
# Called for every client disconnecting
def client_left(client, server):
print("Client(%d) disconnected" % client['id'])

# Websocket 服务器收到消息
# Called when a client sends a message
def message_received(client, server, message):
if len(message) > 200:
message = message[:200]+'..'
print("Client(%d) said: %s" % (client['id'], message))
server.send_message(client, "got your message")
server.send_message_to_all("Client(%d) said: %s" % (client['id'], message))

pyc(message, 9090)
# 直接给hololens发websocket消息不好用,因为websocket使用域名连接。这里加了一段TCP


# 转发给hololens,用TCP
# hololens那头最多接收256字符长度信息,但不够的话hololesn无法读取显示,需要填充
# 多线程部分参考了zhihu设计,连接在最下方
def pyc(msg, p):
message = msg
while(len(message) < 256):
message += '^'
# loop = asyncio.get_event_loop()
# loop.run_until_complete(tcp_echo_clientP(message, p))
# loop.close()
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(tcp_echo_clientP(message, p, loop))

# 异步通信实现无间断的TCP通信
# 指定了hololens的IP地址,换了的话记得修改
async def tcp_echo_clientP(message, port, loop):
reader, writer = await asyncio.open_connection('192.168.1.141', port)

print('Send: %r' % message)
writer.write(message.encode())

data = await reader.read(64)
print('Received: %r' % data.decode())

print('Close the socket')
writer.close()


PORT = 8181
server = WebsocketServer(PORT, '192.168.1.116')
# websocket server地址。改成服务器当前IP。
server.set_fn_new_client(new_client)
server.set_fn_client_left(client_left)
server.set_fn_message_received(message_received)
server.run_forever()

定义交互状态表

为了在其他设备上同步更新,需要定义消息格式并处理。
处理部分的逻辑可以参考手动实现stoi()这类处理字符串的函数,这里不多展开。

按照
设备编号-行为-交互目标信息的格式定义消息。
例如:
HLS1^SLECT^BARCT^DAT93

性能优化

数据IO的优化

目前采用的方法是将数据存放在场景内读取-调用(以绕开UWP繁复的权限控制)。一个可能的修改是改为存放在数据库中,根据用户行为读取数据,但这要比现有流程慢。

动画逻辑的优化

得益于SVG,重绘整个二维视图的时间开销是可以忍受的。但受到hololens 设备性能的限制,重绘相当数量的3D物体可能会导致比较严重的卡顿。

一个很常用的方法是:只更新感兴趣区域的内容(只有交互行为,或者高优先级事件,才触发更新)。

另外特别需要提及一个细节,即在MRTK Unity Inspector中,交互行为响应函数应该在“Ended”或“dwell”状态下调用。立即调用很可能会导致卡顿(视函数具体内容而定)。

功能测试

如前文所言,系统中具有多个视图,且视图之间会联动。
测试过程中应该首先确保无交互状态下视图正常表现; 其次是交互行为及联动部分。

注意Unity Player和真机的不同

MRTK在Unity环境中,将交互行为映射到键盘/鼠标操作上。
即使unity测试通过,在真机也未必好用,这一点在调试交互方法时尤为明显。

我的建议是检查当前设置和mrtk官方文档中是否有不对应的地方,或者参考mrtk demo以更快地检查区别。

此外,由于UWP程序独特的权限控制,一些读写文件操作可能不会在hololens正常运行,需要谨慎调试,并使用#endif控制环境(见第一小节)。

AR空间感知问题/对齐AR内容与真实内容

https://stackoom.com/question/4JglD
StackOverflow镜像页
https://stackoom.com/question/38WP3
另一个镜像问题
https://docs.microsoft.com/en-gb/windows/mixed-reality/develop/advanced-concepts/qr-code-tracking-overview
MS官方文档


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !