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

客户服务
如果您对设备或其操作有任何疑问或顾虑,请联系我们的支持团队以获取帮助。VerseGrip Stylus 设备内置了复杂的专有无线技术。 拆开设备将导致保修失效,并可能导致设备无法使用。为避免发货延误和昂贵的维修费用,请勿拆开设备。
VerseGrip 触控笔的配置与校准
本节介绍了连接适配器、开启 VerseGrip Stylus 设备以及进行校准的步骤。
安装
- 将该适配器插入计算机的USB端口。
- 将手柄放在平坦且不会移动的表面上,按键朝上,球形连接器朝向屏幕。
通电与校准
- 轻按电源/待机按钮一次。状态指示灯将先亮起红色,随后转为黄色,表明设备的惯性测量单元(IMU)正在进行自校准。
- 自动校准完成后,触控笔将连接到计算机。
- 当触控笔成功连接并进行无线数据传输时,状态指示灯将闪烁绿色。
- 如果电池电量过低,VerseGrip Stylus 设备将每 2 分钟自动关机一次;请立即为其充电。
重新校准
校准过程会重置 VerseGrip 的坐标系,使校准过程中握持设备时的方向成为新的参考轴。
- 要重新校准,请将触控笔置于您希望重新校准的位置,然后按住校准按钮三秒钟,或者直到模拟画面中的方向发生变化为止。
以下演示对比了设备未经校准和经过校准时的运行表现。在演示过程中,校准前请按照图示方式握持VerseGrip设备。

待机与关机
- 按一下开/关机按钮,即可返回待机模式。状态指示灯将开始闪烁白色,数据传输将停止。
- 要关闭VerseGrip Stylus设备,请长按电源/待机按钮5秒钟,然后松开。松开按钮后,状态指示灯应熄灭,且不会发出任何光信号。数据传输将停止。
充电
- 将VerseGrip Stylus设备连接到USB-C充电端口。
- 当设备连接到电源时,充电指示灯将持续亮起蓝色,电池充满电后指示灯将熄灭。
- 如果将触控笔插入电源时其处于关闭状态,它会自动开启。
警告:如果您拥有的VerseGrip Stylus设备是在2024年8月至12月期间发货的,请勿让电池完全放电。 一旦电量耗尽,电池可能无法再次充电。如果发生这种情况,VerseGrip Stylus 设备只要保持连接电源,仍可继续工作。请联系Haply 支持团队,以获取可解决此问题的固件更新文件。
状态和指示灯
| 颜色 | 光影序列 | 说明 |
|---|---|---|
| Jaune | 快速进出式熔炉 | 传感器初始自动校准(将VerseGrip设备静置在桌面上) |
| 快速闪烁 | 按下方向校准按钮,校准过程已启动 | |
| Fixe | 按下方向校准按钮,校准完成 | |
| Rouge | 快速进出式熔炉 | 适配器已断开,未收到主机信号 |
| 快速闪烁 | 按下电源/待机按钮,关机过程已启动 | |
| Vert | 输入/输出融合 | 适配器已连接,VerseGrip 处于数据流活动模式 |
| Fixe | 适配器已连接,VerseGrip 处于数据流活动模式,USB 电源已连接 | |
| 布兰克 | 输入/输出融合 | 适配器已连接,VerseGrip 处于待机/休眠模式,未传输数据 |
| Fixe | 适配器已连接,VerseGrip 处于待机/休眠模式,未传输数据,USB 供电已连接 | |
| 橙色 | Flash(交替显示) | 电量不足警告,该通知在当前状态下交替显示 |
| 双闪(交替闪烁) | 电池电量极低警告,该通知在当前状态下交替显示 | |
| 萨尔塞尔 | 输入/输出融合 | 设备固件的更新方法 |
| (更生动) | Fixe | 按下用户按钮后,当前指示灯颜色保持不变且更鲜艳 |
| 已关闭 | 没有灯光 | 停止,断电 |
| 充电指示灯的颜色 | 光影序列 | 说明 |
|---|---|---|
| Bleu | Fixe | 负责 |
| 快速闪烁 | 充电即将完成 | |
| 已关闭 | 没有灯光 | 充电完成 |
历史状态和指示灯
下面的状态指示灯对应于VerseGrip Stylus早期版本(固件1.10及更早版本)中的旧状态和颜色。
| 颜色 | 光影序列 | 说明 |
|---|---|---|
| Rouge | Fixe | 待处理组件的初始化 |
| 闪烁 | 待机模式。不进行数据传输 | |
| Bleu | Fixe | 传感器初始自动校准(将VerseGrip设备静置在桌面上) |
| Vert | 闪烁 | 适配器已连接,VerseGrip 处于数据流活动模式 |
| 已关闭 | 没有灯光 | 停止,断电 |
| 充电指示灯的颜色 | 光影序列 | 说明 |
|---|---|---|
| Bleu | Fixe | 负责 |
| 快速闪烁 | 充电即将完成 | |
| 已关闭 | 没有灯光 | 充电完成 |
代码示例
以下是一个使用 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);
}
}