Skip to main content

Yunxi Python SDK

Gloria-M-SDK is the Python development package for the Gloria-M series mechanical gripper. It communicates with the motor via a serial-to-CAN adapter and encapsulates interfaces such as MIT/PV control, parameter read/write, and status parsing.

This document is based on the main version of Synria-Robotics/Gloria-M-SDK.

1. Introduction

1.1 Core Capabilities

Core Capabilities
  • Controls the Gloria-M gripper motor via a serial-to-CAN adapter
  • Provides the GloriaGripper facade class as the recommended entry point, encapsulating MotorAPI / MotionAPI / ParamAPI
  • Supports two commonly used control methods: MIT mode and PV mode
  • Supports basic control commands such as enable, disable, set zero, and mode switching
  • Supports reading feedback states such as position, speed, and torque
  • Supports parameter read/write and saving, e.g. PMAX, VMAX, TMAX, CTRL_MODE
  • Provides a linkage gripper force control example, which can be combined with a no-load torque baseline for compensation
  • Provides a complete exception hierarchy (GloriaSdkError and its subclasses)

1.2 SDK Directory Structure

The core structure of the official repository's main branch is as follows:

Gloria-M-SDK/
├── src/gloria_m_sdk/
│ ├── __init__.py # Package entry, exports public API
│ ├── client.py # GloriaGripper facade layer (recommended entry)
│ ├── exceptions.py # Exception hierarchy (GloriaSdkError and subclasses)
│ ├── api/ # API layer: sub-APIs split by domain
│ │ ├── __init__.py
│ │ ├── base.py # BaseAPI (shared controller access)
│ │ ├── motor_api.py # MotorAPI: enable/disable/mode/zero/read
│ │ ├── motion_api.py # MotionAPI: send_mit / send_pos_vel
│ │ └── param_api.py # ParamAPI: read/write registers, save, apply limits
│ ├── actuator.py
│ ├── controller.py
│ ├── protocol_mit.py
│ ├── serial_can_adapter.py
│ ├── param_config.py
│ ├── registers.py
│ ├── types.py
│ ├── constants.py
│ └── gripper_baseline.py
├── demos/
│ ├── 01_gripper_quicktest.py # PV mode reciprocating motion test
│ ├── 02_pv_control.py # PV mode compliant closing
│ ├── 03_mit_linkage_force_control.py # MIT linkage gripper force control
│ ├── mit_close_baseline.py # MIT no-load closing baseline collection
│ └── baseline/ # Baseline data CSV output directory
├── requirements.txt
└── pyproject.toml

2. Installation and Preparation

2.1 Environment Requirements

  • Python 3.11 or later
  • Serial-to-CAN adapter
  • A powered Gloria-M gripper or corresponding motor

2.2 Installation Steps

Clone the specified version of the source code:

git clone https://github.com/Synria-Robotics/Gloria-M-SDK.git -b main
cd Gloria-M-SDK

Install dependencies:

pip install -r requirements.txt
pip install -e .

2.3 Serial Connection Check

  • Windows: Confirm the serial port number in Device Manager, e.g. COM5
  • Linux: Common ports are /dev/ttyUSB0 or /dev/ttyACM0
  • The default baud rate is 921600

On Linux, if you encounter permission issues, run:

sudo usermod -a -G dialout $USER

Log in again before accessing the serial port.

3. Public API Overview

gloria_m_sdk exports the following commonly used objects at the package entry:

3.1 Recommended Entry (Facade Layer)

NameDescription
GloriaGripperFacade class that encapsulates MotorAPI / MotionAPI / ParamAPI; recommended as the preferred entry point

3.2 Low-Level Objects (Low-Level Access)

NameDescription
ActuatorActuator configuration object, containing command ID, feedback ID, clamping limits, and safe position range
ActuatorStateStores the current position, speed, torque, and most recent update time
SerialCanAdapterSerial-to-CAN communication adapter
CanControllerLow-level controller responsible for command sending, response parsing, and parameter read/write
VariableMotor parameter register enumeration, e.g. CTRL_MODE, PMAX, VMAX
TorqueBaselineNo-load torque baseline reading and interpolation object, used for linkage gripper force control

3.3 Public Types

