C++/Python SDK_Angle Manual

1. Introduction

1.1. Feature

This SDK provides access to hand joint angle data and supports different operating system environments, currently including Windows and Linux.

1.2. File Download

(Download the Angle file which is open sourced)

  • Windows:

  • Linux: You can refer to the example or compile and run it yourself

1.3. HandDriver Settings

  • Check "Data Transmission" to turn on the data transmission.

  • To transmit IMU data (only supported by gloves with IMU), select the IMU option; otherwise, IMU data will not be transmitted.

  • The data format options—Euler, Quater, and VMC—are mutually exclusive (Select Euler for current SDK).

  • Select the FPS, which supports 120Hz, 90Hz, or 60Hz. The default FPS is 120Hz.

  • Click the "+" icon in the target address field, then enter the IP address and port number of the receiving side to enable data transmission (input 127.0.0.1 for local target).

  • For the SDK side, use the IP address format 0.0.0.0.

  • The default port number is 5555. Unless otherwise specified, use this default port. Otherwise, modify the corresponding port number in the plugin.

  • After configuration, click the "Apply" button to save.

2. Coordinate system and data definition

2.1. coordinate system and data description

2.1.1. HandDriver2.1.2 version after 2.1.2.

Please refer to this document:Hand Model and Data Description

2.1.2. HandDriver2.1.1 version before 2.1.1

The data sent refers to the right-hand coordinate system standard, as shown in the figure below.

2.2. Data Format

The sending data is shown in the figure below and includes information such as the character name, calibration status, finger joint rotation angles, IMU quaternion data (only available when the glove is equipped with an IMU and IMU transmission is enabled) and controllers data.

