C++/Python SDK_Quaternion Manual

1. Introduction

1.1. Feature

Obtain the quaternion of each joint through this SDK.

1.2. File Download

The quaternion SDK is open sourced.

  • Download address:

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.

  • Select "Quater" as the data format of the transmission.

  • 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).

  • 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

The data sent by the HandDriver follows a right-hand coordinate system standard, with the blue Z-axis pointing upwards, as shown in below figure:

2.2. Data format

The data content includes Device ID, Frame index, Device name, Calibration status, Battery, IMU data, bones quaternion and controllers data, as shown in below figure. Note: The quaternion order for fingers is XYZW, while for IMU it is WXYZ.

Proximal
Intermediate
Distal

Index, Middle, Ring, Little Finger

MP

PIP

DIP

Thumb

CM

MP

IP

{
"DeviceID":0, //According to the HandDriver role list, the first role is "0", the second is "1", and so on
"FrameIndex":0,
"DeviceName":"UDXST0001L",
"CalibrationStatus":-1,//Uncalibrated -1, Fist 0, Fingers Straight&Together 1, Spread Fingers 2, Finish 3
"Battery":5,//Battery 1-5, max 5
"Bones":[
[1,1,1,1],//{"LeftindexProximal":[1,1,1,1]}
[1,1,1,1],//{"LeftindexIntermediate":[1,1,1,1]}
[1,1,1,1],//{"LeftindexDistal":[1,1,1,1]}
[1,1,1,1],//{"LeftMiddleProximal":[1,1,1,1]}
[1,1,1,1],//{"LeftMiddlelntermediate":[1,1,1,1]}
[1,1,1,1],//{"LeftMiddleDistal":[1,1,1,1]}
[1,1,1,1],//{"LeftRingProximal":[1,1,1,1]}
[1,1,1,1],//{"LeftRingIntermediate":[1,1,1,1]}
[1,1,1,1],//{"LeftRingDistal":[1,1,1,1]}
[1,1,1,1],//{"LeftLittleProximal":[1,1,1,1]}
[1,1,1,1],//{"LeftLittlelntermediate":[1,1,1,1]}
[1,1,1,1],//{"LeftLittleDistal":[1,1,1,1]}
[1,1,1,1],//{"LeftThumbProximal":[1,1,1,1]}
[1,1,1,1],//{"LeftThumbintermediate":[1,1,1,1]}
[1,1,1,1],//{"LeftThumbDistal":[1,1,1,1]}
[1,1,1,1],//{"IMU Data":[1,1,1,1]}
]
"joyX":0,
"joyY":0,
"aButton":false,
"bButton":false,
"joyButton":false,
"menu":false
}

3. Windows SDK Description

This DLL contains a GloveSDK class, which is used to create a separate receiving thread to receive and parse data sent by HandDriver.

3.1. Data structure definition

struct HandDriverData
{
    int DeviceID=0;
    int FrameIndex=0;
    string DeviceName;
    int CalibrationStatus;
    int Battery;
    // 15 finger joints, each with 4 data points, 16th is for IMU data
    array<array<float, 4>, 16> JointDatas;
};

// This structure stores data for a pair of gloves, divided into L (left) and R (right)
struct PairGloveData
{
    HandDriverData HandData_L;
    HandDriverData HandData_R;
    bool bPrintData;
};

3.2. Data call

3.2.1. Retrieve glove data

3.2.1.1. Retrieve glove driver info

The example of retrieving device information (see Figure), refer to this example to get other information about the device.

//Get the device name on the left hand of device '0'
string deviceName = glovePtr->GloveData[0].HandData_L.DeviceName;

//Get the battery level on the left hand of device '0'
int battery = glovePtr->GloveData[0].HandData_L.Battery;

3.2.1.2. Retrieve glove finger data

The GloveSDK class has an array GloveData with a length of 8, and its format of PairGloveData, meaning it can store data for 8 pairs of gloves. The data is accessed via subscripts.For example: GloveData[0].HandData_L.JointDatas[6] (accesses the sixth joint data in the left-hand array of device 0. The data corresponding to JointDatas[6] is shown in Figure.).

auto a = glovePtr->GloveData[0].HandData_L.JointDatas[6];

3.2.2. Quaternion to euler angle

Use glovePtr->toEulerAngle() to convert a quaternion to Euler angles via glove_sdk. Pay attention to the quaternion order.

glovePtr->toEulerAngle(a[0], a[1], a[2], a[3], roll, pitch, yaw);

3.2.3. Euler angle to rotation angle

Use the conversion method RadToAngle() to obtain the rotation angle.

glovePtr->RadToAngle(pitch);

3.3. Interface function definition

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 register a callback function, which is called after the program has parsed the JSON data.

Parameter description: callback is a function of type GloveCallBack, and the parameter types can be modified in the .h file.

Return value description: None.

3.3.4. Print thread entry

void print_control_thread(GloveSDK* glove_sdk);

Function description: Used to print data on the console.

Parameter description: GloveSDK object pointer, the printing thread will print its data.

Return value description: None.

3.3.5. Restricting printing content

void OnlyPrintFrame();

Function description: After calling, printing parameters will only print the frame number for debugging.

Parameter description: None.

Return value description: None.

3.4. Demo and description

3.4.1. Demo project description

The HandDriver_x64_Cpp_Quater project contains the HandDriver_x64_Cpp_Quaterfolder and the HandDriver_x64_Cpp_Quater.sln file. The HandDriver_x64_Cpp_Quaterfolder contains the library files. If needed, they can be copied for separate use. The version of Visual Studio is vs2019(When using higher versions, follow the log changes), c++14. Run HandDriver_x64_Cpp_Quater.sln, select the x64 platform, and run the sample program.

3.4.2. Brief introduction to code flow

  1. Declare a function to register the callback

void OnNewGloveData(GloveSDK* glovePtr) //The cache size defines a function for registering a callback
{
        //This function will be called in the receiving thread

        //Get finger joint data

        auto a = glovePtr->GloveData[0].HandData_L.JointDatas[6];//Get the 6th data

        cout << "LeftRingProximal: " << a[1] << endl;//Print the second number of quaternions

        glovePtr->toEulerAngle(a[0], a[1], a[2], a[3], roll, pitch, yaw);//Convert quaternions to Euler angles, note that IMU data is in WXYZ order

        cout << "roll:" << glovePtr->RadToAngle(roll) << " pitch:" << glovePtr->RadToAngle(pitch) << " yaw:" << glovePtr->RadToAngle(yaw) << endl;//Print angle

}
  1. Create an instance of GloveSDK

//Create an instance of GloveSDK
GloveSDK glove_sdk;
  1. Register callback function

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

//Create a Socket and check if it is successful. If successful, receive and parse the data
if (!glove_sdk.Initialize("127.0.0.1", 7777))
        {
                cerr << "Failed to initialize GloveSDK." << endl;
                return -1;
        }
  1. Start the print thread and print the glove_sdk data

//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

//After calling, only the number of frames is printed
while (true)
        {
            int a;
            cin >> a;

            switch (a)
            {

            case 1:
                   OnlyPrintFrame();
                   break;

            case 0://Close and clean up the socket
                   glove_sdk.Shutdown();
                   printf("Shutdown");
                   break;
           }
        }
  1. Waiting for input save program

//Waiting for input, Save program
getchar();
  1. Close and clean up the socket

//close and clean up the socket
glove_sdk.Shutdown();
printf("Shutdown");

Last updated