NameDescription
ControlModeControl mode enumeration, including MIT, POS_VEL, etc.
LimitsScaling range for MIT mode packing/unpacking, common values are pmax=3.14, vmax=10.0, tmax=12.0
PositionRangeSafe position range; values beyond it are automatically clamped
MIT_SAFE_Q_MIN / MIT_SAFE_Q_MAXDefault safe range constants provided in the package
apply_limits_and_saveWrites clamping parameters to the motor and saves them (legacy interface; recommended to use gripper.params.apply_limits() instead)

3.4 Exception Hierarchy

NameDescription
GloriaSdkErrorBase class that catches all SDK exceptions
GloriaConnectionErrorSerial port cannot be opened
GloriaCommunicationErrorTimeout or frame format error
GloriaConfigErrorParameter out of range or invalid configuration
GloriaModeErrorMode switch not acknowledged by the motor

4. GloriaGripper Facade API

GloriaGripper is a facade class that encapsulates the low-level five-layer architecture. It is recommended that all new code use this entry point.

4.1 Constructor Parameters

GloriaGripper(
port, # "COM5" or "/dev/ttyUSB0"
*,
baudrate=921_600,
command_id=0x01, # Motor command CAN ID
feedback_id=0x101, # Motor feedback CAN ID
limits=None, # Limits(pmax, vmax, tmax), default (3.14, 10, 12)
safe_position=None, # PositionRange(min, max) — position clamping
baseline_csv=None, # No-load torque baseline CSV path
timeout=0.5, # Serial read timeout [s]
)

4.2 Attributes

AttributeTypeDescription
stateActuatorStateLatest feedback snapshot (position, speed, torque)
current_modeControlMode | NoneThe last control mode acknowledged by the motor; None before set_mode()
is_connectedboolTrue indicates the serial port is open

4.3 .motor — MotorAPI

MethodDescription
enable()Send the enable command
disable()Send the disable command
set_zero()Set the current position as the zero point
set_mode(mode)Switch the control mode; raises GloriaModeError on failure
refresh()Broadcast a status request and update gripper.state
poll()Parse pending RX packets and update the actuator state

4.4 .motion — MotionAPI

MethodDescription
send_mit(*, kp, kd, q, dq, tau)Send a MIT torque control frame
send_pos_vel(*, position, velocity)Send a PV position + velocity frame

4.5 .params — ParamAPI

MethodDescription
read(rid, *, timeout_s)Read a register; returns None on timeout
write_f32(rid, value)Write a float32 register
write_u32(rid, value)Write a uint32 register
save()Persist parameters to Flash
apply_limits(limits)Write PMAX/VMAX/TMAX and save

4.6 Quick Start Example

from gloria_m_sdk import GloriaGripper, ControlMode

with GloriaGripper("COM5") as g:
g.motor.set_mode(ControlMode.POS_VEL)
g.motor.enable()
g.motor.refresh()
print(f"position = {g.state.position:.3f} rad")

# Move to the open position
g.motion.send_pos_vel(position=2.5, velocity=1.0)

5. Example Scripts (Demos)

5.1 01_gripper_quicktest.py

PV mode round-trip quick test. The gripper continuously moves back and forth between open_q and close_q, automatically switching direction after the feedback position stabilizes. It is used to verify that serial communication, CAN ID, and the open/close direction are correct.

Run example:

python demos/01_gripper_quicktest.py --port COM5 --id 0x01 --close-q 0.0 --open-q 2.5 --vel 1.0

Core logic:

from gloria_m_sdk import (
Actuator,
CanController,
ControlMode,
Limits,
PositionRange,
SerialCanAdapter,
apply_limits_and_save,
)

safe_q = PositionRange(min=min(args.open_q, args.close_q), max=max(args.open_q, args.close_q))
limits = Limits(pmax=3.14, vmax=10.0, tmax=12.0)

act = Actuator(
name="gripper_cycle",
command_id=args.id,
feedback_id=args.fb_id,
limits=limits,
safe_position=safe_q,
)

with SerialCanAdapter(args.port, baudrate=args.baud, timeout=0.5) as adapter:
apply_limits_and_save(adapter, motor_id=args.id, limits=limits)

ctrl = CanController(adapter)
ctrl.register(act)

ok = ctrl.set_control_mode(act, ControlMode.POS_VEL)
print(f"[mode] set PV: {ok}")
ctrl.enable(act)
ctrl.refresh_state(act)

