小吕同学真的不会敲代码 2023-11-14 10:01 采纳率: 57.1%
浏览 87
已结题

Python周立功CAN二次开发,怎样实时接收毫米雷达数据?CAN只接收不发送

搜集到的基于Python的周立功CAN二次开发代码,都是CAN1发送一组自定义的数据,然后CAN2接收,我的项目需要CAN1接收毫米雷达发出的数据 (CAN只接收,无需发送),用的是创芯科技的CANalyst-2,以前完全没接触过任何CAN和雷达相关的开发,毫无头绪,求指教

附CAN1发送CAN2接收的代码,完全不知道该怎么改成实时接收雷达发出的:

# python3.8.0 64位(python 32位要用32位的DLL)
# 样例功能:两个通道同时初始化 125K,正常模式。CAN1 发送一帧数据到 CAN2 通道并显示。

from ctypes import *

VCI_USBCAN2 = 4
STATUS_OK = 1


# VCI_INIT_CONFIG 结构体 定义了初始化CAN的配置。
# 结构体将在VCI_InitCan函数中被填充,即初始化之前,要先填好这个结构体变量。
class VCI_INIT_CONFIG(Structure):
    _fields_ = [("AccCode", c_uint),  # 验收码。SJA1000的帧过滤验收码。对经过屏蔽码过滤为“有关位”进行匹配,全部匹配成功后,此帧可以被接收。否则不接收。详见VCI_InitCAN。
                ("AccMask", c_uint),  # 屏蔽码。对接收的CAN帧ID进行过滤,对应位为0的是“有关位”,对应位为1的是“无关位”。屏蔽码推荐设置为0xFFFFFFFF,即全部接收。
                ("Reserved", c_uint),  # 保留。
                ("Filter", c_ubyte),  # 滤波方式。允许设置为1-3
                ("Timing0", c_ubyte),  # 波特率定时器 0(BTR0)。
                ("Timing1", c_ubyte),  # 波特率定时器 1(BTR1)。
                ("Mode", c_ubyte)  # 模式。=0表示正常模式(相当于正常节点),=1表示只听模式(只接收,不影响总线),=2表示自发自收模式(环回模式)。
                ]


# VCI_CAN_OBJ 结构体 是CAN帧结构体,即1个结构体表示一个帧的数据结构。
# 在发送函数VCI_Transmit和接收函数VCI_Receive中,被用来传送CAN信息帧。
class VCI_CAN_OBJ(Structure):
    _fields_ = [("ID", c_uint),  # 帧ID。32位变量,数据格式为靠右对齐。
                ("TimeStamp", c_uint),  # 设备接收到某一帧的时间标识。时间标示从CAN卡上电开始计时,计时单位为0.1ms。
                ("TimeFlag", c_ubyte),  # 是否使用时间标识,为1时TimeStamp有效,TimeFlag和TimeStamp只在此帧为接收帧时有意义。
                ("SendType", c_ubyte),
                # 发送帧类型。
                #   =0时为正常发送(发送失败会自动重发,重发超时时间为1秒,1秒内没有发出则取消);
                #   =1时为单次发送(只发送一次,发送失败不会自动重发,总线只产生一帧数据波形);
                #   其它值无效。
                # 【注意】:
                #   多节点通信时,务必将SendType强制设为0,否则将会出现发送丢帧的情况。
                #   如果只是单节点通信,比如,作为一个CAN信号发生器,定时发出信号, SendType可设为0。
                ("RemoteFlag", c_ubyte),  # 是否是远程帧。=0时为数据帧,=1时为远程帧(数据段空)。
                ("ExternFlag", c_ubyte),  # 是否是扩展帧。=0时为标准帧(11位ID),=1时为扩展帧(29位ID)。
                ("DataLen", c_ubyte),  # 数据长度 DLC (<=8),即CAN帧Data有几个字节。约束了后面Data[8]中的有效字节。
                ("Data", c_ubyte * 8),  # CAN帧的数据。由于CAN规定了最大是8个字节,所以这里预留了8个字节的空间,Data[0]、Data[1]、Data[2]是有效的。
                ("Reserved", c_ubyte * 3)  # 系统保留
                ]


