import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
VerseGrip Stylus 入门指南
VerseGrip 为 Inverse3、Inverse3X 和 MinVerse 增加姿态追踪功能。本文介绍 VerseGrip Stylus 的设置步骤及其功能。
VerseGrip Stylus
VerseGrip Stylus 采用专有的高速无线通信技术,提供姿态追踪和手持输入/输出(I/O)功能。它面向多种应用场景设计,具有很强的通用性。
主要功能包括:
- 2 个用户输入按钮,
- 1 个校准按钮,
- 1 个电源/待机按钮
- 2 个状态指示灯
- USB-C 充电接口
- 无线通信速率最高可达 1 KHz
- 电池续航 10–12 小时
- 同一 band 上最多可将 8 个 VerseGrip Stylus 与一个接收器(dongle)配对

客户支持
如果您对设备或其运行有任何疑问或顾虑,请联系我们的支持团队寻求帮助。VerseGrip Stylus 包含复杂的专有无线技术。打开设备将使保修失效,并可能导致设备无法使用。为避免延长运输时间和产生高额维修费用,请勿打开设备。
VerseGrip Stylus 设置与校准
本节介绍插入接收器、开启 VerseGrip Stylus 以及校准的步骤。
设置
- 将接收器插入电脑的 USB 接口。
- 将手柄放在平整且不会移动的表面上,按钮朝上,球形连接器朝向屏幕。
开机与校准
- 按一次电源按钮。状态灯会先变为红色,然后变为黄色,表示设备内的 IMU 正在进行自校准。
- 自校准完成后,Stylus 将连接到电脑。
- 当 Stylus 成功连接并通过无线传输数据时,状态灯会闪烁绿色。
- 如果电池电量严重不足,VerseGrip Stylus 将每 2 分钟自动关机一次;请立即充电。
重新校准
校准过程会重置 VerseGrip 的坐标系,使校准时握持设备的方向成为新的参考轴。
- 要重新校准,请将 Stylus 保持在您希望校准到的姿态,按住校准按钮三秒钟,或直到仿真中的姿态发生变化为止。
下图对比了设备未校准与已校准时的行为。演示时,VerseGrip 必须先保持图中所示的姿态,再进行校准。

待机与关机
- 按一次电源按钮可返回待机模式。状态灯将开始闪烁白色,数据传输停止。
- 要关闭 VerseGrip Stylus,请按住电源按钮 5 秒后松开。松开后状态灯应熄灭,且不再有任何指示。设备将停止传输数据。
充电
- 通过 USB-C 充电接口为 VerseGrip Stylus 充电。
- 接通电源时,充电状态灯将常亮蓝色,充满后熄灭。
- 如果接通电源时 Stylus 处于关闭状态,它将自动开机。
警告:如果您的 VerseGrip Stylus 在 2024 年 8 月至 12 月间出货,请勿让电池完全耗尽。电量耗尽后,电池可能无法再保持电量。 如果出现这种情况,只要保持接通电源,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);
}
}