while True:
target = float(args.close_q) if cycle % 2 == 0 else float(args.open_q)
ctrl.send_pos_vel(act, position=target, velocity=args.velocity, poll=True)

5.2 02_pv_control.py

PV mode compliant closing. First opens to open_q at a higher speed, then slowly closes to close_q at low speed and holds, suitable for compliant gripping scenarios.

Run example:

python demos/02_pv_control.py --port COM5 --open-q 2.5 --close-q 0.0 --close-vel 0.3

Execution phases: open → slow close → hold. Key code:

with SerialCanAdapter(args.port, baudrate=args.baud, timeout=0.5) as adapter:
apply_limits_and_save(adapter, motor_id=args.id, limits=limits)

ctrl = CanController(adapter)
ctrl.register(act)

ok = ctrl.set_control_mode(act, ControlMode.POS_VEL)
print(f"[mode] set PV: {ok}")
ctrl.enable(act)
ctrl.refresh_state(act)

open_q = act.clamp_position(args.open_q)
_move_to(ctrl, act, target=open_q, velocity=args.open_vel,
loop_sleep=args.loop_sleep, print_hz=args.print_hz,
settle_threshold=args.settle_threshold, settle_time=args.settle_time,
timeout=args.timeout)

close_q = act.clamp_position(args.close_q)
_move_to(ctrl, act, target=close_q, velocity=args.close_vel,
loop_sleep=args.loop_sleep, print_hz=args.print_hz,
settle_threshold=args.settle_threshold, settle_time=args.settle_time,
timeout=args.timeout)

hold_end = time.perf_counter() + args.hold_time
while time.perf_counter() < hold_end:
ctrl.send_pos_vel(act, position=close_q, velocity=0.0, poll=True)

5.3 03_mit_linkage_force_control.py

MIT mode linkage gripper force control. Combines the moment-arm curve and feedback torque for contact determination, achieving "open → approach → contact → hold → release" closed-loop control.

Run example:

python demos/03_mit_linkage_force_control.py --port COM5 --id 0x01 --fb-id 0x101 --open-q 2.77 --close-q 0.003 --target-force 15

For the 4340 high-power gripper version, you need to specify the corresponding baseline file and adjust the force threshold:

python demos/03_mit_linkage_force_control.py --port COM5 --baseline-csv ".\demos\baseline\close_baseline_4340.csv" --target-force 30 --contact-force 60

If --baseline-csv is not specified, the script uses .\demos\baseline\close_baseline_4310.csv by default.

The approximate gripping-force relationship:

Fgripτcloser(q)F_{grip} \approx \frac{\tau_{close}}{r(q)}

where r(q)r(q) is the equivalent moment arm at the current position. Key initialization:

from gloria_m_sdk import (
Actuator,
CanController,
ControlMode,
Limits,
PositionRange,
SerialCanAdapter,
TorqueBaseline,
Variable,
apply_limits_and_save,
)

safe_q = PositionRange(min=min(raw_open_q, raw_close_q), max=max(raw_open_q, raw_close_q))
limits = Limits(pmax=3.14, vmax=10.0, tmax=12.0)
baseline = TorqueBaseline.from_csv(args.baseline_csv) if args.baseline_csv else None

act = Actuator(
name="linkage_gripper",
command_id=args.id,
feedback_id=args.fb_id,
limits=limits,
safe_position=safe_q,
)

with SerialCanAdapter(args.port, baudrate=args.baud, timeout=0.5) as adapter:
apply_limits_and_save(adapter, motor_id=args.id, limits=limits)

ctrl = CanController(adapter)
ctrl.register(act)

ok = ctrl.set_control_mode(act, ControlMode.MIT)
print(f"[mode] set MIT: {ok}")
ctrl.enable(act)
ctrl.refresh_state(act)

The control loop dynamically adjusts parameters based on the phase and sends them via send_mit():

ctrl.send_mit(
act,
kp=float(kp),
kd=float(kd),
q=float(q_target),
dq=0.0,
tau=float(tau_cmd),
poll=True,
)

Notes:

  • The close_q <= open_q direction convention must hold
  • open_tau is positive, close_tau is negative
  • radius-profile is a placeholder value and must be replaced with actual calibration data
  • It is recommended to load the no-load baseline baseline-csv to improve gripping-force accuracy

