HTC VIVE Tracker 作为一款优秀的VR设备,其有着非常好的定位精度,这时,我们就可以用这一设备来实现简单的开发,但是网上众多的教程中仅仅针对如何显示数据做了解读,但是,数据和我们自己编写的软件如何交互就成了一个一直困扰开发者的问题,这里,笔者提出一种解决思路来实现这一问题,关于HTC VIVE Tracker的简单介绍以及如何获取定位数据请参考博主的博客:HTC VIVE Tracker的二次开发(获取位置信息)
话不多说,这里先放上我们最终软件的一个效果图,供大家参考,这是笔者写的一个大软件,获取到两个Tracker的数据后效果大概就是这样的:
为了方便各位实际操作,笔者对原本的软件进行了精简,只实现数据接收这一块,大致的效果如下:软件下载地址:
这里大致说一下笔者的思路,就是在openvr获取到tracker的数据后我们通过TCP网口发送到本机地址上,再在自己写的软件上作为服务器端去接收vive传输的数据,这样我们就可以获取到tracker的数据并进行我们接下来的相关操作了,同时,这种方法也可以再连接其他设备(其他设备的数据发往本机地址,再在数据前加帧头,以便区分)
这里,首先是要对openvr的python文件添加tcp模块,使其收到数据后就向本机不停发送:修改后的opnver源代码(可以连接两台tracker,如果需要连接多台,其原理也是一致的) ,这里附上对于openvr的修改文件:openvr for double trackers
这里也附上核心代码:
HOST = 'localhost' # or 'localhost'
PORT = 5050
BUFSIZ = 16
ADDR=(HOST,PORT)
tcpCliSock = socket(AF_INET,SOCK_STREAM)
tcpCliSock.connect(ADDR)
while True:
#if interval:
#with open("calibration.txt", "w+") as text_file:
#while(True):
start = time.time()
txt = ""
for i in range(1,int(num_device) + 1):
for each in v.devices["tracker_1"].get_pose_euler():
txt += "%.4f" % each
txt += " "
sleep_time = interval - (time.time() - start)
if sleep_time > 0:
time.sleep(sleep_time)
for each in v.devices["tracker_2"].get_pose_euler():
txt += "%.4f" % each
txt += " "
#print(txt, file = text_file)
sleep_time = interval-(time.time()-start)
if sleep_time>0:
time.sleep(sleep_time)
txt="ABCD"+txt
print(txt);
tcpCliSock.send(txt.encode())
tcpCliSock.close()
在完成发送端后,我们就可以进行我们的软件的编写了,其实就是模拟TCP的服务器端,不断的从本机接收数据(也就是Tracker的回传数据),这里关于TCP的编程可以参考博主的文章:C++ 网络编程下的socket编程(TCP\UDP),连接下位机,这里就不再赘述Socket部分的内容了,我们接收到数据之后就要对我们接收到的数据进行处理,我们在之前的博客里也提到过,Tracker回传的信息是由六个自由度构成的,这里我们对数据接收到之后就需要进行切割处理,来提取我们需要的数据以供后期使用,这里附上核心代码:
int l_size = l_pCurSocket->Receive(l_buffer, PACKAGE_HEAD_SIZE);//读取包头;
CString head = "";
head.Format("%s", l_buffer);
if (l_size <= 0)
{
break;
}
Package_Head l_head;
memcpy(&l_head, l_buffer, PACKAGE_HEAD_SIZE);
memset(l_buffer, 0, SOCKET_TCP_BUFFER_SIZE);
int l_bodySize = l_pCurSocket->Receive(l_buffer, l_head.dataLength);//读取包体;
if (l_bodySize <= 0)
{
break;
}
HandleMsg(l_head.messageType, &l_buffer);
CString str;
str.Format("%s", l_buffer);
CString show = "";
CString data = "";
show = head + str;
CString strAll = show;
CStringList list;
int curPos = 0;
CString resToken = strAll.Tokenize(_T(" "), curPos);
if (resToken != _T(""))
list.AddTail(resToken);
x = resToken;
x_f = atof(x);
GetDlgItem(IDC_X)->SetWindowText(x);
resToken = strAll.Tokenize(_T(" "), curPos);
if (resToken != _T(""))
list.AddTail(resToken);
y = resToken;
y_f = atof(y);
GetDlgItem(IDC_Y)->SetWindowText(y);
resToken = strAll.Tokenize(_T(" "), curPos);
if (resToken != _T(""))
list.AddTail(resToken);
z = resToken;
z_f = atof(z);
GetDlgItem(IDC_Z)->SetWindowText(z);
resToken = strAll.Tokenize(_T(" "), curPos);
if (resToken != _T(""))
list.AddTail(resToken);
yaw = resToken;
yaw_f = atof(yaw);
GetDlgItem(IDC_YAW)->SetWindowText(yaw);
resToken = strAll.Tokenize(_T(" "), curPos);
if (resToken != _T(""))
list.AddTail(resToken);
pitch = resToken;
pitch_f = atof(pitch);
GetDlgItem(IDC_PITCH)->SetWindowText(pitch);
resToken = strAll.Tokenize(_T(" "), curPos);
if (resToken != _T(""))
list.AddTail(resToken);
roll = resToken;
roll_f = atof(roll);
GetDlgItem(IDC_ROLL)->SetWindowText(roll);
CString tracker = x + " " + y + " " + z + " " + yaw + " " + pitch + " " + roll;
AddToInfRec(tracker, IDC_EDIT1, TRUE, TRUE);
resToken = strAll.Tokenize(_T(" "), curPos);
if (resToken != _T(""))
list.AddTail(resToken);
x_1 = resToken;
x_1_f = atof(x_1);
GetDlgItem(IDC_X_1)->SetWindowText(x_1);
resToken = strAll.Tokenize(_T(" "), curPos);
if (resToken != _T(""))
list.AddTail(resToken);
y_1 = resToken;
y_1_f = atof(y_1);
GetDlgItem(IDC_Y_1)->SetWindowText(y_1);
resToken = strAll.Tokenize(_T(" "), curPos);
if (resToken != _T(""))
list.AddTail(resToken);
z_1 = resToken;
z_1_f = atof(z_1);
GetDlgItem(IDC_Z_1)->SetWindowText(z_1);
resToken = strAll.Tokenize(_T(" "), curPos);
if (resToken != _T(""))
list.AddTail(resToken);
yaw_1 = resToken;
yaw_1_f = atof(z_1);
GetDlgItem(IDC_YAW_1)->SetWindowText(yaw_1);
resToken = strAll.Tokenize(_T(" "), curPos);
if (resToken != _T(""))
list.AddTail(resToken);
pitch_1 = resToken;
pitch_1_f = atof(pitch_1);
GetDlgItem(IDC_PITCH_1)->SetWindowText(pitch_1);
resToken = strAll.Tokenize(_T(" "), curPos);
if (resToken != _T(""))
list.AddTail(resToken);
roll_1 = resToken;
roll_1_f = atof(roll_1);
GetDlgItem(IDC_ROLL_1)->SetWindowText(roll_1);
CString tracker1 = x_1 + " " + y_1 + " " + z_1 + " " + yaw_1 + " " + pitch_1 + " " + roll_1;
AddToInfRec(tracker1, IDC_EDIT3, TRUE, TRUE);
}
最后就是为我们的程序提供一个执行openvr的功能了,这里我们采用的是新建一个进程来执行,否则会导致接收数据之后整个程序的主进程就阻塞了:这里我们采用的是执行一个bat的批处理文件,当然读者也可以采用执行cmd命令的方法:
WinExec("tracker.bat", SW_HIDE);
而bat文件的内容其实就是对应openvr中我们python文件的位置,比如笔者的test.py文件放在了D盘下的double文件夹下,批处理文件就是如下所示:
@echo off
d:
cd double
test.py
好了,基本的教程就是这些了