CanDLLName = './ControlCAN.dll'  # 把DLL放到对应的目录下
canDLL = windll.LoadLibrary('./ControlCAN.dll')


print(CanDLLName)

# VCI_OpenDevice:此函数用以打开设备。注意一个设备只能打开一次。
# DevType: 设备类型。对应不同的产品型号 详见:适配器设备类型定义。
# DevIndex: 设备索引,比如当只有一个USB-CAN适配器时,索引号为0,这时再插入一个USB-CAN适配器那么后面插入的这个设备索引号就是1,以此类推。
# Reserved: 保留参数,通常为 0。
# 返回值: 返回值=1,表示操作成功;=0表示操作失败;=-1表示USB-CAN设备不存在或USB掉线。

ret = canDLL.VCI_OpenDevice(VCI_USBCAN2, 0, 0)  # (设备类型,只有一个USB-CAN适配器,保留参数)
if ret == STATUS_OK:  # STATUS_OK = 1
    print('调用 VCI_OpenDevice成功\r\n')  # 操作成功
if ret != STATUS_OK:
    print('调用 VCI_OpenDevice出错\r\n')  # 操作失败/USB-CAN设备不存在或USB掉线

# 初始0通道 (can1)
vci_initconfig = VCI_INIT_CONFIG(0x80000008, 0xFFFFFFFF, 0,
                                 0, 0x03, 0x1C, 0)  # 全部接收,波特率125k,正常模式
ret = canDLL.VCI_InitCAN(VCI_USBCAN2, 0, 0, byref(vci_initconfig))
if ret == STATUS_OK:
    print('调用 VCI_InitCAN1成功\r\n')
if ret != STATUS_OK:
    print('调用 VCI_InitCAN1出错\r\n')

# VCI_StartCAN(设备类型,设备索引,CAN通道索引)
# 此函数用以启动CAN卡的某一个CAN通道。有多个CAN通道时,需要多次调用。
ret = canDLL.VCI_StartCAN(VCI_USBCAN2, 0, 0)  # can1
# CANIndex:CAN通道索引。第几路 CAN。即对应卡的CAN通道号,CAN1为0,CAN2为1。
if ret == STATUS_OK:
    print('调用 VCI_StartCAN1成功\r\n')
if ret != STATUS_OK:
    print('调用 VCI_StartCAN1出错\r\n')

# 初始1通道 (can2)
ret = canDLL.VCI_InitCAN(VCI_USBCAN2, 0, 1, byref(vci_initconfig))
if ret == STATUS_OK:
    print('调用 VCI_InitCAN2 成功\r\n')
if ret != STATUS_OK:
    print('调用 VCI_InitCAN2 出错\r\n')

ret = canDLL.VCI_StartCAN(VCI_USBCAN2, 0, 1)
if ret == STATUS_OK:
    print('调用 VCI_StartCAN2 成功\r\n')
if ret != STATUS_OK:
    print('调用 VCI_StartCAN2 出错\r\n')

# 通道1发送数据
ubyte_array = c_ubyte * 8
a = ubyte_array(1, 2, 3, 4, 5, 6, 7, 8)

ubyte_3array = c_ubyte * 3
b = ubyte_3array(0, 0, 0)

vci_can_obj = VCI_CAN_OBJ(0x1, 0, 0, 1, 0, 0, 8, a, b)  # 单次发送

# VCI_Transmit: 发送函数。返回值为实际发送成功的帧数。
# VCI_Transmit(设备类型, 设备索引, CAN通道索引, 要发送的帧结构体 VCI_CAN_OBJ数组的首指针,
# 要发送的帧结构体数组的长度(发送的帧数量)。最大为1000,建议设为1,每次发送单帧,以提高发送效率。);