5.4 mit_close_baseline.py

MIT no-load closing baseline collection. In MIT mode, the gripper closes under no load with a fixed negative torque, recording the position, speed, feedback torque, and estimated gripping force during the closing process. It outputs a baseline CSV file for use by 03_mit_linkage_force_control.py, used to subtract the gripper's own friction and mechanism resistance.

It is recommended to run it without a gripped object:

python demos/mit_close_baseline.py --port COM5 --close-tau -1.25

Common parameters:

ParameterDefaultDescription
--portCOM12Serial port number
--baud921600Serial baud rate
--id0x01Motor command CAN ID
--fb-id0x201Motor feedback CAN ID
--open-q2.77Maximum gripper open position [rad]
--close-q0.003Gripper closed position [rad]
--close-tau-1.25Torque in the closing direction [N·m], must be negative
--kd0.8MIT torque control damping term
--stop-force0.0Stop after the estimated gripping force reaches this threshold; 0 means disabled [N]
--radius-mm12.0Equivalent moment arm used to estimate gripping force [mm]
--timeout5.0Maximum collection time [s]
--position-epsilon0.02Tolerance for determining arrival at the closed position [rad]
--bin-width0.05Bin width when generating the baseline curve by position [rad]
--save-dirdemos/baselineCSV output directory
--save-prefixclose_baselineCSV file name prefix
--no-savefalseRun the test only, do not save CSV

After running, two CSV files are generated by default:

demos/baseline/{save_prefix}_{timestamp}_raw.csv
demos/baseline/{save_prefix}_{timestamp}_binned.csv

Each row of the raw sampling CSV corresponds to one sample in the control loop:

FieldDescription
elapsed_sTime from the start of this test to the current sample point [s]
position_radCurrent motor position feedback [rad]
velocity_rad_sCurrent motor speed feedback [rad/s]
tau_cmd_nmCurrent MIT torque command sent [N·m]
tau_fb_nmMotor feedback torque [N·m]
force_est_nGripping force estimated from feedback torque [N], computed as max(0, -tau_fb_nm) / (radius_mm / 1000)

The binned baseline CSV groups raw sample points with similar positions and averages each group, making it suitable for subsequent use as a baseline curve:

FieldDescription
position_mean_radAverage position within the current position bin [rad]
velocity_mean_rad_sAverage speed within the current position bin [rad/s]
tau_fb_mean_nmAverage feedback torque within the current position bin [N·m]
force_est_mean_nAverage estimated gripping force within the current position bin [N]
sample_countNumber of raw sample points included in the current position bin

5.5 Obtaining Gripper Operating Parameters

5.5.1 Real-Time Status

Position, speed, and torque are automatically written to act.state after each control response:

ctrl.refresh_state(act)

print(f"position = {act.state.position:+.3f} rad")
print(f"velocity = {act.state.velocity:+.3f} rad/s")
print(f"torque = {act.state.torque:+.3f} Nm")
print(f"updated = {act.state.updated_at:.3f}")

Sending a command with poll=True updates it automatically; you can also call ctrl.refresh_state(act) manually.

When using GloriaGripper, access it via gripper.state:

gripper.motor.refresh()
print(f"position = {gripper.state.position:+.3f} rad")

5.5.2 Reading Register Parameters

When using GloriaGripper, you can read any register through the high-level gripper.params.read() interface:

from gloria_m_sdk import GloriaGripper, Variable

with GloriaGripper("COM5") as g:
mode = g.params.read(Variable.CTRL_MODE)
pmax = g.params.read(Variable.PMAX)
vmax = g.params.read(Variable.VMAX)
tmax = g.params.read(Variable.TMAX)

print(f"CTRL_MODE = {mode}")
print(f"PMAX = {pmax}")
print(f"VMAX = {vmax}")
print(f"TMAX = {tmax}")

When using the low-level CanController, you need to send a parameter-read frame directly to 0x7FF:

import struct
import time

from gloria_m_sdk import Variable


def read_param_once(adapter, *, target_id: int, rid: int, timeout_s: float = 0.03):
can_id_l = int(target_id) & 0xFF
can_id_h = (int(target_id) >> 8) & 0xFF

# 0x33 means read parameter
adapter.send(0x7FF, bytes([can_id_l, can_id_h, 0x33, int(rid) & 0xFF, 0, 0, 0, 0]))

