API Reference
- The functions you will need are in SG_main.
- For details on the simulator to work without physical gloves see SG_simulator
SG_API.SG_main
SG_main contains all functions the user needs from the API. See Getting Started docs for a general explanation of how to use it.
Questions? Written by:
- Amber Elferink
Docs: https://adjuvo.github.io/SenseGlove-R1-API/
Support: https://www.senseglove.com/support/
init
init(
wait_for_x_gloves: int,
com_type: Com_type,
SIMULATION_MODE: Simulation_Mode = FINGERS_OPEN_CLOSE,
) -> List[int]
Initializes the API and connection searching.
If wait_for_x_gloves is not 0, the program will not move on until it finds at least x gloves. Returns list of device_ids connected after init.
If setting Com_type to SIMULATED_GLOVE it will generate a single right hand simulator. The movement can be set with the SIMULATION_MODE parameter (which is not used when the REAL glove mod is on).
Examples:
device_ids = SG_main.init(1, SG_T.Com_type.REAL_GLOVE_USB) # Block program until 1 glove connects
device_ids = SG_main.init(2, SG_T.Com_type.REAL_GLOVE_USB) # Block program until 2 gloves connect
device_ids = SG_main.init(1, SG_T.Com_type.SIMULATED_GLOVE) # Simulate 1 glove (max 1 simulated glove via this init, create more with init_rembrandt_sim())
device_ids = SG_main.init(0, SG_T.Com_type.REAL_GLOVE_USB) # Doesn't block the program, may return no device_ids.
add_on_connected_callback() function to capture the device_id on connection, or retrieve the device_ids with get_device_ids(), get_right_hand_deviceid(), get_left_hand_deviceid() after a connection.
init_rembrandt_sim
init_rembrandt_sim(
handedness: Hand,
simulation_mode: Simulation_Mode,
starting_angles=[
0.0,
-0.2617993877991494,
0.7853981633974483,
-1.5707963267948966,
2.0943951023931953,
-1.7453292519943295,
1.5707963267948966,
0.7,
],
) -> Optional[Glove_Simulator]
Creates a simulated hand to test without physical Rembrandt glove. For more options directly create it via SG_sim.create_glove_sim_device(). Currently does max 2 gloves from this function. A right and a left one.
Examples:
get_COM_type
keep_program_running
This is a while true loop with a sleep and exit check. Exits cleanly on Ctrl+C, window close, or stop_program_running()
⚠️ Important Notes
- Code after this call will NOT execute until exit
- Use this instead of a custom while loop to avoid performance issues with 1000hz glove data (subscr_rembrandt_data_callback)
Usage:
SG_main.subscr_rembrandt_data_callback(on_new_data)
SG_main.keep_program_running()
# code here won't execute until exit
How it's implemented (for reference):
def keep_program_running():
try:
while SG_main.SG_cb.running:
time.sleep(1) # This loop does not do anything but keep the program alive. Some sleep is important to not eat all CPU capacity.
except:
pass # important errors will still log due to sg_logger. This try/except just ignores the keyboard interrupt error on Ctrl+C.
If you use your own while loop instead (without the sleep), that may steal all CPU capacity, and the data callback will not be called often, which can result in only 70fps glove data update rate in bad cases.
So if you use your own while loop, make sure to add the sleep, and preferably the shutdown check for clean exit.
is_device_active
get_device_ids
get_right_hand_deviceid
get_left_hand_deviceid
is_left_hand
get_handedness
nr_of_fingers_tracking
nr_of_fingers_force
subscr_r1_data_callback
Expects a callback function that takes device_id as a parameter.
That function will trigger if new data is available for the Rembrandt device. Within your function, call other API functions to retrieve specific data.
⚠️ Important Note
Do not put too heavy computing into this callback, since it will slow down the update rate of data retrieval from the glove, which must remain ~1kHz for optimal haptic performance. Preferably only place data into variables here.
subscr_on_connected_callback
Expects a callback function that takes SG_types.Device_Info as a parameter.
That function will trigger if a new device is connected. Within your function, call other API functions to retrieve specific data.
You can add the following code even before the SG_main.init() call to make sure you capture the connection..
get_exo_angles_rad
Retrieves exoskeleton joint angles in radians.
Can be indexed like:
exo_angle = exo_angles[finger_nr][exo_joint_nr]
Angle definitions:
- A fully extended straight exoskeleton gives all angles 0
- Rotating a joint towards hand palm gives angle > 0
- Rotating joint towards back of hand angle < 0
- The first angle is the splay angle. Those after are the flexion angles
Examples:
get_device_info
get_exo_angles_deg
Retrieves exoskeleton joint angles in degrees (same as get_exo_angles_rad but degrees). Use:
Examples:
get_exo_linkage_lengths
get_fingertips_pos_rot
Returns tuple of positions (x,y,z) and rotations (quat) of each fingertip with respect to the finger base origin. See tracking documentation for more info.
Can be indexed like:
fingertip_pos_xyz = fingertips_pos[finger_nr][xyz_index]fingertip_rot_quat = fingertips_rot[finger_nr][quat_wxyz_index]
Examples:
get_fingertip_thimble_dims
get_fingertip_thimble_dims(
device_id: int,
) -> List[Thimble_dims]
get_fingertip_distances
returns: fingertip distances between thumb and [index, middle, ring, pinky] (float in mm)
get_exo_joints_poss_rots
get_exo_joints_poss_rots(
device_id: int,
) -> Tuple[
Sequence[Sequence[Vec3_type]],
Sequence[Sequence[Quat_type]],
]
Returns tuple of positions (x,y,z) and rotations (quat) of each exoskeleton joint. Use:
Can be indexed like: -joint_pos_xyz = exo_poss[finger_nr][exo_joint_nr][xyz_index]
- joint_rot_quat = exo_rot[finger_nr][exo_joint_nr][quat_wxyz_index]
Examples:
get_percentage_bents
Returns percentage bent values for flexion and abduction of each finger.
Returns tuple containing:
- flexion_perc_bents: Array of flexion percentages (0-out_max_perc_bent, 10000 by default)
- abduction_perc_bents: Array of abduction percentages (0-out_max_perc_bent, 10000 by default)
Can be indexed like:
finger_flex_bent = flex_bents[finger_nr]finger_abd_bent = abd_bents[finger_nr]
Examples:
thumb_flex_bent = flex_bents[0] # 0 (open) to 10000 (closed)
thumb_abd_bent = abd_bents[0] # 0 (in plane with palm) to 10000 (maximally radially extended)
index_abd_bent = abd_bents[1] # 0 abducted to y, 5000 (neutral) to 10000 (abducted to the -y)
Notes:
- Values range from 0 (finger open) to out_max_perc_bent (finger closed)
- For the thumb:
- Flexion: 0 = extended, out_max_perc_bent = fully flexed
- Abduction: 0 = in plane with palm, out_max_perc_bent = maximally radially extended
- For other fingers:
- Flexion: 0 = extended, out_max_perc_bent = fully flexed
- Abduction: 0 = neutral, out_max_perc_bent = maximally abducted
Other notes:
- 10000 is used by default instead of 100 to avoid floating point inaccuracies
- It calculates this based on the fingertip orientation with respect to the finger base orientation
- This method is used because it's independent of user hand sizes, and requires no calibration
- Use set_perc_bent_vars() to change the max perc bent values to control and how 0 and 10000 are mapped to the raw values (from raw_percentage_bent_angles())
get_raw_percentage_bent_angles
Returns the raw flexion and abduction angles used to calculate percentage bent. Returns: (flex_angles, abd_angles) Each is an array of the fingers, containing the raw angles in radians. See the tracking documentation for more info. Use:
Can be indexed like: -finger_flex_angle = flex_angles[finger_nr]
- finger_abd_angle = abd_angles[finger_nr]
Examples:
set_percentage_bent_vars
set_percentage_bent_vars(
device_id: int,
min_thetas_flexion: NDArray[float64] = array(
[0, 0.524, 0.345, 0.414, 0.4]
),
max_thetas_flexion: NDArray[float64] = array(
[1.8, 3.265, 3.0, 3.0, 2.75]
),
min_thetas_abduction: NDArray[float64] = array(
[0.0, -0.3, -0.3, -0.3, -0.3]
),
max_thetas_abduction: NDArray[float64] = array(
[0.5, 0.3, 0.3, 0.3, 0.3]
),
out_max_perc_bent: int = 10000,
)
Sets the variables used to calculate percentage bent values. With this you can override when a finger is considered bent or open:
Steps:
- Monitor your values from get_raw_percentage_bent_angles()
- Move your fingers and note down the values per finger of your desired bent and open
- Set the min and max values to the values you noted down on startup of your program, just after init()
Example:
device_ids = SG_main.init(1, SG_T.Com_type.REAL_GLOVE_USB)
device_id = device_ids[0]
SG_main.set_percentage_bent_vars(device_id,
min_flex = [0.23, -0.27, -0.27, -0.27, 0.1],
max_flex = [1.00, 2.75, 2.75, 2.75, 2.75],
min_abd = [0.04, -0.18, -0.27, -0.27, 0.1],
max_abd = [0.6, 0.18, 0.27, 0.27, 0.1])
# using SG_main.get_percentage_bents(device_id) you can now see the adjusted percentage bent values for each finger.
create_robot_hand_mapper
create_robot_hand_mapper(
device_id: int, config: Optional[PinchConfig] = None
) -> RobotHandMapper
Create and return a RobotHandMapper instance. If no config is provided, the default pinch mapping config is used.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
device_id
|
int
|
Rembrandt device ID |
required |
config
|
Optional[PinchConfig]
|
Optional custom PinchConfig |
None
|
Returns:
| Type | Description |
|---|---|
RobotHandMapper
|
Configured RobotHandMapper instance |
get_robot_hand_mapper
Return the RobotHandMapper instance for the specified device. If no config is provided, the default pinch mapping config is used.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
device_id
|
int
|
Rembrandt device ID |
required |
Returns:
| Type | Description |
|---|---|
RobotHandMapper
|
Configured RobotHandMapper instance |
create_rhm_pinch_gui
Create and register a PinchMapperGUI for the given device_id.
get_pinch_debug_info
Retrieve pinch-config debug info from the RobotHandMapper. Returns pinch diagnostics as dictionary.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
device_id
|
int
|
Rembrandt device ID |
required |
get_rhm_percentage_bents
get_rhm_percentage_bents(
device_id: int,
) -> Tuple[
Sequence[Union[int, float]], Sequence[Union[int, float]]
]
Retrieve robot-mapped percentage flexion and abduction values (pinch mapping).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
device_id
|
int
|
Rembrandt device ID |
required |
Returns:
| Name | Type | Description |
|---|---|---|
tuple |
Tuple[Sequence[Union[int, float]], Sequence[Union[int, float]]]
|
(robot_flex, robot_abd) |
update_robot_hand_mapper_gui
Update the RHM GUI
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
device_id
|
int
|
Rembrandt device ID |
required |
get_forces_sensed
set_force_goals
Sets force goals for each finger (thumb to ringfinger) in milliNewtons. Note: pinky has no force module
⚠️ Development Status
The resulting force on the wire might differ from the force goal requested to allow stable control. Scale up the forces if necessary. The milliNewtons is the tension in the wire pulling the finger back.
⚠️ Warning for motor jitter
Do not use
if contact -> set force_goal to full forcelogic, since that is not stable around the contact point. Instead, use:force_goal = K * distance_to_contactor another gradual function, tuning K to stability.
Use:
force_goals = [3000, 3000, 3000, 3000] # forces in mN per finger thumb to ring.
SG_main.set_force_goals(device_id, force_goals)
Parameters: - force_goals: List of force goals per finger
Troubleshooting jittering: - If you notice jittering, plot your input data (easy with Teleplot extension VScode). - Check sudden force goal changes you command, improve data FPS, reduce K or in another way transition force goals more gradually. - Note that filtering/averaging your data too much can cause delays, which can cause instable feedback loops as result!
set_raw_vibro_data
Sets vibration data for the specified device.
Parameters: - device_id: ID of the device - vibro_data: List of vibration data per actuator.
Still in development. See examples/vibration_example.py for an example of the current format.
SG_API.SG_simulator
SenseGlove Rembrandt Simulator Module
This module provides glove simulation functionality for testing and development when no physical device is connected. It simulates finger tracking data with various animation modes.
The simulator can generate realistic finger movements for: - Static poses (STEADY_MODE) - Sine wave animations (SINE_MODE) - Finger open/close cycles (FINGERS_OPEN_CLOSE)
Basic Usage:
import SG_API.SG_simulator as SG_sim
# Create a simulated left hand with finger animation
device_info = SG_T.Device_Info(device_id=123, handedness=SG_T.Hand.LEFT)
sim = SG_sim.create_glove_sim_device(device_info, SG_sim.Simulation_Mode.FINGERS_OPEN_CLOSE)
# Update simulation in your main loop
while True:
SG_sim.update_all_glove_sims()
time.sleep(0.001)
# You can then read data from SG_main like usual, but the output will be coming from the simulated glove.
Questions? Written by: - Amber Elferink Docs: https://adjuvo.github.io/SenseGlove-R1-API/ Support: https://www.senseglove.com/support/
Simulation_Mode
Bases: Enum
Enumeration of available simulation modes for the glove simulator.
Modes: - STEADY_MODE: Static finger pose, no animation - SINE_MODE: Sine wave oscillations across all finger joints - FINGERS_OPEN_CLOSE: Smooth finger opening and closing cycles
Examples:
# Set different animation modes
SG_sim.set_mode(device_id, SG_sim.Simulation_Mode.STEADY_MODE)
SG_sim.set_mode(device_id, SG_sim.Simulation_Mode.FINGERS_OPEN_CLOSE)
Glove_Simulator
Simulates a SenseGlove Rembrandt device for testing and development.
Provides realistic finger tracking data without requiring physical hardware. Supports multiple animation modes and allows manual control of finger positions.
Attributes: device_id (int): Unique identifier for this simulated device mode (Simulation_Mode): Current animation mode running (bool): Whether simulation is active
Examples:
# Create simulator with finger animation
sim = Glove_Simulator(device_id=123, mode=Simulation_Mode.FINGERS_OPEN_CLOSE)
# Update simulation in loop
while sim.running:
sim.update()
time.sleep(0.001)
Initialize a new glove simulator instance.
Args: device_id (int): Unique device identifier for this simulator mode (Simulation_Mode): Animation mode to use
Examples:
restart
stop
update_exo_hand_angles_rad
Update the exoskeleton joint angles in radians without changing the starting angles (which are used for base of open/close etc). So this is for calls from the update, and set_exo_rad_hand is for the user.
Args: exo_angles_rad (SG_T.Sequence[Sequence[Union[int, float]]]): Joint angles in radians
update
Update the simulation for one frame. This is called automatically by update_all_glove_sims (called by SG_main.update)
Calculates new finger positions based on the current simulation mode: - SINE_MODE: Applies sine wave oscillations to all joints - FINGERS_OPEN_CLOSE: Smooth finger opening/closing cycles - STEADY_MODE: Maintains static finger positions
set_simulation_mode
set_simulation_mode(mode: Simulation_Mode)
set_exo_rad_hand
Set exoskeleton joint angles in radians. Also saves it to the simulation starting angles, so all simulation functions (such as open close) will be done around these initial angles.
Args: exo_rad_hand_angles (SG_T.Sequence[Sequence[Union[int, float]]]): Joint angles in radians
Updates both current and starting angles for the simulation.
Examples:
# Set custom finger pose
import numpy as np
zero_angles = np.zeros((5, 8)) # 5 fingers, 8 joints each, this would be setting all exo linkages in a straight long line (not a natural pose)
angles_rad_single_finger = np.radians(_angles_deg_single_finger) # convert to radians
angles = np.tile(angles_rad_single_finger, (5, 1)) # create array using the the single finger angles for all fingers
angles[1][2] = 0.5 # Bend index finger second joint
sim.set_exo_rad_hand(angles)
set_exo_deg_hand
Set exoskeleton joint angles in degrees.
Args: exo_deg_hand_angles (SG_T.Sequence[Sequence[Union[int, float]]]): Joint angles in degrees
Convenience method that converts degrees to radians internally.
Examples:
# Set finger pose in degrees (easier to visualize)
import numpy as np
angles = np.zeros((5, 8)) # 5 fingers, 8 joints each, this would be setting all exo linkages in a straight long line (not a natural pose)
angles_deg_single_finger = np.array([0, -15, 45, -90, 120, -100, 90, -50]) # this is a more natural starting pose for a single finger
angles = np.tile(angles_deg_single_finger, (5, 1)) # create array using the the single finger angles for all fingers
angles[1][2] = 30 # Bend index exo joint 2 30 degrees
sim.set_exo_deg_hand(angles)
smoothstep
Smooth interpolation function for easing animations.
Args: t (float): Input value between 0 and 1
Returns: float: Smoothed output value between 0 and 1
Uses the smoothstep formula: t² × (3 - 2t) for smooth acceleration/deceleration.
set_angles_rad
set_angles_rad(
device_info: Device_Info,
exo_angles_rad_hand: Sequence[
Sequence[Union[int, float]]
],
)
Set finger joint angles for a simulated device (radians).
Args: device_id (int): Device ID of the simulator exo_angles_rad_hand (SG_T.Sequence[Sequence[Union[int, float]]]): Joint angles in radians
Examples:
zero_angles = np.zeros((5, 8)) # 5 fingers, 8 joints each, this would be setting all exo linkages in a straight long line (not a natural pose)
angles_rad_single_finger = np.radians(_angles_deg_single_finger) # convert to radians
angles = np.tile(angles_rad_single_finger, (5, 1)) # create array using the the single finger angles for all fingers
angles[1][2] = 0.5 # Bend index finger second joint
SG_sim.set_angles_rad(123, angles)
set_angles_deg
set_angles_deg(
device_info: Device_Info,
exo_angles_deg_hand: Sequence[
Sequence[Union[int, float]]
],
)
Set finger joint angles for a simulated device (degrees).
Args: device_info: DeviceInfo of the simulator exo_angles_deg_hand (SG_T.Sequence[Sequence[Union[int, float]]]): Joint angles in degrees
Examples:
# Set finger pose in degrees (easier to visualize)
import numpy as np
angles = np.zeros((5, 8)) # 5 fingers, 8 joints each, this would be setting all exo linkages in a straight long line (not a natural pose)
angles_deg_single_finger = np.array([0, -15, 45, -90, 120, -100, 90, -50]) # this is a more natural starting pose for a single finger
angles = [angles_deg_single_finger.copy() for _ in range(5)] # Create list of arrays, one per finger
angles[1][2] = 30 # Bend index exo joint 2 30 degrees
SG_main.SG_sim.set_angles_deg(SG_main.get_device_info(hand_id), angles)
SG_sim.set_angles_deg(device_info, angles)
create_glove_sim_device
create_glove_sim_device(
device_info: Device_Info, mode: Simulation_Mode
) -> Glove_Simulator
Create a glove simulator that integrates with the main API system.
Args: device_info (SG_T.Device_Info): Device information (device_id, handedness, etc.) mode (Simulation_Mode): Animation mode to use
Returns: Optional[Glove_Simulator]: The created simulator instance, or None if validation fails
This function creates a simulator and properly registers it with the callback system so it appears as a connected device to the main API.
Examples:
# Create left hand simulator that integrates with SG_main functions
device_info = SG_T.Device_Info(device_id=123, handedness=SG_T.Hand.LEFT)
sim = SG_sim.create_glove_sim_device(device_info, SG_sim.Simulation_Mode.SINE_MODE)
# Now you can use normal API functions
angles = SG_main.get_exo_angles_rad(123)
stop_all_glove_sims
get_sim
update_all_glove_sims
set_mode
set_mode(device_id: int, mode: Simulation_Mode)
Change the animation mode for a specific simulator.
Args: device_id (int): Device ID of the simulator mode (Simulation_Mode): New animation mode to use
Examples:
is_exo_angles_type
Check if an object is of type Sequence[Sequence[Union[int, float]]] AKA Union[List[List[float]], List[List[int]], List[np_array_type]]
set_simulation_fn
set_simulation_fn(
device_id: int,
update_fn: Callable[
[float], Sequence[Sequence[Union[int, float]]]
],
)
Set the custom update function for angles for a specific simulator, when CUSTOM_FUNCTION mode is enabled.
Args: device_id (int): Device ID of the simulator update_fn (Simulation_Mode): Function to call. Should have signature myFunction(t: float) and must return Sequence[Sequence[Union[int, float]]] containing 8 angles per finger.
Examples:
_angles_deg_single_finger = np.array([0, -15, 45, -90, 120, -100, 90, 90])
_angles_rad_single_finger = np.radians(_angles_deg_single_finger)
_starting_angles_rad_hand = np.tile(_angles_rad_single_finger, (5, 1))
def test_custom_fn(t : float) -> SG_T.Sequence[Sequence[Union[int, float]]]:
realT = t * 50
MIN_ANGLE_RAD = math.radians(75) # Set your minimum angle (e.g., 10°)
MAX_ANGLE_RAD = math.radians(90) # Set your maximum angle (e.g., 60°)
t_normalized = 0.5 * (1 - math.cos(2 * math.pi * realT))
angle = MIN_ANGLE_RAD + SG_simulator.smoothstep(t_normalized) * (MAX_ANGLE_RAD - MIN_ANGLE_RAD)
return _starting_angles_rad_hand + np.cos(angle)
SG_main.SG_sim.set_simulation_fn(hand_id, test_custom_fn)
SG_API.SG_types
Contains all types used throughout the whole API.
Questions? Written by: - Amber Elferink Docs: https://adjuvo.github.io/SenseGlove-R1-API/ Support: https://www.senseglove.com/support/
Vec3_type
module-attribute
List, tuple, or np array of 3 numbers: x, y, z
Quat_type
module-attribute
List, tuple, or np array of 4 numbers: w, x, y, z
Control_Mode
Bases: IntEnum
FORCE_GOAL_DEFAULT
class-attribute
instance-attribute
This is the default mode. The motors will try to reach the force goal set. Set the force goal to 0 to move freely.
OFF
class-attribute
instance-attribute
Motors will fully turn off, so also not move with your finger