python-can的学习笔记

本文介绍了如何安装Python CAN库,以及通过Kvaser软件、配置文件和环境变量进行CAN总线操作。重点讲解了BUS对象创建、消息发送与接收、配置文件使用和常用API应用,适合初学者快速上手CAN通信。
摘要由CSDN通过智能技术生成

实在不想那种中规中矩的写东西了,写的自己都看不下去了,想到啥就写啥吧!

聊两句

最近没事就看了一CAN总线相关的东西,平时工作上接触的不多,大部分时间都是在移动端的自动化上,服务端的自动化上面,针对这块了解的非常非常的少。不看就会焦虑。

因为平时工作用的不多,也就是看一下混个眼熟吧,记录下来一些自己觉得重要的信息,以便后面使用。

安装

一行命令吧

pip install python-can

没有CAN硬件咋办

我也是看到知乎上的一个帖子才知道Kvaser这个软件。链接地址

上个示例代码吧,看看是什么样子

  • 代码①
# coding:utf-8
"""
This example shows how sending a single message works
"""
import can

def send_one():
    """ Sends a single message."""

    # Using specific buses works similar:
    # bus = can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000)
    # bus = can.interface.Bus(bustype='pcan', channel='PCAN_USBBUS1', bitrate=250000)
    # bus = can.interface.Bus(bustype='ixxat', channel=0, bitrate=250000)

    bus = can.interface.Bus(bustype='kvaser', channel=0, bitrate=250000)
    msg = can.Message(arbitration_id=0xc0ffee, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
    try:
        bus.send(msg)
        print("Message sent on {}".format(bus.channel_info))
    except can.CanError:
        print("Message NOT sent")


if __name__ == '__main__':
    send_one()
  • 代码②
# coding:utf-8
"""
通过代码设置实现
"""
import can
can.rc['interface'] = 'kvaser'
can.rc['channel'] = '0'
can.rc['bitrate'] = 250000

from can.interface import Bus

bus = Bus()
# 或者可以通过以下方式
# bus = Bus(bustype='kvaser', channel=0, bitrate=250000)
msg = can.Message(arbitration_id=0xc0ffee, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
try:
    bus.send(msg)
    print("Message sent on {}".format(bus.channel_info))
except can.CanError:
    print("Message NOT sent")

上面两种示例代码主要是体现了两种bus的创建方式,针对CAN的配置还有更多的方式,比如配置文件,环境变量,还有刚才的代码中配置。下面看看有哪些配置方式。

配置方式

配置文件

在linux系统中,配置文件主要在以下路径中搜索:

  1. ~/can.conf
  2. /etc/can.conf
  3. $HOME/.can
  4. $HOME/.canrc

在Windows系统中的配置文件路径:

  1. %USERPROFILE%/can.conf
  2. can.ini (当前工作目录)
  3. %APPDATA%/can.ini

can.ini文件长啥样呢?

[default]
interface = kvaser
channel = 0
bitrate = 250000

除了设置default项,还可以设置别的项,比如可以配置更多的CAN硬件信息,配置文件样式如下:(没有的配置项可以共享default的配置)

[default]

interface = <the name of the interface to use>

channel = <the channel to use by default>

bitrate = <the bitrate in bits/s to use by default>

[HS]

*# All the values from the 'default' section are inherited*

channel = <the channel to use>

bitrate = <the bitrate in bits/s to use. i.e. 500000>

[MS]

*# All the values from the 'default' section are inherited*

channel = <the channel to use>

bitrate = <the bitrate in bits/s to use. i.e. 125000>

那怎么使用呢,在创建BUS对象的时候传入context参数就可以了

from can.interface import Bus

hs_bus = Bus(context='HS')
ms_bus = Bus(context='MS')

有了配置文件,就在创建BUS对象的时候不需要传递参数了

# coding: utf-8
"""
通过with语句执行can
"""
from can import CanError, Message
from can.interface import Bus

with Bus() as bus:
    msg = Message(arbitration_id=0xc0ffee, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
    try:
        bus.send(msg)
    except CanError as e:
        print(e)

环境变量配置

也可以通过配置环境变量来配置can信息,主要有以下配置项:

  • CAN_INTERFACE
  • CAN_CHANNEL
  • CAN_BITRATE
  • CAN_CONFIG

CAN_CONFIG 环境变量可以通过json格式来设置任意的bug配置信息.

CAN_CONFIG = {"interface":"kvaser","channel":0,"bitrate":250000}

支持的CAN名称

名称描述信息
“socketcan”SocketCAN
“kvaser”Kvaser’s CANLIB
“serial”CAN over Serial
“slcan”CAN over Serial / SLCAN
“ixxat”IXXAT Virtual CAN Interface
“pcan”PCAN Basic API
“usb2can”USB2CAN Interface
“nican”NI-CAN
“iscan”isCAN
“neovi”neoVI
“vector”Vector
“virtual”Virtual
“canalystii”CANalyst-II
“systec”SYSTEC interface

基本上python-can的基本信息也就这么多,写法也比较简单,重要的就是怎么发消息,收消息之类的,下面就看一下常见的API信息。

常用API

直接上pycharm的代码吧,就当是笔记了,注释部分也是重点哦

BUS对象创建

有两种创建方式

  1. 可以通过with语句

此处展示不传递参数,通过读取配置文件或者环境变量

with Bus() as bus: # 具体参数可以通过环境变量或者配置文件进行配置
    pass

配置文件如下:

[default]
interface = kvaser
channel = 0
bitrate = 250000
  1. 通过普通方式创建

顺便展示一下通过传递参数方式

bus = Bus(bustype='kvaser', channel=0, bitrate=250000)

设置过滤器

  • set_filters(filters=None)

    设置过滤器可以接收指定的消息,如果参数是空或者长度为0 会接收所有消息
    参数格式: [{“can_id”:0x11, “can_mask”:0x21, “extended”: False}]
    可以在创建BUS对象的时候设置,也可以通过BUS对象调用此方法进行设置

filters = [{"can_id":0x123, "can_mask":0x21, "extended": False}]
bus.set_filters(filters)  
# 或者
bus = Bus(can_filters=filters)

CAN报文封装

通过调用Message类实现CAN报文的创建

  • Message(timestamp=0.0, arbitration_id=0, is_extended_id=True, is_remote_frame=False,is_error_frame=False, channel=None, dlc=None, data=None, is_fd=False, is_rx=True,bitrate_switch=False, error_state_indicator=False, check=False)

参数: 基本上数据帧结构中的字段都包含在内,具体可以根据实际情况设置
描述:

  • Message对象可用于CAN信号的发送,接收,和不同格式之间的转换
  • 同时也支持copy()和deepcopy()方法

消息的发送

两种方法send() 和send_periodic()

  • send(msg, timeout=None)
with Bus() as bus:
    msg = Message(arbitration_id=0xc0ffee, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
    bus.send(msg)
  • send_periodic(msgs, period, duration=None, store_taks=True)

在特定的时间内每个特定时间发送消息,返回CyclicSendTaskABC类型的任务

  • 参数解释

    • msgs: CAN消息,数组的话 要保证id都是一样
    • period: 每条消息间隔时间(秒)
    • duration:消息发送的总时长,如果不设置,会一直发送
    • store_task: 默认设置为true,会附加到bus实例中管理,如果设置false,需要手动管理控制
    • 何时设置store_task为False呢?
      • 如果一个can任务是一个临时性的任务,发送后就结束了,就最好设置为False, 因为如果默认设置的话,这个任务就会存在bus实例中,还会占用一定的内存空间

什么场景下发送会停止呢?
在以下几种情况下,此任务会停止执行

  • duration字段设置的时间过期
  • Bus实例离开了作用域,也就是无效
  • Bus实例停止
  • BusABC.stop_all_periodic_tasks()被调用 bus对象方法
  • CyclicTask.stop()方法被调用,任务对象方法

基于上面提到的几种方法,顺便也说一下吧

  • shutdown()
    • 停止bus 在with语句中不需要使用此方法
  • stop_alll_perriodic_tasks(remove_tasks=True)
    • 停止发送任何消息,和send_periodic方法对应,如果在停止的时候任务出现异常,返回结果为None
    • 参数: remove_tasks: 停止跟进的任务

使用任务管理方法

with Bus() as bus:  # ①

    msg1 = Message(arbitration_id=0xc0ffaa, data=[3, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
    msg2 = Message(arbitration_id=0xc0ffaa, data=[4, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
    msg3 = Message(arbitration_id=0xc0ffaa, data=[5, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
    print("msgs是个消息数组")
    t1 = bus.send_periodic(msgs=[msg1, msg2, msg3], period=2, duration=30)
    print("msgs是单个消息")
    t2 = bus.send_periodic(msgs=msg, period=2, duration=30)
    print("在第十秒的时候停止")
    for i in range(10):
        print("第{}秒.............".format(i+1))
        time.sleep(1)
    # t1.stop()  # 通过任务来管理
    # t2.stop()  # 通过任务来管理
    bus.stop_all_periodic_tasks()  # 通过调用函数任务停止
    # bus.shutdown()  # 通过bus来管理 如果使用with语句初始化bus 此方法会报错

使用bus.shutdown方法

    bus = Bus()
    # 以下 ④
    msg = Message(arbitration_id=0xc0ffee, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
    msg1 = Message(arbitration_id=0xc0ffaa, data=[3, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
    msg2 = Message(arbitration_id=0xc0ffaa, data=[4, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
    msg3 = Message(arbitration_id=0xc0ffaa, data=[5, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
    print("msgs是个消息数组")
    t1 = bus.send_periodic(msgs=[msg1, msg2, msg3], period=2, duration=30)
    print("msgs是单个消息")
    t2 = bus.send_periodic(msgs=msg, period=2, duration=30)
    print("在第十秒的时候停止")
    for i in range(10):
        print("第{}秒.............".format(i + 1))
        time.sleep(1)
    bus.shutdown()  # 通过bus来管理  停止bus ⑥ 在with语句中不需要使用shutdown函数

消息接收

消息接收有两种方法

  1. 调用recv()
  2. 直接在bus对象中迭代获取

这个没啥好说的,咱写作能力也就这水平(太差劲),上代码吧

with Bus() as bus:  # ①
    # recv(timeout=None) 在给定timeout时间内接受消息,超时未接到消息 返回None
    msg = bus.recv()
    # 或者如下方法
    for msg in bus:  # ⑩ 此类方法会一直循环下去,只要有消息就会执行打印
        print("发送的消息看一看吧:{}".format(msg))

监听 Listener

用于注册接收can消息的通知,可以通过两种方式使用

  1. 默认方式,新的can消息作为参数调用listener
  2. 通过调用on_message_received()

监听类必须继承Listener类并重写on_message_received方法
停止监听,调用stop()方法

class MyListener(Listener):

    def on_message_received(self, msg: Message) -> None:
        print("监听器收到消息咯----{}".format(msg))

if __name__ == '__main__':
    listener = MyListener() # ①② 定义监听器 必须要继承抽象类
    with Bus() as bus:  # ①
        msg = bus.recv()

        # listener(msg)  # ①② 结合MyListener一起来看  第一种方法
        listener.on_message_received(msg)  # ①② 结合MyListener一起来看  第二种方法
        listener.stop()  # 停止监听

以下选择性无视了

以上的代码都在一个文件里调试了,全都放上来吧(选择性无视)

# coding:utf-8
"""
针对python-can的api的学习示例
"""
import time

import can
from can import CanError, Message, Listener
from can.interface import Bus



class MyListener(Listener):

    def on_message_received(self, msg: Message) -> None:
        print("监听器收到消息咯----{}".format(msg))

# ①. bus对象创建
# 可以通过with语句
# with Bus() as bus: # 具体参数可以通过环境变量或者配置文件进行配置
#    pass
# 配置文件如下:
"""
[default]
interface = kvaser
channel = 0
bitrate = 250000
"""
# 可以通过传递参数的方式
# bus = Bus(bustype='kvaser', channel=0, bitrate=250000)

# ②. recv(timeout=None)
# 在给定timeout时间内接受消息,超时未接到消息 返回None

# ③ send(msg, timeout=None)
# 发送CAN消息
# ④ send_periodic(msgs, period, duration=None, store_taks=True)
# 在特定的时间内每个特定时间发送消息,返回CyclicSendTaskABC类型的任务
    # 参数解释
        # msgs: CAN消息,数组的话 要保证id都是一样
        # period: 每条消息间隔时间(秒)
        # duration:消息发送的总时长,如果不设置,会一直发送
        # store_task: 默认设置为true,会附加到bus实例中管理,如果设置false,需要手动管理控制
            # - 何时设置store_task为False呢,如果一个can任务是一个临时性的任务,发送后就结束了,就最好设置为False,
            # 因为如果默认设置的话,这个任务就会存在bus实例中,还会占用一定的内存空间

    # 在以下几种情况下,此任务会停止执行
    #   - duration字段设置的时间过期
    #   - Bus实例离开了作用域,也就是无效
    #   - Bus实例停止
    #   - BusABC.stop_all_periodic_tasks()被调用 bus对象方法
    #   - CyclicTask.stop()方法被调用,任务对象方法
# ⑤ set_filters(filters=None)
    # 设置过滤器可以接收指定的消息,如果参数是空或者长度为0 会接收所有消息
        # 参数:
            # filters:[{"can_id":0x11, "can_mask":0x21, "extended": False}]

# ⑥ shutdown()
    #  停止bus 在with语句中不需要使用此方法

# ⑦  state
    # 属性字段 获取当前硬件状态

# ⑧ stop_alll_perriodic_tasks(remove_tasks=True)
    # 停止发送任何消息,和send_periodic方法对应,如果在停止的时候任务出现异常,返回结果为None
        # 参数:
            # remove_tasks: 停止跟进的任务

# ⑨ 发送消息
    # 可以通过send()方法传递一个Message对象,发送一条独立的can信号

# ⑩ 接收消息
    # 两种方法用来接收消息,一种是调用recv()方法,第二种是直接在bus对象中迭代获取
    # 还可以通过Listener来实现

# ①① CAN报文
  # 对象结构
    # Message(timestamp=0.0, arbitration_id=0, is_extended_id=True, is_remote_frame=False,is_error_frame=False,
    # channel=None, dlc=None, data=None, is_fd=False, is_rx=True,bitrate_switch=False,
    # error_state_indicator=False, check=False)
        # 参数:基本上数据帧结构中的字段都包含在内,具体可以根据实际情况设置
    # 描述:
        #  Message对象可用于CAN信号的发送,接收,和不同格式之间的转换
        # 同时也支持copy()和deepcopy()方法

# ①② 监听 Listener
    # 用于注册接收can消息的通知,可以通过两种方式使用
        # 1. 默认方式,新的can消息作为参数调用listener
        # 2. 通过调用on_message_received()
    # 监听类必须继承Listener类并重写on_message_received方法
    # 停止监听,调用stop()方法

if __name__ == '__main__':
    filters = [{"can_id":0x123, "can_mask":0x21, "extended": False}]
    listener = MyListener() # ①② 定义监听器 必须要继承抽象类
    with Bus() as bus:  # ①

        msg = Message(arbitration_id=0xc0ffee, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
        print("消息体的数据内容:{}".format(msg.data))  # ①①
        print("消息体的数据长度:{}".format(msg.dlc))  # ①①
        print("发送的消息体为:{}".format(msg))
        bus.send(msg)  # ③
        print(bus.state)  # ⑦  BusState.ACTIVE
        msg = bus.recv()

        # listener(msg)  # ①② 结合MyListener一起来看  第一种方法
        listener.on_message_received(msg)  # ①② 结合MyListener一起来看  第二种方法
        listener.stop()  # 停止监听


        """
        for msg in bus:  # ⑩ 此类方法会一直循环下去,只要有消息就会执行打印
            print("发送的消息看一看吧:{}".format(msg))
            can.CSVWriter("zz.txt", True).on_message_received(msg) # 将收到的can消息写入文件
        
        # 以下 ④
        msg1 = Message(arbitration_id=0xc0ffaa, data=[3, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
        msg2 = Message(arbitration_id=0xc0ffaa, data=[4, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
        msg3 = Message(arbitration_id=0xc0ffaa, data=[5, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
        print("msgs是个消息数组")
        t1 = bus.send_periodic(msgs=[msg1, msg2, msg3], period=2, duration=30)
        print("msgs是单个消息")
        t2 = bus.send_periodic(msgs=msg, period=2, duration=30)
        print("在第十秒的时候停止")
        for i in range(10):
            print("第{}秒.............".format(i+1))
            time.sleep(1)
        # t1.stop()  # 通过任务来管理
        # t2.stop()  # 通过任务来管理
        bus.stop_all_periodic_tasks()  # 通过调用函数任务停止
        # bus.shutdown()  # 通过bus来管理 如果使用with语句初始化bus 此方法会报错
        """

        # bus.set_filters(filters)  # ⑤ 设置过滤器 也可以在初始化bus时设置 如 Bus(can_filters=filters)
        # res = bus.recv(30)  # 30s的超时时间 ②
        # print("接收到的消息为:{res}".format(**locals()))


    """
    #  针对不使用with语句初始化bus的示例 ④
    bus = Bus()
    # 以下 ④
    msg = Message(arbitration_id=0xc0ffee, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
    msg1 = Message(arbitration_id=0xc0ffaa, data=[3, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
    msg2 = Message(arbitration_id=0xc0ffaa, data=[4, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
    msg3 = Message(arbitration_id=0xc0ffaa, data=[5, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False)
    print("msgs是个消息数组")
    t1 = bus.send_periodic(msgs=[msg1, msg2, msg3], period=2, duration=30)
    print("msgs是单个消息")
    t2 = bus.send_periodic(msgs=msg, period=2, duration=30)
    print("在第十秒的时候停止")
    for i in range(10):
        print("第{}秒.............".format(i + 1))
        time.sleep(1)
    bus.shutdown()  # 通过bus来管理  停止bus ⑥ 在with语句中不需要使用shutdown函数
    """

先写到这吧!最近太忙了!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值