Files
5dof/else/qiege_motor.py
2025-09-17 16:39:51 +08:00

183 lines
5.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# qiege_motor.py
# 切割电机控制模块(支持 CAN 和 USB 两种通信方式)
# 使用 control(enable) 统一接口
import time
import serial
# ------------------------ 配置区 ------------------------
# <<< 切换通信模式 >>>
MODE = 'usb' # 'can' 或 'usb'
# CAN 模式配置
CAN_SERIAL_PORT = '/dev/ttyACM0' # CAN 转串口设备
CAN_BAUDRATE = 921600
CAN_MOTOR_ID = 0x05 # 轮趣电机对应的 CAN ID
# USB 串口模式配置(轮趣 HEX 指令)
USB_SERIAL_PORT = '/dev/ttyACM0' # 或 '/dev/ttyUSB0'
USB_BAUDRATE = 115200
# 目标转速rad/s
CUTTING_SPEED_RAD_PER_SEC = 8.0 # 对应 HEX 指令中的 speed=8 → 80 (0x50)
# 是否启用调试输出
DEBUG = True
# ------------------------ 全局变量 ------------------------
usb_serial = None
can_serial = None
# ------------------------ USB 模式HEX 指令生成与发送 ------------------------
def generate_usb_command(speed):
"""
生成轮趣电机 USB 串口 HEX 指令
格式: 7B 01 01 00 20 00 00 00 <speed_byte> <checksum> 7D
speed: float (rad/s), 映射为 0~255
"""
# 将 rad/s 映射为速度字节示例8 rad/s → 80
# 你可以根据实际响应调整映射关系
speed_val = int(speed * 10) # 示例8 → 80
speed_byte = max(0, min(255, speed_val))
# 构造前9字节
cmd = [
0x7B, 0x01, 0x02, # 帧头、地址、模式
0x01, 0x20, # 方向、细分
0x0E, 0x10, # 位置高、低(持续旋转)
0x00, 0x64, # 速度值
]
# 计算校验和前9字节异或
checksum = 0
for b in cmd:
checksum ^= b
cmd.append(checksum)
cmd.append(0x7D) # 帧尾
return bytes(cmd)
def send_usb_command(port, baudrate, command):
"""发送 USB 串口指令"""
global usb_serial
try:
if usb_serial is None or not usb_serial.is_open:
usb_serial = serial.Serial(port, baudrate, timeout=0.5)
time.sleep(0.5) # 等待串口稳定
usb_serial.write(command)
if DEBUG:
print(f"[qiege_motor] USB 发送: {command.hex().upper()}")
return True
except Exception as e:
if DEBUG:
print(f"[qiege_motor] USB 发送失败: {e}")
usb_serial = None
return False
# ------------------------ CAN 模式CAN 指令发送 ------------------------
def send_can_command(data, can_id=CAN_MOTOR_ID):
"""通过 CAN 发送指令(假设使用串口转 CAN 模块)"""
global can_serial
try:
if can_serial is None or not can_serial.is_open:
can_serial = serial.Serial(CAN_SERIAL_PORT, CAN_BAUDRATE, timeout=0.5)
time.sleep(0.5)
# 示例:简单封装 CAN 帧ID + DLC + Data
# 格式根据你使用的 CAN 模块调整(如 CANable、TJA1050 等)
can_frame = [
(can_id >> 24) & 0xFF,
(can_id >> 16) & 0xFF,
(can_id >> 8) & 0xFF,
can_id & 0xFF,
len(data) # DLC
] + list(data)
can_serial.write(bytes(can_frame))
if DEBUG:
print(f"[qiege_motor] CAN 发送 → ID:{can_id:05X}, Data:{[f'{b:02X}' for b in data]}")
return True
except Exception as e:
if DEBUG:
print(f"[qiege_motor] CAN 发送失败: {e}")
can_serial = None
return False
def generate_can_command(speed_rpm=300, direction=1, microstep=32):
"""生成轮趣 CAN 速度指令(示例)"""
speed_01rpm = int(abs(speed_rpm) * 10)
high_byte = (speed_01rpm >> 8) & 0xFF
low_byte = speed_01rpm & 0xFF
direction_bit = 1 if direction else 0
data = [
0x88, # 控制字:速度模式
((direction_bit & 0x01) << 7) | (microstep & 0x7F),
0x00, 0x00,
high_byte, low_byte,
0x00, 0x00
]
return data
# ------------------------ 统一控制接口 ------------------------
def start_motor():
"""启动切割电机"""
if MODE == 'usb':
cmd = generate_usb_command(CUTTING_SPEED_RAD_PER_SEC)
return send_usb_command(USB_SERIAL_PORT, USB_BAUDRATE, cmd)
elif MODE == 'can':
# 示例8 rad/s ≈ 76.4 RPM
rpm = CUTTING_SPEED_RAD_PER_SEC * 60 / (2 * 3.1416)
cmd = generate_can_command(speed_rpm=rpm, direction=1)
return send_can_command(cmd, can_id=CAN_MOTOR_ID)
else:
print(f"[qiege_motor] 错误:不支持的模式 '{MODE}'")
return False
def stop_motor():
"""停止切割电机"""
if MODE == 'usb':
cmd = generate_usb_command(0.0)
return send_usb_command(USB_SERIAL_PORT, USB_BAUDRATE, cmd)
elif MODE == 'can':
cmd = generate_can_command(speed_rpm=0, direction=1)
return send_can_command(cmd, can_id=CAN_MOTOR_ID)
else:
return False
def control(enable: bool):
"""
统一控制接口
:param enable: True 启动, False 停止
:return: bool 成功与否
"""
try:
if enable:
return start_motor()
else:
return stop_motor()
except Exception as e:
if DEBUG:
print(f"[qiege_motor] 控制异常: {e}")
return False
# ------------------------ 测试 ------------------------
if __name__ == "__main__":
print(f"【qiege_motor】测试开始 (模式: {MODE})")
print("启动 → 1秒后停止")
control(True)
time.sleep(1)
control(False)
print("测试结束。")