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
- Controls the Gloria-M gripper motor via a serial-to-CAN adapter
- Provides the
GloriaGripperfacade 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 (
GloriaSdkErrorand 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/ttyUSB0or/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)
| Name | Description |
|---|---|
GloriaGripper | Facade class that encapsulates MotorAPI / MotionAPI / ParamAPI; recommended as the preferred entry point |
3.2 Low-Level Objects (Low-Level Access)
| Name | Description |
|---|---|
Actuator | Actuator configuration object, containing command ID, feedback ID, clamping limits, and safe position range |
ActuatorState | Stores the current position, speed, torque, and most recent update time |
SerialCanAdapter | Serial-to-CAN communication adapter |
CanController | Low-level controller responsible for command sending, response parsing, and parameter read/write |
Variable | Motor parameter register enumeration, e.g. CTRL_MODE, PMAX, VMAX |
TorqueBaseline | No-load torque baseline reading and interpolation object, used for linkage gripper force control |
3.3 Public Types
| Name | Description |
|---|---|
ControlMode | Control mode enumeration, including MIT, POS_VEL, etc. |
Limits | Scaling range for MIT mode packing/unpacking, common values are pmax=3.14, vmax=10.0, tmax=12.0 |
PositionRange | Safe position range; values beyond it are automatically clamped |
MIT_SAFE_Q_MIN / MIT_SAFE_Q_MAX | Default safe range constants provided in the package |
apply_limits_and_save | Writes clamping parameters to the motor and saves them (legacy interface; recommended to use gripper.params.apply_limits() instead) |
3.4 Exception Hierarchy
| Name | Description |
|---|---|
GloriaSdkError | Base class that catches all SDK exceptions |
GloriaConnectionError | Serial port cannot be opened |
GloriaCommunicationError | Timeout or frame format error |
GloriaConfigError | Parameter out of range or invalid configuration |
GloriaModeError | Mode 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
| Attribute | Type | Description |
|---|---|---|
state | ActuatorState | Latest feedback snapshot (position, speed, torque) |
current_mode | ControlMode | None | The last control mode acknowledged by the motor; None before set_mode() |
is_connected | bool | True indicates the serial port is open |
4.3 .motor — MotorAPI
| Method | Description |
|---|---|
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
| Method | Description |
|---|---|
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
| Method | Description |
|---|---|
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:
where 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_qdirection convention must hold open_tauis positive,close_tauis negativeradius-profileis a placeholder value and must be replaced with actual calibration data- It is recommended to load the no-load baseline
baseline-csvto 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:
| Parameter | Default | Description |
|---|---|---|
--port | COM12 | Serial port number |
--baud | 921600 | Serial baud rate |
--id | 0x01 | Motor command CAN ID |
--fb-id | 0x201 | Motor feedback CAN ID |
--open-q | 2.77 | Maximum gripper open position [rad] |
--close-q | 0.003 | Gripper closed position [rad] |
--close-tau | -1.25 | Torque in the closing direction [N·m], must be negative |
--kd | 0.8 | MIT torque control damping term |
--stop-force | 0.0 | Stop after the estimated gripping force reaches this threshold; 0 means disabled [N] |
--radius-mm | 12.0 | Equivalent moment arm used to estimate gripping force [mm] |
--timeout | 5.0 | Maximum collection time [s] |
--position-epsilon | 0.02 | Tolerance for determining arrival at the closed position [rad] |
--bin-width | 0.05 | Bin width when generating the baseline curve by position [rad] |
--save-dir | demos/baseline | CSV output directory |
--save-prefix | close_baseline | CSV file name prefix |
--no-save | false | Run 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:
| Field | Description |
|---|---|
elapsed_s | Time from the start of this test to the current sample point [s] |
position_rad | Current motor position feedback [rad] |
velocity_rad_s | Current motor speed feedback [rad/s] |
tau_cmd_nm | Current MIT torque command sent [N·m] |
tau_fb_nm | Motor feedback torque [N·m] |
force_est_n | Gripping 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:
| Field | Description |
|---|---|
position_mean_rad | Average position within the current position bin [rad] |
velocity_mean_rad_s | Average speed within the current position bin [rad/s] |
tau_fb_mean_nm | Average feedback torque within the current position bin [N·m] |
force_est_mean_n | Average estimated gripping force within the current position bin [N] |
sample_count | Number 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}")
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
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).
| Method | Function |
|---|---|
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
| Parameter | Default |
|---|---|
| Baud rate | 921600 |
| Command CAN ID | 0x01 |
| Feedback CAN ID | 0x101 |
| MIT scaling | pmax=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.7are 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
- Use low speed and low torque for the first debugging
- Verify single open/close first, then proceed to looping or force control
- Always call
disable()before exiting - For force control, first verify the no-load baseline and moment-arm curve
8. Troubleshooting
| Symptom / Exception | Troubleshooting Direction |
|---|---|
GloriaConnectionError / serial port cannot be opened | Check the serial port number, whether the CAN adapter appears in Device Manager; on Linux, check dialout group permissions |
GloriaCommunicationError / timeout or frame error | Check 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 failed | Check command_id / feedback_id; the same applies when the low-level set_control_mode() returns False |
GloriaConfigError / parameter out of range | Check whether the Limits and PositionRange values are valid |
| No status feedback | Confirm poll=True or manually call gripper.motor.poll() / ctrl.poll(); check the feedback ID |
| Open/close direction reversed | Check the open_q / close_q definitions, torque sign, and PositionRange interval |