ret = canDLL.VCI_Transmit(VCI_USBCAN2, 0, 0, byref(vci_can_obj), 1)  # 每次发送单帧
if ret == STATUS_OK:      # 返回实际发送的帧数,=-1表示USB-CAN设备不存在或USB掉线。
    print('CAN1通道发送成功\r\n')
if ret != STATUS_OK:
    print('CAN1通道发送失败\r\n')

# 通道2接收数据

# 结构体数组类
import ctypes


class VCI_CAN_OBJ_ARRAY(Structure):
    _fields_ = [('SIZE', ctypes.c_uint16), ('STRUCT_ARRAY', ctypes.POINTER(VCI_CAN_OBJ))]  # c_uint16: 用于创建16位无符号整数类型。

    def __init__(self, num_of_structs):
        # 这个括号不能少
        self.STRUCT_ARRAY = ctypes.cast((VCI_CAN_OBJ * num_of_structs)(), ctypes.POINTER(VCI_CAN_OBJ))  # 结构体数组
        self.SIZE = num_of_structs  # 结构体长度
        self.ADDR = self.STRUCT_ARRAY[0]  # 结构体数组地址  byref()转c地址


rx_vci_can_obj = VCI_CAN_OBJ_ARRAY(2500)  # 结构体数组

# print(ret)

# 接收函数。此函数从指定的设备CAN通道的接收缓冲区中读取数据。
# DWORD __stdcall VCI_Receive(DWORD DevType, DWORD DevIndex, DWORD CANIndex, PVCI_CAN_OBJ pReceive, ULONG Len, INT WaitTime);

# DevType
# 设备类型。对应不同的产品型号 详见:适配器设备类型定义。
#
# DevIndex
# 设备索引,比如当只有一个USB-CAN适配器时,索引号为0,这时再插入一个USB-CAN适
# 配器那么后面插入的这个设备索引号就是1,以此类推。
#
# CANIndex
# CAN通道索引。第几路 CAN。即对应卡的CAN通道号,CAN1为0,CAN2为1。
#
# pReceive
# 用来接收的帧结构体VCI_CAN_OBJ数组的首指针。注意:数组的实际大小不能小于下面
# 的len参数,否则会出现内存读写错误。
#
# Len
# 用来接收的帧结构体数组的长度(本次接收的最大帧数,实际返回值小于等于这个值)。该值为所提供的存储空间大小,适配器中为每个通道设置了2500帧左右的接收缓存区,用户
# 根据自身系统和工作环境需求,在1到2000之间选取适当的接收数组长度。一般pReceive数组大小与Len都设置为2500宜,可一次性把缓存里的数据读完。VCI_Receive采用循环方式调用,可以以最快的速度读出缓存里的数据。
#
# WaitTime
# 保留参数。
#
# 返回值: :
# 返回实际读取的帧数,=-1表示USB-CAN设备不存在或USB掉线。

while 1:  # 一直循环查询接收。
    ret = canDLL.VCI_Receive(VCI_USBCAN2, 0, 0, byref(rx_vci_can_obj.ADDR), 2500, 0)
    # 一般pReceive数组大小与Len都设置为2500宜,可一次性把缓存里的数据读完。VCI_Receive采用循环方式调用,可以以最快的速度读出缓存里的数据。
    if ret > 0:  # 接收到数据
        for i in range(0, ret):
            print('CAN1通道接收成功', end=" ")
            print('ID:', end="")
            print(hex(rx_vci_can_obj.STRUCT_ARRAY[i].ID), end=" ")
            print('DataLen:', end="")
            print(hex(rx_vci_can_obj.STRUCT_ARRAY[i].DataLen), end=" ")
            print('Data:', end="")
            print(list(rx_vci_can_obj.STRUCT_ARRAY[i].Data), end=" ")
            print('\r')

        # 接收到数据
        print('CAN1通道接收成功', end=" ")

    ret = canDLL.VCI_Receive(VCI_USBCAN2, 0, 1, byref(rx_vci_can_obj.ADDR), 2500, 0)
    if ret > 0:  # 接收到数据
        for i in range(0, ret):
            print('CAN2通道接收成功', end=" ")
            print('ID:', end="")
            print(hex(rx_vci_can_obj.STRUCT_ARRAY[i].ID), end=" ")
            print('DataLen:', end="")
            print(hex(rx_vci_can_obj.STRUCT_ARRAY[i].DataLen), end=" ")
            print('Data:', end="")
            print(list(rx_vci_can_obj.STRUCT_ARRAY[i].Data), end=" ")
            print('\r')