{
    "Udhand": { //the corresponding Character name in HandDriver
        "Bones": [
            {
                "Name": "Head", 
                "Parent": 0,
                "Location": [ 0,0,0 ],
                "Rotation": [ 0,0,0 ],
                "Scale": [ 0,0,0 ]
            }
        ],
        "Parameter": [
                { "Name": "L_CalibrationStatus", "Value": 3 },//Left hand calibration status -1:Not calibrated,0:fist,1:fingers together,2:fingers spreading,3:Calibration completed
                { "Name": "l0", "Value": 0 },//Left hand Thumb IP pitch                        
                { "Name": "l1", "Value": 0 },//Left hand Thumb MP pitch                        
                { "Name": "l2", "Value": 0 },//Left hand Thumb CM pitch                        
                { "Name": "l3", "Value": 0 },//Left hand Thumb CM yaw                        
                { "Name": "l4", "Value": 0 },//Left hand Index DIP pitch                        
                { "Name": "l5", "Value": 0 },//Left hand Index PIP pitch
                { "Name": "l6", "Value": 0 },//Left hand Index MP pitch
                { "Name": "l7", "Value": 0 },//Left hand Index MP yaw
                { "Name": "l8", "Value": 0 },//Left hand Middle DIP pitch
                { "Name": "l9", "Value": 0 },//Left hand Middle PIP pitch
                { "Name": "l10", "Value": 0 },//Left hand Middle MP pitch
                { "Name": "l11", "Value": 0 },//Left hand Middle MP yaw
                { "Name": "l12", "Value": 0 },//Left hand Ring DIP pitch
                { "Name": "l13", "Value": 0 },//Left hand Ring PIP pitch
                { "Name": "l14", "Value": 0 },//Left hand Ring MP pitch
                { "Name": "l15", "Value": 0 },//Left hand Ring MP yaw
                { "Name": "l16", "Value": 0 },//Left hand Pinky DIP pitch
                { "Name": "l17", "Value": 0 },//Left hand Pinky PIP pitch
                { "Name": "l18", "Value": 0 },//Left hand Pinky MP pitch
                { "Name": "l19", "Value": 0 },//Left hand Pinky MP yaw
                { "Name": "l20", "Value": 0 },//Left hand Thumb CM roll
                { "Name": "l21", "Value": 0 },//Left hand Index MP roll
                { "Name": "l22", "Value": 0 },//Left hand Pinky MP roll
                { "Name": "l23", "Value": -1 },//Left hand Reserved for gesture recognition
                { "Name": "l24", "Value": -1 },//Left hand IMU quaternion-W
                { "Name": "l25", "Value": -1 },//Left hand IMU quaternion-X
                { "Name": "l26", "Value": -1 },//Left hand IMU quaternion-Y
                { "Name": "l27", "Value": -1 },//Left hand IMU quaternion-Z
                { "Name": "l_joyX", "Value": 0 },
                { "Name": "l_joyY", "Value": 0 },
                { "Name": "l_aButton", "Value": false },
                { "Name": "l_bButton", "Value": false },
                { "Name": "l_joyButton", "Value": false },
                { "Name": "l_menu", "Value": false },
                { "Name": "R_CalibrationStatus", "Value": 3 },////Right hand calibration status -1:Not calibrated,0:fist,1:fingers together,2:fingers spreading,3:Calibration completed
                { "Name": "r0", "Value": 0 },//Right hand Thumb IP pitch
                { "Name": "r1", "Value": 0 },//Right hand Thumb MP pitch
                { "Name": "r2", "Value": 0 },//Right hand Thumb CM pitch
                { "Name": "r3", "Value": 0 },//Right hand Thumb CM yaw
                { "Name": "r4", "Value": 0 },//Right hand Index DIP pitch
                { "Name": "r5", "Value": 0 },//Right hand Index PIP pitch
                { "Name": "r6", "Value": 0 },//Right hand Index MP pitch
                { "Name": "r7", "Value": 0 },//Right hand Index MP yaw
                { "Name": "r8", "Value": 0 },//Right hand Middle DIP pitch
                { "Name": "r9", "Value": 0 },//Right hand Middle PIP pitch
                { "Name": "r10", "Value": 0 },//Right hand Middle MP pitch
                { "Name": "r11", "Value": 0 },//Right hand Middle MP yaw
                { "Name": "r12", "Value": 0 },//Right hand Ring DIP pitch
                { "Name": "r13", "Value": 0 },//Right hand Ring PIP pitch
                { "Name": "r14", "Value": 0 },//Right hand Ring MP pitch
                { "Name": "r15", "Value": 0 },//Right hand Ring MP yaw
                { "Name": "r16", "Value": 0 },//Right hand Pinky DIP pitch
                { "Name": "r17", "Value": 0 },//Right hand Pinky PIP pitch
                { "Name": "r18", "Value": 0 },//Right hand Pinky MP pitch
                { "Name": "r19", "Value": 0 },//Right hand Pinky MP yaw
                { "Name": "r20", "Value": 0 },//Right hand Thumb CM roll
                { "Name": "r21", "Value": 0 },//Right hand Index MP roll
                { "Name": "r22", "Value": 0 },//Right hand Pinky MP roll
                { "Name": "r23", "Value": -1 },//Right hand Reserved for gesture recognition
                { "Name": "r24", "Value": -1 },//Right hand IMU quaternion-W
                { "Name": "r25", "Value": -1 },//Right hand IMU quaternion-X
                { "Name": "r26", "Value": -1 },//Right hand IMU quaternion-Y
                { "Name": "r27", "Value": -1 },//Right hand IMU quaternion-Z
                { "Name": "r_joyX", "Value": 0 },
                { "Name": "r_joyY", "Value": 0 },
                { "Name": "r_aButton", "Value": false },
                { "Name": "r_bButton", "Value": false },
                { "Name": "r_joyButton", "Value": false },
                { "Name": "r_menu", "Value": false }
        ]
    }
}

2.3. The Receiving Angle

Regarding the receiving angle value, please refer to the HandDriver data page. The data on this page is consistent with the SDK receiving data.

  • Consistent positive and negative

  • Specific values are expressed as integers when displayed in HandDriver

  • For detailed instructions, please see here: UDEXREAL HandDriver Hand Model and Data Description

3. C++ Windows SDK

3.1. Data Structure Definition

3.1.1. Character list

vector<GloveData>gloveDataList;

Data Structure Description: Save received character data.

Parameter Description: GloverData contains information for a single character.

3.1.2. Glove data

struct GloveData
{
    string deviceName;
    HandData handDatas;
};