deadline = time.time() + float(timeout_s)
expect_ids = {int(target_id), int(0x100 + int(target_id)), 0x00}
is_u32 = (7 <= int(rid) <= 10) or (13 <= int(rid) <= 16) or (35 <= int(rid) <= 36)

while time.time() < deadline:
for pkt in adapter.read_packets():
if len(pkt.data) != 8:
continue
if int(pkt.can_id) not in expect_ids and not (
pkt.data[0] == can_id_l and pkt.data[1] == can_id_h
):
continue
if pkt.data[2] not in (0x33, 0x55):
continue
if int(pkt.data[3]) != (int(rid) & 0xFF):
continue

if is_u32:
return struct.unpack("<I", pkt.data[4:8])[0]
return struct.unpack("<f", pkt.data[4:8])[0]

time.sleep(0.002)

return None

Usage example:

mode = read_param_once(adapter, target_id=act.command_id, rid=int(Variable.CTRL_MODE))
pmax = read_param_once(adapter, target_id=act.command_id, rid=int(Variable.PMAX))
vmax = read_param_once(adapter, target_id=act.command_id, rid=int(Variable.VMAX))
tmax = read_param_once(adapter, target_id=act.command_id, rid=int(Variable.TMAX))
pm = read_param_once(adapter, target_id=act.command_id, rid=int(Variable.p_m))
xout = read_param_once(adapter, target_id=act.command_id, rid=int(Variable.xout))

print(f"CTRL_MODE = {mode}")
print(f"PMAX = {pmax}")
print(f"VMAX = {vmax}")
print(f"TMAX = {tmax}")
print(f"p_m = {pm}")
print(f"xout = {xout}")
tip

For application-layer control, prefer using act.state (position / velocity / torque); only read registers when troubleshooting mode switching or clamping configuration, to avoid introducing too many parameter-read requests in high-frequency loops.

6. Low-Level CanController Method Quick Reference

info

CanController is a low-level interface intended for advanced users who need fine-grained control. Regular users are recommended to use the GloriaGripper facade (see Section 4).

MethodFunction
register(act)Register the actuator
enable(act)Enable the motor
disable(act)Disable the motor
set_zero(act)Set the current position as the zero point
set_control_mode(act, mode)Switch the control mode (MIT / POS_VEL)
refresh_state(act)Broadcast a status response request
send_mit(act, *, kp, kd, q, dq, tau)Send a MIT command
send_pos_vel(act, *, position, velocity)Send a PV command

7. Parameter Conventions and Notes

7.1 Default Parameters

ParameterDefault
Baud rate921600
Command CAN ID0x01
Feedback CAN ID0x101
MIT scalingpmax=3.14, vmax=10.0, tmax=12.0

7.2 Position and Direction Conventions

  • MIT_SAFE_Q_MIN=0.0, MIT_SAFE_Q_MAX=2.7 are the general safe range
  • The demos default to open_q > close_q (e.g. open_q=2.5, close_q=0.0)
  • Positive torque = open, negative torque = close

In actual use, PositionRange, open_q / close_q, and the torque sign must all be consistent with the mechanism direction; otherwise direction reversal or limit jitter may occur.

7.3 Safety Recommendations

  1. Use low speed and low torque for the first debugging
  2. Verify single open/close first, then proceed to looping or force control
  3. Always call disable() before exiting
  4. For force control, first verify the no-load baseline and moment-arm curve

8. Troubleshooting

Symptom / ExceptionTroubleshooting Direction
GloriaConnectionError / serial port cannot be openedCheck the serial port number, whether the CAN adapter appears in Device Manager; on Linux, check dialout group permissions
GloriaCommunicationError / timeout or frame errorCheck the baud rate (default 921600), whether the CAN cable is properly connected; confirm feedback_id matches the motor's actual feedback ID
GloriaModeError / mode switch failedCheck command_id / feedback_id; the same applies when the low-level set_control_mode() returns False
GloriaConfigError / parameter out of rangeCheck whether the Limits and PositionRange values are valid
No status feedbackConfirm poll=True or manually call gripper.motor.poll() / ctrl.poll(); check the feedback ID
Open/close direction reversedCheck the open_q / close_q definitions, torque sign, and PositionRange interval