285 lines
10 KiB
Python
285 lines
10 KiB
Python
"""
|
||
CTAG2F90D(Servo)控制器SDK (基于Modbus RTU的RS-485通信)
|
||
---------------------------------------------
|
||
一个轻量级的Python SDK,用于通过minimalmodbus库控制电机,
|
||
具有线程安全、清晰的寄存器访问方式和实时数据监控功能。
|
||
|
||
依赖安装:
|
||
pip install minimalmodbus pyserial
|
||
|
||
作者: 知行机器人
|
||
"""
|
||
|
||
import time
|
||
import threading
|
||
import minimalmodbus
|
||
import serial
|
||
|
||
# -----------------------------
|
||
# 寄存器映射 (保持寄存器,功能码0x03/0x06)
|
||
# -----------------------------
|
||
# 写入寄存器 (目标值/控制)
|
||
REG_TARGET_POS_HIGH = 0x0102 # 目标位置(高16位)
|
||
REG_TARGET_POS_LOW = 0x0103 # 目标位置(低16位)
|
||
REG_TARGET_SPEED = 0x0104 # 目标速度
|
||
REG_TARGET_FORCE = 0x0105 # 目标力/力矩
|
||
REG_TARGET_ACCELERATION = 0x0106 # 目标加速度
|
||
REG_TARGET_DECELERATION = 0x0107 # 目标减速度
|
||
REG_MOTION_TRIGGER = 0x0108 # 运动触发(0: 空闲, 1: 触发)
|
||
|
||
# 读取寄存器 (反馈/状态)
|
||
REG_REAL_POS_HIGH = 0x0418 # 实时位置(高16位)
|
||
REG_REAL_POS_LOW = 0x0419 # 实时位置(低16位)
|
||
REG_REAL_SPEED = 0x041A # 实时速度反馈
|
||
REG_REAL_CURRENT = 0x041B # 实时电流反馈
|
||
|
||
|
||
class MotorController:
|
||
def __init__(self, port: str, slave_id: int = 1, baudrate: int = 115200, timeout: float = 1.0):
|
||
"""初始化与电机控制器的Modbus RTU通信
|
||
|
||
参数:
|
||
port: 串口端口(例如,Windows上为'COM3',Linux上为'/dev/ttyUSB0')
|
||
slave_id: Modbus从机地址(默认: 1)
|
||
baudrate: 串口波特率(默认: 115200)
|
||
timeout: 读写超时时间(秒,默认: 1.0)
|
||
"""
|
||
self.instrument = minimalmodbus.Instrument(port, slave_id)
|
||
self.instrument.serial.baudrate = baudrate
|
||
self.instrument.serial.bytesize = 8
|
||
self.instrument.serial.parity = serial.PARITY_NONE
|
||
self.instrument.serial.stopbits = 1
|
||
self.instrument.serial.timeout = timeout
|
||
self.instrument.mode = minimalmodbus.MODE_RTU
|
||
self._lock = threading.Lock() # 线程锁,确保串口操作线程安全
|
||
self._monitor_running = False # 监控线程运行标志
|
||
self._monitor_thread = None # 监控线程对象
|
||
|
||
# -----------------------------
|
||
# 低级寄存器操作工具方法
|
||
# -----------------------------
|
||
def _write_register(self, addr: int, value: int) -> None:
|
||
"""写入单个16位保持寄存器(Modbus功能码0x06)
|
||
|
||
参数:
|
||
addr: 寄存器地址
|
||
value: 要写入的值
|
||
"""
|
||
with self._lock:
|
||
self.instrument.write_register(addr, value, functioncode=6)
|
||
|
||
def _read_register(self, addr: int) -> int:
|
||
"""读取单个16位保持寄存器(Modbus功能码0x03)
|
||
|
||
参数:
|
||
addr: 寄存器地址
|
||
|
||
返回:
|
||
寄存器的值
|
||
"""
|
||
with self._lock:
|
||
return self.instrument.read_register(addr, functioncode=3)
|
||
|
||
def _write_registers(self, addr: int, values: list) -> None:
|
||
"""写入多个16位保持寄存器(Modbus功能码0x10)
|
||
|
||
参数:
|
||
addr: 起始寄存器地址
|
||
values: 要写入的值列表
|
||
"""
|
||
with self._lock:
|
||
self.instrument.write_registers(addr, values)
|
||
|
||
def _read_registers(self, addr: int, count: int) -> list:
|
||
"""读取多个16位保持寄存器(Modbus功能码0x03)
|
||
|
||
参数:
|
||
addr: 起始寄存器地址
|
||
count: 要读取的寄存器数量
|
||
|
||
返回:
|
||
寄存器值的列表
|
||
"""
|
||
with self._lock:
|
||
return self.instrument.read_registers(addr, count, functioncode=3)
|
||
|
||
# -----------------------------
|
||
# 目标值/控制写入操作
|
||
# -----------------------------
|
||
def set_target_position(self, position: int) -> None:
|
||
"""设置目标位置(组合高低16位寄存器)
|
||
|
||
参数:
|
||
position: 目标位置值
|
||
"""
|
||
hi = (position >> 16) & 0xFFFF # 提取高16位
|
||
lo = position & 0xFFFF # 提取低16位
|
||
self._write_registers(REG_TARGET_POS_HIGH, [hi, lo])
|
||
|
||
def set_target_speed(self, speed: int) -> None:
|
||
"""设置目标速度
|
||
|
||
参数:
|
||
speed: 目标速度值
|
||
"""
|
||
self._write_register(REG_TARGET_SPEED, speed)
|
||
|
||
def set_target_force(self, force: int) -> None:
|
||
"""设置目标力/力矩
|
||
|
||
参数:
|
||
force: 目标力/力矩值
|
||
"""
|
||
self._write_register(REG_TARGET_FORCE, force)
|
||
|
||
def set_target_acceleration(self, acceleration: int) -> None:
|
||
"""设置目标加速度
|
||
|
||
参数:
|
||
acceleration: 目标加速度值
|
||
"""
|
||
self._write_register(REG_TARGET_ACCELERATION, acceleration)
|
||
|
||
def set_target_deceleration(self, deceleration: int) -> None:
|
||
"""设置目标减速度
|
||
|
||
参数:
|
||
deceleration: 目标减速度值
|
||
"""
|
||
self._write_register(REG_TARGET_DECELERATION, deceleration)
|
||
|
||
def trigger_motion(self) -> None:
|
||
"""使用已配置的目标参数触发运动"""
|
||
self._write_register(REG_MOTION_TRIGGER, 1)
|
||
|
||
def temp_move(self, position_mm: int, speed_pct: int, force_pct: int,
|
||
accel: int, decel: int, trigger: bool = True) -> None:
|
||
"""临时运动控制方法,一次性设置所有运动参数并可选触发运动
|
||
|
||
参数:
|
||
position_mm: 目标位置(毫米)
|
||
speed_pct: 速度百分比
|
||
force_pct: 力/力矩百分比
|
||
accel: 加速度值
|
||
decel: 减速度值
|
||
trigger: 是否立即触发运动,默认为True
|
||
"""
|
||
self.set_target_position(position_mm)
|
||
self.set_target_speed(speed_pct)
|
||
self.set_target_force(force_pct)
|
||
self.set_target_acceleration(accel)
|
||
self.set_target_deceleration(decel)
|
||
|
||
if trigger:
|
||
self.trigger_motion()
|
||
print(f"已触发运动到位置 {position_mm},速度 {speed_pct}%,力 {force_pct}%")
|
||
|
||
# -----------------------------
|
||
# 反馈/状态读取操作
|
||
# -----------------------------
|
||
def read_real_position(self) -> int:
|
||
"""读取实时位置(组合高低16位寄存器)
|
||
|
||
返回:
|
||
组合后的32位实时位置(处理有符号值)
|
||
"""
|
||
regs = self._read_registers(REG_REAL_POS_HIGH, 2)
|
||
hi, lo = regs[0], regs[1]
|
||
combined = (hi << 16) | lo
|
||
# 处理32位有符号整数
|
||
if combined & 0x80000000: # 最高位为1表示负数
|
||
combined = combined - 0x100000000
|
||
return combined
|
||
|
||
def read_real_speed(self) -> int:
|
||
"""读取实时速度反馈
|
||
|
||
返回:
|
||
实时速度值
|
||
"""
|
||
return self._read_register(REG_REAL_SPEED)
|
||
|
||
def read_real_current(self) -> int:
|
||
"""读取实时电流反馈
|
||
|
||
返回:
|
||
实时电流值
|
||
"""
|
||
return self._read_register(REG_REAL_CURRENT)
|
||
|
||
# -----------------------------
|
||
# 实时监控线程
|
||
# -----------------------------
|
||
def _monitor_loop(self, interval: float = 0.5) -> None:
|
||
"""实时数据监控的后台循环"""
|
||
while self._monitor_running:
|
||
try:
|
||
pos = self.read_real_position()
|
||
speed = self.read_real_speed()
|
||
current = self.read_real_current()
|
||
print(f"[实时数据] 位置: {pos:6d}, 速度: {speed:3d}, 电流: {current:3d}")
|
||
except Exception as e:
|
||
print(f"监控错误: {e}")
|
||
time.sleep(interval)
|
||
|
||
def start_monitoring(self, interval: float = 0.01) -> None:
|
||
"""启动实时数据监控线程
|
||
|
||
参数:
|
||
interval: 监控间隔(秒,默认: 0.01)
|
||
"""
|
||
if not self._monitor_running:
|
||
self._monitor_running = True
|
||
self._monitor_thread = threading.Thread(
|
||
target=self._monitor_loop,
|
||
args=(interval,)
|
||
)
|
||
self._monitor_thread.daemon = True # 守护线程,主程序退出时自动结束
|
||
self._monitor_thread.start()
|
||
print("实时监控已启动。")
|
||
|
||
def stop_monitoring(self) -> None:
|
||
"""停止实时数据监控线程"""
|
||
self._monitor_running = False
|
||
if self._monitor_thread:
|
||
self._monitor_thread.join(timeout=1.0) # 等待线程结束
|
||
print("实时监控已停止。")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
# 示例用法(根据实际设备调整端口/从机ID)
|
||
PORT = "COM5"
|
||
SLAVE_ID = 1
|
||
sdk = MotorController(PORT, SLAVE_ID, baudrate=115200, timeout=1.0)
|
||
|
||
try:
|
||
# 启动实时监控
|
||
sdk.start_monitoring(interval=0.5)
|
||
|
||
# 循环控制:5次往复运动
|
||
# 假设 9000 为闭合位置,0 为张开位置
|
||
# 最后一次循环结束后,确保处于张开位置 (0)
|
||
for i in range(5):
|
||
print(f"\n--- 第 {i+1}/5 次循环 ---")
|
||
|
||
# 阶段1: 运动到位置 9000 (闭合?)
|
||
sdk.temp_move(position_mm=9000, speed_pct=5, force_pct=15, accel=60, decel=60, trigger=True)
|
||
time.sleep(5) # 等待运动完成
|
||
|
||
# 阶段2: 运动到位置 0 (张开?)
|
||
sdk.temp_move(position_mm=0, speed_pct=5, force_pct=15, accel=60, decel=60, trigger=True)
|
||
time.sleep(5) # 等待运动完成
|
||
|
||
print("\n循环结束,确认最终状态为张开 (位置 0)...")
|
||
# 再次发送指令确保最终状态为 0 (虽然循环最后一步已经是 0,但这保证了"最后要张开"的要求)
|
||
sdk.temp_move(position_mm=0, speed_pct=5, force_pct=15, accel=60, decel=60, trigger=True)
|
||
time.sleep(1)
|
||
|
||
except KeyboardInterrupt:
|
||
print("\n程序被用户中断。")
|
||
except Exception as e:
|
||
print(f"执行过程中出错: {e}")
|
||
finally:
|
||
sdk.stop_monitoring()
|
||
print("程序已完成。")
|