Data Structure Description: Save the character's name and hand data.

Parameter Description: handDatas contains the hand data for a single character.

3.1.3. Hand data

struct HandData
{
    Bone bones;
    handsfingerJoint fingerJoints;
};

Data Structure Description: Save the character's skeletal data and finger joint data.

Parameter Description: bones contain the glove skeletal data, while fingerJoints contain the glove finger joint data.

3.1.4. Bone data

struct Bone
{
    string name;
    int parent = 0;
    vector<float> location = {};
    vector<float> rotation = {};
    vector<float> scale = {};        
};

Data Structure Description: Save skeletal data.

Parameter Description: Used for adapting data formats for Unreal Engine; the information is currently not in use.

3.1.5. Finger joint data

struct handsfingerJoint
{
    vector<Parameter> fingerJoint_L;
    vector<Parameter> fingerJoint_R;
};

Data Structure Description: Save the finger joint data for both hands of the character, including the hand calibration status.

Parameter Description: fingerJoint_L contains the data for the character's left hand, while fingerJoint_R contains the data for the character's right hand.

3.2. Data Calling

3.2.1. Retrieve glove data

3.2.1.1. Retrieve glove driver info

Example for retrieving device information. You can obtain other device information in a similar manner.

//Retrieve the character name for the device; "0" indicates the first device.
string name = glovePtr->gloveDataList[0].deviceName;

//Retrieve the left hand calibration status; note that this information is stored in the finger data array.
float L_CalibrationStatus= glovePtr->gloveDataList[0].handDatas.fingerJoints.fingerJoint_L[0].value;

3.2.1.2. Retrieve Finger Joint Rotation Data from the Glove

In the GloveSDK class, there is an array gloveDataList of type GloveData, which stores data for all gloves. Access it using the index, for example: glovePtr->gloveDataList[0].handDatas.fingerJoints.fingerJoint_L[6].value (Accessed the pitch data of the second phalanx of the left hand index finger for device 0, where fingerJoint_L[6] corresponds to the image in The corresponding parameters are viewed in the data format.)。

//Retrieve left hand Index intermediate pitch
float L_indexIntermediate pitch= glovePtr->gloveDataList[0].handDatas.fingerJoints.fingerJoint_L[6].value;

3.3. Definition of Interface

3.3.1. Initialization

bool Initialize(const char* udp_ip, unsigned short udp_port);

Function description: Create a Socket according to the parameters. If successful, start a thread to receive data.

Parameter description: udp_ip is the IP address for receiving data, udp_port is the port number, which can be the same as the sending HandDriver.

Return value description: Returns true if successful, false if failed.

3.3.2. Close thread

void Shutdown();

Function description: Used to close the receiving thread and clean up the Socket.

Parameter description: None.

Return value description: None.

3.3.3. Registering callback function

void RegisterGloveCallBack(GloveCallBack callback);

Function description: Used to print all data for a character to check the data status.

Parameter Description: inputGloveData is a function of type GloveData, which takes a GloveData object as input when called.

Return value description: None.

3.4. Windows Demo and Description

3.4.1. Demo project description

The HandDriver_x64_Cpp_Angleproject contains the HandDriver_x64_Cpp_Anglefolder and the HandDriver_x64_Cpp_Angle.sln file. The HandDriver_x64_Cpp_Anglefolder includes the library files, which can be copied for standalone use. The version of Visual Studio is VS2019(When using higher versions, follow the log changes), using C++14. To run the example program, open HandDriver_x64_Cpp_Angle.sln and select the x64 platform.

3.4.2. Brief introduction to code flow

  1. Declare a function to register the callback

void OnNewGloveData(GloveSDK* glovePtr)
{
        /*
        * 打印手数据
        * Print gloveData
        */
         
        //Print all data for character "0"
        glovePtr->ShowGloveData(glovePtr->gloveDataList[0]);
        cout << endl << endl;


}
  1. Create an instance of GloveSDK

GloveSDK glove_sdk;
  1. Register callback function

glove_sdk.RegisterGloveCallBack(OnNewGloveData);
  1. Create a socket and check if it is successful. If successful, start receiving and parsing data

