1. 程式人生 > >HTC VIVE Tracker的二次開發(實際操作篇)附軟體

HTC VIVE Tracker的二次開發(實際操作篇)附軟體

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

好了,基本的教程就是這些了