import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
VerseGrip 触控笔入门指南
VerseGrips 为Inverse3 、Inverse3X 和 MinVerse 添加了方向追踪功能。本文将介绍 VerseGrip 触控笔的设置步骤及其功能。
VerseGrip 触控笔简介
VerseGrip 触控笔采用独有的高速无线通信技术,可实现方向追踪以及手持状态下的输入/输出(I/O)功能。其设计旨在广泛应用于各种场景。
主要功能如下:
- 2个用户输入按钮,
- 1个校准按钮,
- 1个电源/待机按钮
- 2个状态LED指示灯
- USB-C 充电端口
- 最高 1 KHz 的无线通信速率
- 电池续航时间 10~12 小时
- 在同一频段内,一个配对设备最多可与8支VerseGrip触控笔配对

客户支持
如果您对设备或其操作有任何疑问或顾虑,请联系支持团队。VerseGrip Stylus 内部采用了复杂的专有无线技术。一旦拆开设备,保修将失效,且设备可能无法正常工作。为避免交货时间延长或产生高额维修费用,请切勿拆开设备。
VerseGrip 触控笔的设置与校准
本节将介绍连接适配器、开启 VerseGrip Stylus 电源以及进行校准的步骤。
设置
- 请将该设备插入计算机的USB端口。
- 请将手柄放在不会移动的平坦表面上。确保按钮朝上,球形连接器朝向屏幕。
开机与校准
- 请按一下电源按钮。状态指示灯会先亮起红色,随后变为黄色,这表明设备内的IMU正在进行自校准。
- 自动校准完成后,触控笔将连接到计算机上。
- 当Stylus成功连接并开始无线传输数据时,状态指示灯会闪烁绿色。
- 当电量非常低时,VerseGrip Stylus 会每 2 分钟自动关机。请立即充电。
重新校准
校准过程会重置VerseGrip的坐标系,使校准过程中握持设备时的方向成为新的基准轴。
- 若要重新校准,请在所需位置握住触控笔,并按住校准按钮3秒钟,或者一直按住该按钮,直到模拟中的方向发生变化为止。
以下是未进行校准和已进行校准时设备运行情况的对比演示。在演示中,必须在执行校准之前将VerseGrip保持在下图所示的姿态。