if (!glove_sdk.Initialize("127.0.0.1", 5555))
        {
                cerr << "Failed to initialize GloveSDK." << endl;
                return -1;
        }
  1. Start the print thread and print the glove_sdk data

thread print_th(print_control_thread, &glove_sdk);
  1. Calling the function to print only the frame number

while (true)
        {
                int a;
                cin >> a;

                switch (a)
                {

                case 1:
                        OnlyPrintFrame();
                        break;

                case 0:
                        glove_sdk.Shutdown();
                        printf("Shutdown");
                        break;
                }
        }
  1. Waiting for input save program

getchar();
  1. Close and clean up the socket

glove_sdk.Shutdown();
printf("Shutdown");

4. C++ Linux SDK

4.1. File Directory

The Linux Cpp SDK folder contains: the folder "include" (which contains the header files required for JSON parsing), the folder "lib" (which contains the necessary JSON dynamic libraries such as libjsoncpp.so and libjsoncpp.so.27), UDEServer.h (the SDK header file), UDEServer.cxx (the implementation of methods), main.cxx (an example), build.sh (an example build file), CMakeLists.txt, and ReadMe.txt.

4.2. Data Format

Please click here for the format.

4.3. Data Calling

Include the header file UDEServer.h first and complete the initialization of the server.The method for obtaining the received list of character names is as follows:

#include "UDEServer.h"

//create new SDK instance
UDEGloveSDK sdk;
//initialization method
sdk.Initialize();

//start server listening
sdk.StartListening();

//Call the GetRoleNameList() method to get the list of received character names
vector<string> list = sdk.GetRoleNameList();

The default port number of the server is 5555. You can obtain the current port number or set the port through below method:

int port = sdk.GetPortNum();

int newPort = 1234;
sdk.SetPortNum(newPort);

Close data reception or obtain current status in server listening.

//Server status enumeration parameters
enum ServerStatus
{
    NO_INIT, 
    READY, 
    IN_LISTENING, 
    END
};

int status = sdk.GetStatus()

//close server listener
sdk.EndListening();

According to the specified character name, all knuckle data can be obtained with the type of vector. The names of the finger joints and the array of Euler angles can be obtained corresponding to the index order in GloveDataHeaders. The controller data can also be obtained. An array of the type float can be obtained according to the index order in ControllerHeaders, which corresponds to the controller parameters.

//Vector3Float struct
struct Vector3Float
{
    float x;
    float y;
    float z;

    Vector3Float(float X, float Y, float Z)
    {
        this->x = X;
        this->y = Y;
        this->z = Z;
    }
};

//The name index corresponding to the return value of the finger joint
string GloveDataHeaders[30] = 
{
    "LeftThumb1", 
    "LeftThumb2", 
    "LeftThumb3", 
    "LeftIndex1", 
    "LeftIndex2", 
    "LeftIndex3", 
    "LeftMiddle1",
    "LeftMiddle2",
    "LeftMiddle3",
    "LeftRing1",
    "LeftRing2",
    "LeftRing3",
    "LeftPinky1", 
    "LeftPinky2", 
    "LeftPinky3", 
    "RightThumb1", 
    "RightThumb2", 
    "RightThumb3", 
    "RightIndex1", 
    "RightIndex2", 
    "RightIndex3",
    "RightMiddle1",
    "RightMiddle2",
    "RightMiddle3",
    "RightRing1",
    "RightRing2",
    "RightRing3",
    "RightPinky1", 
    "RightPinky2", 
    "RightPinky3", 
};

//The name index corresponding to the controller return value
string ControllerHeaders[12] = 
{
    "Left Joy X",
    "Left Joy Y",
    "Left A Button",
    "Left B Button",
    "Left Joy Button",
    "Left Menu Button",
    "Right Joy X",
    "Right Joy Y",
    "Right A Button",
    "Right B Button",
    "Right Joy Button",
    "Right Menu Button"
};

vector<string> list = sdk.GetRoleNameList();
if(list.size() > 0)
{
    //Get the data of the first character by default
    auto FingerData = sdk.GetVecFingerData(list[0]);
    
    //Controller parameters
    auto ControllerData = sdk.GetVecControllerData(list[0]);
}

