电脑主机端USB-can模块控制OmniPicker夹爪开合

一、设备:

1.维特智能USB-CAN模块

维特USB-CAN产品手册

2.智元灵犀X1 OmniPicker

智元灵犀X1 OmniPicker产品手册

二、调试软件

智元 上位机软件:REF-CLI v1.0.3.exe

维特智能 上位机软件: CanMiniIMU.exe

开源:XCOM 串口调试助手

注:上述软件 网页与产品手册有下载地址。

三、连接方式

image-20250906102554017

注:抓手是6p(6线) 只需接一对输入VIN 与GND,多余的VIN与GND无需接线。

OmniPicker夹爪有多个版本 ,线序不一致,注意以实际线序为主

四、can报文

image-20250906103224960

  • 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最好 (智元的手册有更改方法)

image-20250906104508630

2、打开维特智能的上位软件 - 选择对应的 串口 - 波特率为115200

image-20250906104143733

3、AT指令模式 - 标准帧 -发送对应内容 - 通过can驱动夹爪

image-20250906104859592

帧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步使用)

image-20250906105433290

5、打开XCOM - 选择串口 -115200 - 停止位1 - 数据位8 - None - 打开串口

  • 以标准发送 AT+AT 等待返回OK就是 进入 AT模式指令

image-20250906105959411

  • 再以16进制发送CAN报文 - 即可完成发送can帧 - 在上一步方式 - AT命令记录看到的数据

image-20250906110503689

注:如你已经连接夹爪,夹爪已上电的话,夹爪会动,成功发送-是会有返回消息的

六、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}")

注:启动程序 。需要先给串口模块设备权限,再需要管理员权限启动程序