import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
VerseGrip スタイラスの入門ガイド
VerseGripは、Inverse3、Inverse3X、MinVerse に姿勢追跡機能を追加します。本記事では、VerseGrip Stylusの設定手順とその機能について解説します。
VerseGrip スタイラス
VerseGrip Stylusは、独自の高速無線通信技術を採用し、姿勢追跡およびハンドヘルド入出力(I/O)機能を提供します。さまざまな用途を想定して設計されており、汎用性に優れています。
主な機能は以下の通りです:
- 2つのユーザー入力ボタン、
- 1つのキャリブレーションボタン、
- 電源/スタンバイボタン 1個
- 2つの状態インジケーター
- USB-C 充電ポート
- 無線通信速度は最大1 kHzに達する
- バッテリー駆動時間:10~12時間
- 1つのバンド上で、最大8本のVerseGrip Stylusを1つの受信機(ドングル)とペアリングできます

カスタマーサポート
本製品やその動作についてご質問やご懸念がございましたら、サポートチームまでお問い合わせください。VerseGrip Stylus には、複雑な独自開発のワイヤレス技術が採用されています。本製品を開封すると保証が無効となり、製品が使用できなくなる可能性があります。配送期間の長期化や高額な修理費用の発生を防ぐため、本製品を開封しないでください。
VerseGrip スタイラスの設定とキャリブレーション
このセクションでは、レシーバーの挿入、VerseGrip Stylus の起動、およびキャリブレーションの手順について説明します。
設定
- 受信機をパソコンのUSBポートに接続してください。
- ハンドルを平らで動かない面に置き、ボタンが上を向くようにし、球形コネクタが画面の方を向くようにしてください。
電源投入とキャリブレーション
- 電源ボタンを1回押してください。ステータスランプがまず赤色に点灯し、その後黄色に変わります。これは、デバイス内のIMUが自己校正を行っていることを示しています。
- 自動キャリブレーションが完了すると、スタイラスはパソコンに接続されます。
- このスタイラスが正常に接続され、ワイヤレスでデータが転送されると、ステータスランプが緑色に点滅します。
- バッテリー残量が著しく少ない場合、VerseGrip Stylus は 2 分ごとに自動的に電源が切れます。直ちに充電してください。
再校正
キャリブレーションの過程で、VerseGrip の座標系がリセットされ、キャリブレーション時にデバイスを保持していた方向が新しい基準軸となります。
- 再キャリブレーションを行うには、Stylus をキャリブレーションしたい姿勢に保ったまま、キャリブレーションボタンを3秒間、またはシミュレーション上の姿勢が変化するまで押し続けてください。
下図は、校正前の状態と校正後の状態におけるデバイスの挙動を比較したものです。デモを行う際は、VerseGrip をまず図に示す姿勢に保った上で、校正を行う必要があります。

待機と電源オフ
- 電源ボタンを1回押すと、スタンバイモードに戻ります。ステータスランプが白く点滅し始め、データ転送が停止します。
- VerseGrip Stylus の電源を切るには、電源ボタンを 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);
}
}