待机模式和关机
- 按一下电源按钮,设备将返回待机模式。状态指示灯开始闪烁白色,数据传输将停止。
- 要关闭VerseGrip触控笔的电源,请按住电源按钮5秒钟,然后松开。松开按钮后,状态指示灯将熄灭,且不会显示任何提示。数据传输也将停止。
充电
- 请通过 USB-C 充电端口连接 VerseGrip 触控笔。
- 连接电源后,充电状态指示灯将持续亮起蓝色,充满电后熄灭。
- 当Stylus处于关闭状态时,若将其连接到电源,设备会自动开启。
警告:如果您正在使用2024年8月至12月期间出厂的VerseGrip Stylus,请务必避免电池完全放电。电池一旦放电,可能无法再保持电量。在此情况下,VerseGrip Stylus只要保持连接电源,即可继续工作。如需获取用于解决此问题的固件更新文件,请联系Haply 支持团队。
状态和指示灯
| 颜色 | 指示灯模式 | 说明 |
|---|---|---|
| 黄色 | 快速淡入/淡出 | 初始自动传感器基准校准(请将VerseGrip静置在桌面上) |
| 快速闪烁 | 按下方向校准按钮,校准过程随即开始 | |
| 持续亮着 | 方向校准按钮已被按下,校准已完成 | |
| 红色 | 快速淡入/淡出 | 适配器已断开连接,未收到来自主机的信号 |
| 快速闪烁 | 按下电源按钮后,关机过程开始 | |
| 绿色 | 淡入/淡出 | 适配器已连接,VerseGrip 处于活动数据流模式 |
| 持续亮着 | 适配器已连接,VerseGrip 处于活动数据流模式,USB 电源已连接 | |
| 白色 | 淡入/淡出 | 适配器已连接,VerseGrip 处于空闲/待机模式,无数据流 |
| 持续亮着 | 适配器已连接,VerseGrip 处于空闲/待机模式,无数据流,USB 电源已连接 | |
| 橙色 | 闪烁(交错扫描) | 电池电量不足警告提示,当前状态下通知以交错方式显示 |
| 双重闪烁(交错扫描) | 电池电量极低警告提示,当前状态下通知以交错方式显示 | |
| 青绿色 | 淡入/淡出 | 设备固件更新模式 |
| (更亮) | 持续亮着 | 用户按钮被按下,当前显示颜色持续亮起,且更亮 |
| 关闭 | 无指示灯 | 结束,关机 |
| 充电指示灯颜色 | 指示灯模式 | 说明 |
|---|---|---|
| 蓝色 | 持续亮着 | 正在充电 |
| 快速闪烁 | 充电即将完成 | |
| 关闭 | 无指示灯 | 充电完成 |
旧版本的状态和指示灯
下文所述的指示灯状态和颜色是 VerseGrip Stylus 早期版本(固件 1.10 及以下)中使用的旧版状态。
| 颜色 | 指示灯模式 | 说明 |
|---|---|---|
| 红色 | 持续亮着 | 正在等待组件初始化 |
| 闪烁 | 待机模式。无数据传输 | |
| 蓝色 | 持续亮着 | 初始自动传感器基准校准(请将VerseGrip静置在桌面上) |
| 绿色 | 闪烁 | 适配器已连接,VerseGrip 处于活动数据流模式 |
| 关闭 | 无指示灯 | 结束,关机 |
| 充电指示灯颜色 | 指示灯模式 | 说明 |
|---|---|---|
| 蓝色 | 持续亮着 | 正在充电 |
| 快速闪烁 | 充电即将完成 | |
| 关闭 | 无指示灯 | 充电完成 |
示例代码
以下是一个简单的示例,演示了如何在 C++ 集成环境中使用 VerseGrip Stylus。
#include <external/libhv.h>
#include <nlohmann/json.hpp>
#include <chrono>
#include <cstdio>
#include <string>
using namespace hv;
using json = nlohmann::json;
// Procedure to get the first detected and available Wired VerseGrip Stylus device id
std::string get_first_verse_grip_device_id(const json &data) {
const auto& vgs = data["wireless_verse_grip"];
if (vgs.empty()) {
return "";
}
return vgs.items().begin().key();
}
int main() {
const auto print_delay = std::chrono::milliseconds(100);
auto current = std::chrono::high_resolution_clock::now();
bool first_message = true;
std::string device_id;
WebSocketClient ws;
ws.onmessage = [&](const std::string &msg) {
json data = json::parse(msg);
if (first_message) {
first_message = false;
const std::string first_id = get_first_verse_grip_device_id(data);
if (first_id.empty()) {
printf("no Wireless VerseGrip found.\n");
ws.close();
return;
}
device_id = first_id;
}
if (device_id.empty() || !data["wireless_verse_grip"].contains(device_id)) {
return;
}
const auto now = std::chrono::high_resolution_clock::now();
if (std::chrono::high_resolution_clock::now() > current + print_delay) {
current = now;
const json state = data["wireless_verse_grip"][device_id];
printf("Rotation : { x:%f, y:%f, z:%f, w:%f }, Hall:%i, Button : [%d, %d, %d], Battery: {%f}\n",
state["orientation"]["x"].get<float>(),
state["orientation"]["y"].get<float>(),
state["orientation"]["z"].get<float>(),
state["orientation"]["w"].get<float>(),
state["hall"].get<int8_t>(),
state["buttons"]["a"].get<bool>(),
state["buttons"]["b"].get<bool>(),
state["buttons"]["c"].get<bool>(),
state["battery_level"].get<float>());
}
};
ws.open("ws://localhost:10000");
printf("Press ENTER to stop...\n\n");
while (std::cin.get() != '\n') {
}
if (ws.isConnected()) {
ws.close();
}
return 0;
}
#include <string.h>
#include <chrono>
#include <iostream>
#include <iterator>
#include <string>
#include <thread>
#include "HardwareAPI.h"
int main(int argc, char* argv[])
{
char* portName;
if (argc < 2)
{
std::printf("Usage: %s <port>\n", argv[0]);
}
else
{
#if defined(_WIN32) || defined(_WIN64)
portName = _strdup(argv[1]); // argv1;
#endif
#if defined(__linux__)
portName = strdup(argv[1]); // argv1;
#endif
}
Haply::HardwareAPI::IO::SerialStream serial_stream(portName);
Haply::HardwareAPI::Devices::Handle handle(&serial_stream);
while (true)
{
Haply::HardwareAPI::Devices::Handle::VersegripStatusResponse data;
data = handle.GetVersegripStatus();
std::printf(
"device_id: %d battery_level: %f quaternion: %f %f %f %f buttons: "
"%d error_flags: %d\n",
data.device_id, data.battery_level, data.quaternion[0],
data.quaternion[1], data.quaternion[2], data.quaternion[3],
data.buttons, data.error_flag);
}
}