The corresponding entries obtained are as follows:

{
    "LeftThumb1", {0, 0, 0};
    "LeftThumb2", {0, 0, 0};
    "LeftThumb3", {0, 0, 0};
    ...
    "RightThumb1", {0, 0, 0};
    "RightThumb2", {0, 0, 0};
    "RightThumb3", {0, 0, 0};
    ...
    "Left Joy X", 0;
    "Left Joy Y", 0;
    "Left A Button", 0;
    "Left B Button", 0;
    ...
}

The rules for knuckle names are Left/Right + Thumb/Index/Middle/Ring/Pinky + 1/2/3 (joint 1/joint 2/joint 3). The order of the angle array is {pitch, yaw, roll}.

Joy X and Joy Y in the controller data correspond to joystick parameters, ranging from (-1,1) as floating numbers. All other button parameters are also set to floating numbers 0 or 1 for consistency, corresponding to untriggered or triggered.

4.4. Definition of Interface

  • Initialization

int Initialize();

Function description: Socket initialization, including the required declaration and calling to create a server.

Return value description: If successful, return 0; if failed, return -1.

  • Set Port Number

void SetPortNum(int port);

Function description: Set the server listener port number.

Parameter description: The port number is integer type.

  • Get Port Number

int GetPortNum();

Function description: Get the server listener port number.

Return value description: The port number is integer type.

  • Start server listener

void StartListening();

Function Description: The start server listener method can obtain parameters in the loop.

  • Shut down server listener

void EndListening();

Function description: Close the server listener.

  • Get server status

ServerStatus GetStatus();

Function description: Get the current server status.

Return value description: ServerStatus enumeration parameter, 0 means NO_INIT (not initialized), 1 means READY, 2 means IN_LISTENING (listening), 3 means END (listening terminated).

  • Get a list of character names

vector<string> GetRoleNameList();

Function description: Get a string list of all received character names.

Return value description: name string array.

  • Get knuckle data

map<string, vector<float>> GetFingerData(string RoleName);

Function description: Get the knuckle data of the specified character.

Parameter description: Character name in String type.

Return value description: Euler angle array sorted in the same order as the character index in GloveDataHeaders.

  • Get controller data

map<string, float> GetControllerData(string RoleName);

Function description: Get controller data for the specified character.

Parameter description: Character name in String type.

Return value description: A float array sorted in the same order as the character index in ControlHeaders.

4.5. Example

The main.cxx provides an example of calling the SDK interface within the main method to receive and print data. An executable file can be generated by running build.sh.

  1. Start the "Terminal", enter the root folder of the SDK, run the following command:

$ sudo sh ./build.sh
  1. This can generate an executable example ServerSample, which can be run in the same directory using the following command:

$ ./ServerSample

And then the link library libUDEServer.so of the project can be found in the folder "build".

5. Python Linux SDK

5.1. Data Format

Please click here for the format.

5.2. Data Calling

The method for obtaining the received list of character names is as follows:

sdk = UDEGloveSDK()  # Create an instance of the SDK
sdk.initialize()  # Initialize the SDK
sdk.start_listening()  # Start listening for data
role_list = sdk.get_role_name_list()  # Get the list of role names

The default port number of the server is 5555. You can obtain the current port number or set the port through below method:

self.port = 5555  # Port number for the server
self.sock = None  # Socket object for communication
self.server_addr = ("0.0.0.0", self.port)  # Server address and port

Close data reception or obtain current status in server listening.

# Enum-like class to represent server status
class ServerStatus:
    NO_INIT = 0  # Server not initialized
    READY = 1  # Server ready
    IN_LISTENING = 2  # Server is listening for data
    END = 3  # Server has stopped
    
status = sdk.cur_status  # Get the current server status

sdk.end_listening()  # Stop listening

According to the specified character name, all knuckle data can be obtained with the type of vector. The names of the finger joints and the array of Euler angles can be obtained corresponding to the index order in GloveDataHeaders. The controller data can also be obtained. An array of the type float can be obtained according to the index order in ControllerHeaders, which corresponds to the controller parameters.