# 关闭
canDLL.VCI_CloseDevice(VCI_USBCAN2, 0, 0)

  • 写回答

17条回答 默认 最新

  • 专家-赤兔[在线] 全栈领域优质创作者 2023-11-14 10:01
    关注
    获得3.75元问题酬金

    引自强大而免费的微信小程序:皆我百晓生

    要实时接收毫米雷达数据,首先需要知道毫米雷达使用的CAN协议的帧格式。根据雷达的数据格式,可以修改接收函数中的打印代码来解析并处理接收到的数据。

    以下是修改后的代码示例,假设毫米雷达数据的帧ID为0x123,并且数据长度为8个字节:

    while 1:  # 一直循环查询接收。
        ret = canDLL.VCI_Receive(VCI_USBCAN2, 0, 0, byref(rx_vci_can_obj.ADDR), 2500, 0)
        if ret > 0:  # 接收到数据
            for i in range(0, ret):
                if rx_vci_can_obj.STRUCT_ARRAY[i].ID == 0x123 and rx_vci_can_obj.STRUCT_ARRAY[i].DataLen == 8:
                    # 解析并处理接收到的雷达数据
                    radar_data = list(rx_vci_can_obj.STRUCT_ARRAY[i].Data)
                    # 处理雷达数据的代码
                    
        ret = canDLL.VCI_Receive(VCI_USBCAN2, 0, 1, byref(rx_vci_can_obj.ADDR), 2500, 0)
        if ret > 0:  # 接收到数据
            for i in range(0, ret):
                if rx_vci_can_obj.STRUCT_ARRAY[i].ID == 0x123 and rx_vci_can_obj.STRUCT_ARRAY[i].DataLen == 8:
                    # 解析并处理接收到的雷达数据
                    radar_data = list(rx_vci_can_obj.STRUCT_ARRAY[i].Data)
                    # 处理雷达数据的代码
    

    在上述代码中,通过判断接收到的帧ID和数据长度,可以确定接收到的是否是雷达数据。然后可以将接收到的数据进行处理,例如打印、存储、分析等操作。修改代码中的"处理雷达数据的代码"部分来具体实现你的需求。

    请注意,以上代码仅适用于帧ID为0x123,数据长度为8个字节的情况。如果毫米雷达的数据格式不同,请根据实际情况修改代码。此外,还要确保CANalyst-2的硬件和驱动正

    评论 编辑记录

报告相同问题?

问题事件

  • 系统已结题 11月22日
  • 创建了问题 11月14日

悬赏问题

  • ¥15 有关类的报错,在模块里调用另一个模块的方法下的变量时出错
  • ¥15 delphi7 HMAC_SHA256方式加密
  • ¥15 关于#qt#的问题:我想实现qcustomplot完成坐标轴
  • ¥15 下列c语言代码为何输出了多余的空格
  • ¥15 kali linux用wget archive.kali.org/archive-key.asc指令下载签名无效(失败)
  • ¥15 openHarmony 利用c++程序在dayu210开发板上实现拉取RTSP视频流并且在屏幕上显示
  • ¥15 GD32H757的can通信配置
  • ¥20 nist随机数测试的问题
  • ¥20 帮我解决这个项目,thank you各位程序员
  • ¥15 哪位能用ea软件做一下,有偿