--- tag : Vive 傳承資料 --- # Vive tracker 定位系統簡介 ## Vive tracker 硬體介紹 * VIVE 移動定位器 3.0 也可以叫做 tracker 。 ![](https://hackmd.io/_uploads/Bkyyg8yC2.png) 上圖是 tracker 的所有配件。 而 tracker 可選擇使用有線或無線的方式與電腦進行連結,如要使用無線連結,須將 dongle 插在電腦上。 * SteamVR 基地台 2.0 又稱作 lighthouse , 網路上有很多他的簡稱,像LH。 ![](https://hackmd.io/_uploads/Skw1DQ1Ch.png) 可視的範圍如下圖,能夠提供150 度的水平視野和 110 度的垂直視野。 傾角可以自己調整。 ![](https://hackmd.io/_uploads/HkZFhry0h.png) * 工作原理 藉由 LH 發射固定頻率的紅外光, 而LH 2.0發射的是有經過編碼的紅外光,因此tracker可以藉由判別編碼,得知紅外光是從哪台 LH 發射出來的。 [點這裡看動畫](http://n1.itc.cn/img8/wb/smccloud/recom/2015/09/07/144164015861410513.GIF) 動畫是 LH 1.0 的運作模式,但基本上與 LH 2.0 大同小異。皆是利用 tracker 上的 IR sensor 接收到紅外光的==時間差==進行推算 tracker 與 LH 2.0 之間的相對位置。 # Vive 系統的使用 我們使用的是 github 上的開源專案 [cntools/libsurvive](https://github.com/cntools/libsurvive) 利用此 library 內的 API 去取得 tracker 的資料,而我們在利用這些資料去做後續的應用。 ## 架設硬體環境 1. 將 LH 固定在一個定點,插上電源線開始運作。 2. 將 tracker 的 dongle 插在電腦上(或使用有線連接)後,按中心的按鈕亮起白燈,再長按一次直到燈開始閃爍,這時 tracker 會開始配對,配對成功後會顯示綠燈。(快沒電會顯示紅燈) ## libsurvive 範例 可以在[libsurvive/tools/ros_publisher/main.cc](https://github.com/cntools/libsurvive/blob/master/tools/ros_publisher/main.cc) 看到範例的使用方式。 ### 基本設置 最重要的地方是main的地方,這裡啟用的是high-level api,一切都在thead裡面進行。 * 初始化 actx. 先宣告使用 `survive_simple_init()`,將參數吃進來。 ```cpp= SurviveSimpleContext *actx = survive_simple_init(argc, argv); ``` 而 `main()` 是透過自訂的 `actx_from_ros()` ,來完成操作。 * 啟用與關閉線程. ```cpp= survive_simple_start_thread(actx); survive_simple_close(actx); ``` 要啟用前須先放一個`survive_simple_start_thread()`,才可以發布所有數據。 用完要關。 * 發布資料 接著進入while迴圈 * Step 1. 尋訪所有資料類型 ```cpp= survive_simple_wait_for_event(actx, &event) ``` 使用這個func 猜測可以尋訪所有的資料。 種類分成:pose、button、config object 又可以分成: ```cpp= enum SurviveSimpleObject_type { SurviveSimpleObject_UNKNOWN, SurviveSimpleObject_LIGHTHOUSE, SurviveSimpleObject_HMD, SurviveSimpleObject_OBJECT, SurviveSimpleObject_EXTERNAL }; ``` 用 `survive_simple_object_get_type()` 得到內部的類型。 * Step 2. 依照種類發布資料 ```cpp= switch (event.event_type){ case SurviveSimpleEventType_PoseUpdateEvent: case SurviveSimpleEventType_ButtonEvent: case SurviveSimpleEventType_ConfigEvent: } ``` * Step 3. 發布前要先更新 ```cpp= const struct SurviveSimplePoseUpdatedEvent *pose_event = survive_simple_get_pose_updated_event(&event); const struct SurviveSimpleButtonEvent *button_event = survive_simple_get_button_event(&event); const struct SurviveSimpleConfigEvent *configEvent = survive_simple_get_config_event(&event); ``` tracker 和 LH 都有各自的 serial number 。 LH 的 serial number 則是紅外線的編碼,因此每顆 LH 的 serial number 都是獨一無二的。 :::info tracker 與 LH 的 serial number |貼紙|serial number| |-|-| |A|LHR-94135635| |B|LHR-15565625| |C|LHR-662B1E75| |D|LHR-38203A4C| |E|LHR-E833C29B| |LH0|LHB-400B1A3E| |LH1|LHB-D4EEE18| ::: 總而言之基本取得 tracker 和 LH 資訊的架構長這樣,會用這個就好了。 ```cpp= int main(int argc, char** argv) { ros::init(argc, argv, "vive_simple"); ros::NodeHandle nh; SurviveSimpleContext* actx = survive_simple_init_with_logger(argc, argv, log_fn); if (actx == 0) // implies -help or similiar return 0; double start_time = OGGetAbsoluteTime(); survive_simple_start_thread(actx); // 取得初次讀到的設備 for (const SurviveSimpleObject* it = survive_simple_get_first_object(actx); it != 0; it = survive_simple_get_next_object(actx, it)) { printf("Found '%s'\n", survive_simple_object_name(it)); } // 開始進入迴圈內不斷取得資訊 while (keepRunning && survive_simple_wait_for_event(actx, &event) != SurviveSimpleEventType_Shutdown) { // 加入中止判斷 if (!ros::ok()) break; switch (event.event_type) { case SurviveSimpleEventType_PoseUpdateEvent: { for (const SurviveSimpleObject* it = survive_simple_get_first_object(actx); it != 0; it = survive_simple_get_next_object(actx, it)){ // 依序取得各項設備 的 pose 和 velocity SurvivePose pose; SurviveVelocity velocity; survive_simple_object_get_latest_pose(it, &pose); survive_simple_object_get_latest_velocity(it, &velocity); // 有可能是 tracker 也有可能是 LH // 如果有複數顆 tracker、LH ,則要看 serial number 來判斷是誰 // 取得 serial number string serial_number = survive_simple_serial_number(it); // 是tracker if (survive_simple_object_get_type(it) == SurviveSimpleObject_OBJECT) { // 確認 serial number if(serial_number == tracker1_serial_number){ // 確認是哪顆tracker後就可以使用 pose 的資料了,資料結構如下 // pose.Pos[]依序是xyz // pose.Pos[0], pose.Pos[1], pose.Pos[2] // pose.Rot[]則是四元數 // pose.Rot[1], pose.Rot[2], pose.Rot[3], pose.Rot[0] } } // 是LH else if (survive_simple_object_get_type(it) == SurviveSimpleObject_LIGHTHOUSE){ // LH 的資料使用也像 tracker 那樣 } } } survive_simple_close(actx); return 0; } ``` libsurvive 還有提供許多功能,可以去[survive_api.c](https://github.com/cntools/libsurvive/blob/master/src/survive_api.c)裡面查看!! 裡面還有許多沒被挖掘的好用功能!! ### 執行程式 試著執行 ros_publisher 的程式 * 成功 run 的結果。 ```cpp= ( 0.001) SimpleApi: Loaded drivers: GlobalSceneSolver, HTCVive Found 'LH0' Found 'LH1' ( 0.033) SimpleApi: Adding tracked object T20 from HTC (1676463604.420826) Found 'LH0' (1676463604.420831) Found 'LH1' (1676463604.448262) Found 'T20' LH1 LHB-400B1A3E (1676463604.749): -0.000000 0.799962 0.299096 0.001864 0.023649 -0.638960 -0.768874 ( 0.806) SimpleApi: Device T20 has watchman FW version 1592875850 and FPGA version 538/7/2; named 'watchman'. Hardware id 0x84020109 Board rev: 3 (len 56) (1676463605.222462) T20 received configuration of length 8064 type 3-7 ( 0.832) SimpleApi: Detected LH gen 2 system. ( 0.833) SimpleApi: OOTX not set for LH in channel 14; attaching ootx decoder using device T20 ( 0.872) SimpleApi: LightcapMode (T20) 1 -> 2 (4) T20 LHR-94135635 (1676463606.042): -0.049684 -0.012097 0.001747 -0.007373 0.818621 0.574287 0.000697 ``` 可以看到如果 tracker 有抓到 LH 的紅外光則會顯示 ```cpp= (1676463604.420826) Found 'LH0' (1676463604.420831) Found 'LH1' ``` :::success 會依照'LH0'、'LH1'的方式為 LH 進行編號 而'T20' 是 tracker 本身的代號 ::: * Config.json檔案的生成 關閉程式後可在`~/.config/libsurvive/config.json`這個地方看到config.json檔案被生成 內容大致如下,而這是自動生成的json檔,裡面的參數會自動生成 ```cpp= "LighthouseCount":"2", "DefaultPoser":"PoserTurveyTori", "RequiredTrackersForCal":"", "AllowAllTrackersForCal":"1", "ConfigPoser":"PoserTurveyTori", "TurveyToriDebug":"0" "lighthouse0":{ "index":"0", "id":"138441170", "pose":["0.000000","0.000000","0.000000","0.000000","0.000000","0.000000","0.000000"], "fcalphase":["-0.011757","0.020172"], "fcaltilt":["-0.003302","-0.001370"], "fcalcurve":["0.000323","-0.002600"], "fcalgibpha":["-4.316406","0.740723"], "fcalgibmag":["0.001188","-0.009270"] } "lighthouse1":{ "index":"-1" } ``` 如果 LH 的位置有大幅度的移動,或新增了新的 LH 接需要把 config.json 刪除。並重新執行程式生成,第一次生成會花比較多的時間!! ```cpp= rm ~/.config/libsurvive/config.json ```