电脑主机端USB-can模块控制OmniPicker夹爪开合
一、设备:
1.维特智能USB-CAN模块
2.智元灵犀X1 OmniPicker
二、调试软件
智元 上位机软件:REF-CLI v1.0.3.exe
维特智能 上位机软件: CanMiniIMU.exe
开源:XCOM 串口调试助手
注:上述软件 网页与产品手册有下载地址。
三、连接方式
注:抓手是6p(6线) 只需接一对输入VIN 与GND,多余的VIN与GND无需接线。
OmniPicker夹爪有多个版本 ,线序不一致,注意以实际线序为主
四、can报文
- PC端直接夹爪USB口
- 只能用官方的调试软件去使用
- 无法二次开发
- 目前只能win
- PC端通过USB-CAN模块连接夹爪6P接口
- 通过模块的发送CAN标准帧驱动夹爪
- 把模块当串口使用–模块提供AT接口指令
- 可以不用系统供其使用
五:调试方法
1、打开REF-CLI v1.0.3.exe 查看属性 -
- 确保fw_version = 330 (int) 或者要大于330
- can_node_id = 8 (int) 要知道数字 或者改为8最好 (智元的手册有更改方法)
2、打开维特智能的上位软件 - 选择对应的 串口 - 波特率为115200
3、AT指令模式 - 标准帧 -发送对应内容 - 通过can驱动夹爪
帧ID | 数据 | 常用 |
---|---|---|
8 | 00 FF FF FF FF FF 00 00 | 全开 |
8 | 00 7F FF FF FF FF 00 00 | 半开 |
8 | 00 00 FF FF FF FF 00 00 | 关闭 |
注:数据详细要看智元的产品手册
4、查看AT命令(留给第5步使用)
5、打开XCOM - 选择串口 -115200 - 停止位1 - 数据位8 - None - 打开串口
- 以标准发送 AT+AT 等待返回OK就是 进入 AT模式指令
- 再以16进制发送CAN报文 - 即可完成发送can帧 - 在上一步方式 - AT命令记录看到的数据
注:如你已经连接夹爪,夹爪已上电的话,夹爪会动,成功发送-是会有返回消息的
六、linux的驱动代码
在ubuntu上使用这个模块时,尝试使用多种调试助手,均无成功,决定直接使用代码驱动模块,向模块发送AT指令
import serial
import time
import subprocess
import sys
import signal
import binascii
import os
def release_serial_port(port):
"""尝试释放被占用的串口"""
try:
print(f"尝试释放被占用的串口: {port}")
# 使用 fuser 终止占用进程
result = subprocess.run(['sudo', 'fuser', '-k', port], capture_output=True, text=True)
if result.returncode == 0:
print("成功终止占用进程")
time.sleep(1) # 等待进程终止
return True
# 如果 fuser 不可用,使用 pkill
result = subprocess.run(['sudo', 'pkill', '-f', port], capture_output=True, text=True)
if result.returncode == 0:
print("成功终止占用进程")
time.sleep(1)
return True
print("无法终止占用进程,尝试直接访问")
return False
except Exception as e:
print(f"释放串口失败: {e}")
return False
def send_at_command(port, command, timeout=1.0, max_retries=3):
"""发送 AT 命令并返回响应(带重试机制)"""
for attempt in range(max_retries):
try:
# 创建串口连接
with serial.Serial(
port=port,
baudrate=115200,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=timeout
) as ser:
# 清空缓冲区
ser.reset_input_buffer()
ser.reset_output_buffer()
# 发送命令
full_command = command + '\r\n'
ser.write(full_command.encode('ascii'))
print(f"尝试 #{attempt+1}: 已发送 {command}")
# 等待响应
time.sleep(0.1)
# 读取返回数据
response = ser.read_all().decode('ascii', errors='ignore')
if response:
return response
else:
print(f"尝试 #{attempt+1}: 无响应")
except serial.SerialException as e:
if "Device or resource busy" in str(e):
print(f"尝试 #{attempt+1}: 设备忙,尝试释放")
release_serial_port(port)
else:
print(f"尝试 #{attempt+1}: 串口错误 - {e}")
except Exception as e:
print(f"尝试 #{attempt+1}: 未知错误 - {e}")
# 重试前等待
time.sleep(1)
return "所有尝试失败,无响应"
def send_hex_data(port, hex_data, timeout=1.0, max_retries=3):
"""发送原始十六进制数据并返回响应"""
# 将十六进制字符串转换为字节数组
try:
# 移除空格
hex_data = hex_data.replace(" ", "")
# 转换为字节数组
data_bytes = bytes.fromhex(hex_data)
except ValueError as e:
return f"无效的十六进制数据: {e}"
for attempt in range(max_retries):
try:
# 创建串口连接
with serial.Serial(
port=port,
baudrate=115200,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=timeout
) as ser:
# 清空缓冲区
ser.reset_input_buffer()
ser.reset_output_buffer()
# 发送原始字节数据
ser.write(data_bytes)
print(f"尝试 #{attempt+1}: 已发送原始数据: {hex_data}")
# 等待响应
time.sleep(0.1)
# 读取返回数据
response = ser.read_all()
if response:
# 同时显示十六进制和ASCII格式
hex_response = response.hex().upper()
ascii_response = ''.join([chr(b) if 32 <= b <= 126 else '.' for b in response])
return f"十六进制响应: {hex_response}\nASCII响应: {ascii_response}"
else:
print(f"尝试 #{attempt+1}: 无响应")
except serial.SerialException as e:
if "Device or resource busy" in str(e):
print(f"尝试 #{attempt+1}: 设备忙,尝试释放")
release_serial_port(port)
else:
print(f"尝试 #{attempt+1}: 串口错误 - {e}")
except Exception as e:
print(f"尝试 #{attempt+1}: 未知错误 - {e}")
# 重试前等待
time.sleep(1)
return "所有尝试失败,无响应"
def main():
# 默认设备
port = "/dev/ttyUSB0"
print(f"开始与 {port} 通信 (115200 bps)")
# 发送 AT+AT 命令
print("\n=== 发送 AT+AT 命令 ===")
response = send_at_command(port, "AT+AT", timeout=1.5)
print("\n响应:")
print(response)
# 发送原始十六进制数据
hex_data = "41 54 01 00 00 00 08 00 00 FF FF FF FF 00 00 0D 0A"
print(f"\n=== 发送原始十六进制数据: {hex_data} ===")
response = send_hex_data(port, hex_data)
print(f"\n响应:\n{response}")
time.sleep(1)
hex_data = "41 54 01 00 00 00 08 00 7F FF FF FF FF 00 00 0D 0A"
print(f"\n=== 发送原始十六进制数据: {hex_data} ===")
response = send_hex_data(port, hex_data)
print(f"\n响应:\n{response}")
time.sleep(1)
hex_data = "41 54 01 00 00 00 08 00 FF FF FF FF FF 00 00 0D 0A"
print(f"\n=== 发送原始十六进制数据: {hex_data} ===")
response = send_hex_data(port, hex_data)
print(f"\n响应:\n{response}")
time.sleep(1)
def signal_handler(sig, frame):
print("\n程序已终止")
sys.exit(0)
if __name__ == "__main__":
# 设置Ctrl+C信号处理 - 修复了Signal错误
signal.signal(signal.SIGINT, signal_handler)
try:
# 需要sudo权限来终止占用进程
if os.geteuid() != 0:
print("请使用sudo运行此程序")
sys.exit(1)
main()
except Exception as e:
print(f"程序异常终止: {e}")import serial
import time
import subprocess
import sys
import signal
import binascii
import os
def release_serial_port(port):
"""尝试释放被占用的串口"""
try:
print(f"尝试释放被占用的串口: {port}")
# 使用 fuser 终止占用进程
result = subprocess.run(['sudo', 'fuser', '-k', port], capture_output=True, text=True)
if result.returncode == 0:
print("成功终止占用进程")
time.sleep(1) # 等待进程终止
return True
# 如果 fuser 不可用,使用 pkill
result = subprocess.run(['sudo', 'pkill', '-f', port], capture_output=True, text=True)
if result.returncode == 0:
print("成功终止占用进程")
time.sleep(1)
return True
print("无法终止占用进程,尝试直接访问")
return False
except Exception as e:
print(f"释放串口失败: {e}")
return False
def send_at_command(port, command, timeout=1.0, max_retries=3):
"""发送 AT 命令并返回响应(带重试机制)"""
for attempt in range(max_retries):
try:
# 创建串口连接
with serial.Serial(
port=port,
baudrate=115200,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=timeout
) as ser:
# 清空缓冲区
ser.reset_input_buffer()
ser.reset_output_buffer()
# 发送命令
full_command = command + '\r\n'
ser.write(full_command.encode('ascii'))
print(f"尝试 #{attempt+1}: 已发送 {command}")
# 等待响应
time.sleep(0.1)
# 读取返回数据
response = ser.read_all().decode('ascii', errors='ignore')
if response:
return response
else:
print(f"尝试 #{attempt+1}: 无响应")
except serial.SerialException as e:
if "Device or resource busy" in str(e):
print(f"尝试 #{attempt+1}: 设备忙,尝试释放")
release_serial_port(port)
else:
print(f"尝试 #{attempt+1}: 串口错误 - {e}")
except Exception as e:
print(f"尝试 #{attempt+1}: 未知错误 - {e}")
# 重试前等待
time.sleep(1)
return "所有尝试失败,无响应"
def send_hex_data(port, hex_data, timeout=1.0, max_retries=3):
"""发送原始十六进制数据并返回响应"""
# 将十六进制字符串转换为字节数组
try:
# 移除空格
hex_data = hex_data.replace(" ", "")
# 转换为字节数组
data_bytes = bytes.fromhex(hex_data)
except ValueError as e:
return f"无效的十六进制数据: {e}"
for attempt in range(max_retries):
try:
# 创建串口连接
with serial.Serial(
port=port,
baudrate=115200,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=timeout
) as ser:
# 清空缓冲区
ser.reset_input_buffer()
ser.reset_output_buffer()
# 发送原始字节数据
ser.write(data_bytes)
print(f"尝试 #{attempt+1}: 已发送原始数据: {hex_data}")
# 等待响应
time.sleep(0.1)
# 读取返回数据
response = ser.read_all()
if response:
# 同时显示十六进制和ASCII格式
hex_response = response.hex().upper()
ascii_response = ''.join([chr(b) if 32 <= b <= 126 else '.' for b in response])
return f"十六进制响应: {hex_response}\nASCII响应: {ascii_response}"
else:
print(f"尝试 #{attempt+1}: 无响应")
except serial.SerialException as e:
if "Device or resource busy" in str(e):
print(f"尝试 #{attempt+1}: 设备忙,尝试释放")
release_serial_port(port)
else:
print(f"尝试 #{attempt+1}: 串口错误 - {e}")
except Exception as e:
print(f"尝试 #{attempt+1}: 未知错误 - {e}")
# 重试前等待
time.sleep(1)
return "所有尝试失败,无响应"
def main():
# 默认设备
port = "/dev/ttyUSB0"
print(f"开始与 {port} 通信 (115200 bps)")
# 发送 AT+AT 命令
print("\n=== 发送 AT+AT 命令 ===")
response = send_at_command(port, "AT+AT", timeout=1.5)
print("\n响应:")
print(response)
# 发送原始十六进制数据
hex_data = "41 54 01 00 00 00 08 00 00 FF FF FF FF 00 00 0D 0A"
print(f"\n=== 发送原始十六进制数据: {hex_data} ===")
response = send_hex_data(port, hex_data)
print(f"\n响应:\n{response}")
time.sleep(1)
hex_data = "41 54 01 00 00 00 08 00 7F FF FF FF FF 00 00 0D 0A"
print(f"\n=== 发送原始十六进制数据: {hex_data} ===")
response = send_hex_data(port, hex_data)
print(f"\n响应:\n{response}")
time.sleep(1)
hex_data = "41 54 01 00 00 00 08 00 FF FF FF FF FF 00 00 0D 0A"
print(f"\n=== 发送原始十六进制数据: {hex_data} ===")
response = send_hex_data(port, hex_data)
print(f"\n响应:\n{response}")
time.sleep(1)
def signal_handler(sig, frame):
print("\n程序已终止")
sys.exit(0)
if __name__ == "__main__":
# 设置Ctrl+C信号处理 - 修复了Signal错误
signal.signal(signal.SIGINT, signal_handler)
try:
# 需要sudo权限来终止占用进程
if os.geteuid() != 0:
print("请使用sudo运行此程序")
sys.exit(1)
main()
except Exception as e:
print(f"程序异常终止: {e}")
注:启动程序 。需要先给串口模块设备权限,再需要管理员权限启动程序