---
tag : Vive 傳承資料
---
# Vive tracker 定位系統簡介
## Vive tracker 硬體介紹
* VIVE 移動定位器 3.0
也可以叫做 tracker 。

上圖是 tracker 的所有配件。
而 tracker 可選擇使用有線或無線的方式與電腦進行連結,如要使用無線連結,須將 dongle 插在電腦上。
* SteamVR 基地台 2.0
又稱作 lighthouse , 網路上有很多他的簡稱,像LH。

可視的範圍如下圖,能夠提供150 度的水平視野和 110 度的垂直視野。
傾角可以自己調整。

* 工作原理
藉由 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
```