一、材料准备
幻尔总线舵机(我用的是xArm机械臂)
幻尔TTL/USB调试板(一定要是幻尔的,其他牌子的调试板驱动不了舵机)
二、Python SDK
"""
串口舵机Python SDK
----------------------------------------------
"""
import serial
import time
import ctypes
###################################################################################################
# 幻尔科技总线舵机通信协议
###################################################################################################
ROBOT_SERVO_FRAME_HEADER = 0x55
ROBOT_SERVO_MOVE_TIME_WRITE = 1 # 该命令发送给舵机,舵机将在参数时间内从当前角度匀速转动到参数角度
ROBOT_SERVO_MOVE_TIME_READ = 2 # 读取发送给舵机的角度运动指令
ROBOT_SERVO_MOVE_TIME_WAIT_WRITE = 7 # 舵机转动指令,等待一段时间后转动
ROBOT_SERVO_MOVE_TIME_WAIT_READ = 8 # 读取上面的指令
ROBOT_SERVO_MOVE_START = 11 # 舵机转动指令,舵机立即开始转动
ROBOT_SERVO_MOVE_STOP = 12 # 舵机停止指令
ROBOT_SERVO_ID_WRITE = 13 # 重新写入舵机ID
ROBOT_SERVO_ID_READ = 14 # 读取舵机的ID值
ROBOT_SERVO_ANGLE_OFFSET_ADJUST = 17 # 舵机转动角度偏差调整
ROBOT_SERVO_ANGLE_OFFSET_WRITE = 18 # 保存偏差值
ROBOT_SERVO_ANGLE_OFFSET_READ = 19 # 读取偏差值
ROBOT_SERVO_ANGLE_LIMIT_WRITE = 20 # 设置舵机转动的范围
ROBOT_SERVO_ANGLE_LIMIT_READ = 21 # 读取舵机转动的范围
ROBOT_SERVO_VIN_LIMIT_WRITE = 22 # 设置舵机的电压范围
ROBOT_SERVO_VIN_LIMIT_READ = 23 # 读取舵机的电压范围
ROBOT_SERVO_TEMP_MAX_LIMIT_WRITE = 24 # 设置舵机内部最高温度
ROBOT_SERVO_TEMP_MAX_LIMIT_READ = 25 # 读取舵机允许最高温度
ROBOT_SERVO_TEMP_READ = 26 # 读取舵机实时温度
ROBOT_SERVO_VIN_READ = 27 # 读取舵机实时电压
ROBOT_SERVO_POS_READ = 28 # 读取舵机当前实际角度位置值
ROBOT_SERVO_OR_MOTOR_MODE_WRITE = 29 # 控制舵机的转速
ROBOT_SERVO_OR_MOTOR_MODE_READ = 30 # 读取舵机控制模式的相关参数
ROBOT_SERVO_LOAD_OR_UNLOAD_WRITE = 31 # 设置舵机内部电机是否掉电
ROBOT_SERVO_LOAD_OR_UNLOAD_READ = 32 # 读取舵机内部电机状态
ROBOT_SERVO_LED_CTRL_WRITE = 33 # 设置LED灯状态
ROBOT_SERVO_LED_CTRL_READ = 34 # 读取LED灯状态
ROBOT_SERVO_LED_ERROR_WRITE = 35 # 设置导致LED灯闪烁报警的值
ROBOT_SERVO_LED_ERROR_READ = 36 # 读取舵机故障报警值
serialHandle = serial.Serial("COM5", 115200) # 初始化串口, 波特率为115200
time_out = 50
###################################################################################################
# 舵机与PC通信
###################################################################################################
def checksum(buf):
"""计算校验和"""
check_sum = 0x00
for b in buf:
check_sum += b
check_sum = check_sum - 0x55 - 0x55
check_sum = ~check_sum # 取反
return check_sum & 0xff
def serial_servo_write(ID=None, w_cmd=None, dat1=None, dat2=None):
"""写指令"""
buf = bytearray(b'\x55\x55') # 帧头
buf.append(ID)
# 指令长度
if dat1 is None and dat2 is None:
buf.append(3)
elif dat1 is not None and dat2 is None:
buf.append(4)
elif dat1 is not None and dat2 is not None:
buf.append(7)
buf.append(w_cmd) # 指令
# 写数据
if dat1 is None and dat2 is None:
pass
elif dat1 is not None and dat2 is None:
buf.append(dat1 & 0xff)
elif dat1 is not None and dat2 is not None:
buf.extend([(0xff & dat1), (0xff & (dat1 >> 8))]) # 分低8位 高8位 放入缓存
buf.extend([(0xff & dat2), (0xff & (dat2 >> 8))]) # 分低8位 高8位 放入缓存
# 校验和
buf.append(checksum(buf))
serialHandle.write(buf) # 发送
def serial_servo_read(ID=None, r_cmd=None):
"""发送读取命令"""
buf = bytearray(b'\x55\x55') # 帧头
buf.append(ID)
buf.append(3) # 指令长度
buf.append(r_cmd) # 指令
buf.append(checksum(buf)) # 校验和
serialHandle.write(buf) # 发送
time.sleep(0.00034)
def serial_servo_get_r_msg(cmd):
"""获取指定读取数据命令的数据"""
serialHandle.flushInput() # 清空接收缓存
time.sleep(0.005) # 稍作延迟,等待接收完毕
count = serialHandle.inWaiting() # 获取接收缓存中的字节数
if count != 0:
recv_data = serialHandle.read(count) # 读取接收到的数据
try:
if recv_data[0] == 0x55 and recv_data[1] == 0x55 and recv_data[4] == cmd:
dat_len = recv_data[3]
serialHandle.flushInput() # 清空接收缓存
if dat_len == 4:
return recv_data[5]
elif dat_len == 5:
pos = 0xffff & (recv_data[5] | (0xff00 & (recv_data[6] << 8)))
return ctypes.c_int16(pos).value
elif dat_len == 7:
pos1 = 0xffff & (recv_data[5] | (0xff00 & (recv_data[6] << 8)))
pos2 = 0xffff & (recv_data[7] | (0xff00 & (recv_data[8] << 8)))
return ctypes.c_int16(pos1).value, ctypes.c_int16(pos2).value
else:
return None
except BaseException as e:
print(e)
else:
serialHandle.flushInput() # 清空接收缓存
return None
###################################################################################################
# 舵机使用API
###################################################################################################
def setBusServoID(old_id, new_id):
"""配置舵机ID号,出厂默认为1"""
serial_servo_write(old_id, ROBOT_SERVO_ID_WRITE, new_id)
def getBusServoID(ID=None):
"""读取串口舵机ID"""
while True:
if ID is None: # 总线上只能有一个舵机
serial_servo_read(0xfe, ROBOT_SERVO_ID_READ)
else:
serial_servo_read(ID, ROBOT_SERVO_ID_READ)
# 获取内容
msg = serial_servo_get_r_msg(ROBOT_SERVO_ID_READ)
if msg is not None:
return msg
def setBusServoPulse(ID, pulse, use_time):
"""驱动串口舵机运动到指定位置"""
pulse = 0 if pulse < 0 else pulse
pulse = 1000 if pulse > 1000 else pulse
use_time = 0 if use_time < 0 else use_time
use_time = 30000 if use_time > 30000 else use_time
serial_servo_write(ID, ROBOT_SERVO_MOVE_TIME_WRITE, pulse, use_time)
def getBusServoPulse(ID):
"""
读取舵机当前位置
"""
while True:
serial_servo_read(ID, ROBOT_SERVO_POS_READ)
msg = serial_servo_get_r_msg(ROBOT_SERVO_POS_READ)
if msg is not None:
return msg
def stopBusServo(ID=None):
"""停止舵机运行"""
serial_servo_write(ID, ROBOT_SERVO_MOVE_STOP)
def setBusServoDeviation(ID, d=0):
"""调整偏差"""
serial_servo_write(ID, ROBOT_SERVO_ANGLE_OFFSET_ADJUST, d)
def saveBusServoDeviation(ID):
"""配置偏差,掉电保护"""
serial_servo_write(ID, ROBOT_SERVO_ANGLE_OFFSET_WRITE)
def getBusServoDeviation(ID):
"""读取偏差值"""
# 发送读取偏差指令
count = 0
while True:
serial_servo_read(ID, ROBOT_SERVO_ANGLE_OFFSET_READ)
# 获取
msg = serial_servo_get_r_msg(ROBOT_SERVO_ANGLE_OFFSET_READ)
count += 1
if msg is not None:
return msg
if count > time_out:
return None
def setBusServoAngleLimit(ID, low, high):
"""设置舵机转动范围"""
serial_servo_write(ID, ROBOT_SERVO_ANGLE_LIMIT_WRITE, low, high)
def getBusServoAngleLimit(ID):
"""读取舵机转动范围"""
while True:
serial_servo_read(ID, ROBOT_SERVO_ANGLE_LIMIT_READ)
msg = serial_servo_get_r_msg(ROBOT_SERVO_ANGLE_LIMIT_READ)
if msg is not None:
return msg
def setBusServoVinLimit(ID, low, high):
"""设置舵机电压范围"""
serial_servo_write(ID, ROBOT_SERVO_VIN_LIMIT_WRITE, low, high)
def getBusServoVinLimit(ID):
"""读取舵机转动范围"""
while True:
serial_servo_read(ID, ROBOT_SERVO_VIN_LIMIT_READ)
msg = serial_servo_get_r_msg(ROBOT_SERVO_VIN_LIMIT_READ)
if msg is not None:
return msg
def setBusServoMaxTemp(ID, m_temp):
"""设置舵机最高温度报警"""
serial_servo_write(ID, ROBOT_SERVO_TEMP_MAX_LIMIT_WRITE, m_temp)
def getBusServoTempLimit(ID):
"""读取舵机温度报警范围"""
while True:
serial_servo_read(ID, ROBOT_SERVO_TEMP_MAX_LIMIT_READ)
msg = serial_servo_get_r_msg(ROBOT_SERVO_TEMP_MAX_LIMIT_READ)
if msg is not None:
return msg
def getBusServoTemp(ID):
"""读取舵机温度"""
while True:
serial_servo_read(ID, ROBOT_SERVO_TEMP_READ)
msg = serial_servo_get_r_msg(ROBOT_SERVO_TEMP_READ)
if msg is not None:
return msg
def getBusServoVin(ID):
"""读取舵机电压"""
while True:
serial_servo_read(ID, ROBOT_SERVO_VIN_READ)
msg = serial_servo_get_r_msg(ROBOT_SERVO_VIN_READ)
if msg is not None:
return msg
def restBusServoPulse(old_id):
# 舵机清零偏差和P值中位(500)
setBusServoDeviation(old_id, 0) # 清零偏差
time.sleep(0.1)
serial_servo_write(old_id, ROBOT_SERVO_MOVE_TIME_WRITE, 500, 100) # 中位
def unloadBusServo(ID):
serial_servo_write(ID, ROBOT_SERVO_LOAD_OR_UNLOAD_WRITE, 0)
def getBusServoLoadStatus(ID):
"""读取是否掉电"""
while True:
serial_servo_read(ID, ROBOT_SERVO_LOAD_OR_UNLOAD_READ)
msg = serial_servo_get_r_msg(ROBOT_SERVO_LOAD_OR_UNLOAD_READ)
if msg is not None:
return msg
三、调用说明
# 导入API
from servo import *
# 驱动舵机运动
setBusServoPulse(1, 500, 500)