# Class representing a 3D vector with float values
class Vector3Float:
    def __init__(self, x: float, y: float, z: float):
        self.x = x
        self.y = y
        self.z = z
        
# Headers for glove data
GloveDataHeaders = [
    "LeftThumb1", "LeftThumb2", "LeftThumb3", "LeftIndex1", "LeftIndex2", "LeftIndex3",
    "LeftMiddle1", "LeftMiddle2", "LeftMiddle3", "LeftRing1", "LeftRing2", "LeftRing3",
    "LeftPinky1", "LeftPinky2", "LeftPinky3", "RightThumb1", "RightThumb2", "RightThumb3",
    "RightIndex1", "RightIndex2", "RightIndex3", "RightMiddle1", "RightMiddle2", "RightMiddle3",
    "RightRing1", "RightRing2", "RightRing3", "RightPinky1", "RightPinky2", "RightPinky3"
]

# Headers for controller data
ControllerHeaders = [
    "Left Joy X", "Left Joy Y", "Left A Button", "Left B Button", "Left Joy Button",
    "Left Menu Button", "Right Joy X", "Right Joy Y", "Right A Button", "Right B Button",
    "Right Joy Button", "Right Menu Button"
]

role_list = sdk.get_role_name_list()  # Get the list of role names
if len(role_list) > 0: 
    for role_name in role_list:
        finger_data = sdk.get_vec_finger_data(role_name)  # Get finger data
        controller_data = sdk.get_vec_controller_data(role_name)  # Get controller data

The corresponding entry examples obtained are as follows:

{
    "LeftThumb1", {0, 0, 0};
    "LeftThumb2", {0, 0, 0};
    "LeftThumb3", {0, 0, 0};
    ...
    "RightThumb1", {0, 0, 0};
    "RightThumb2", {0, 0, 0};
    "RightThumb3", {0, 0, 0};
    ...
    "Left Joy X", 0;
    "Left Joy Y", 0;
    "Left A Button", 0;
    "Left B Button", 0;
    ...
}

The rules for knuckle names are Left/Right + Thumb/Index/Middle/Ring/Pinky + 1/2/3 (joint 1/joint 2/joint 3). The order of the angle array is {pitch, yaw, roll}.

Joy X and Joy Y in the controller data correspond to joystick parameters, ranging from (-1,1) as floating numbers. All other button parameters are also set to floating numbers 0 or 1 for consistency, corresponding to untriggered or triggered.

5.3. Definition of Interface

  • Initialization

def initialize(self):

Function description: Socket initialization, including the required declaration and calling to create a server.

Return value description: If successful, return 0; if failed, return -1.

  • Set Port Number

def __init__(self):
    self.port = 5555

Function description: Set the port number of server listener.

Parameter description: The port number is integer type.

  • Get Port Number

Port = sdk.port

Function description: Get the port number of server listener.

Return value description: The port number is integer type.

  • Start service listener

def start_listening(self):

Function description: start service listener.

  • Shut down server listener

def end_listening(self):

Function description: Close the server listener.

  • Get server status

status = sdk.cur_status

Function description: Get the current server status.

Return value description: ServerStatus enumeration parameter, 0 means NO_INIT (not initialized), 1 means READY, 2 means IN_LISTENING (listening), 3 means END (listening terminated).

  • Get a list of character names

def get_role_name_list(self) -> List[str]:

Function description: Get a string list of all received character names.

Return value description: name string array.

  • Get knuckle data

def get_vec_finger_data(self, role_name: str) -> List[Vector3Float]:

Function description: Get the knuckle data of the specified character.

Parameter description: Character name in String type.

Return value description: Euler angle array sorted in the same order as the character index in GloveDataHeaders.

  • Get controller data

def get_vec_controller_data(self, role_name: str) -> List[float]:

Function description: Get the controller data of the specified character.

Parameter description: Character name in String type.

Return value description: A float array sorted in the same order as the character index in ControlHeaders.

5.4. Example

Start "Terminal" in the directory where the .py file is located and run the following command:

$ python3 HandDriver_Linux_Py_Angle_20250402.py

Last updated