一、CAN協(xié)議簡介
1.1 CAN發(fā)展歷程

1.2 標(biāo)準(zhǔn)CAN與拓展CAN區(qū)別
CAN 1.0 定義的 CAN 具有 11 位消息標(biāo)識,提供可能的 2048 個消息標(biāo)識符。2.0 版允許有效的 29 位消息 ID。為了使新的 CAN 設(shè)備與舊的實現(xiàn)兼容,CAN 2.0 規(guī)范分為兩部分,2.0A 和 2.0B。在 CAN 2.0A 中,消息格式與僅使用 11 位消息 ID 的舊版本 CAN 一致。 CAN 2.0B 中,允允許有效的 29 位消息 ID,然后可以在被動或主動模式下實現(xiàn) CAN 2.0B。CAN 版本 1.0 和 2.0A 被稱為“標(biāo)準(zhǔn) CAN”,因為它們都使用 11 位消息 ID。CAN 2.0 B 被稱為“擴(kuò)展 CAN”,因為它使用擴(kuò)展的 29 位消息 ID
1.3 參考
https://www.can-cia.org/can-knowledge/can/can-history/
https://en.wikipedia.org/wiki/CAN_bus#History
https://www.edaboard.com/threads/difference-between-can1-0-and-can-2-0.217912/
二、基于CAN的典型應(yīng)用協(xié)議
SAE J1939/ISO11783
CANOpen
CANaerospace
DeviceNet
NMEA 2000
三、Linux上使用CAN的幾種方式
3.1 Linux中CAN設(shè)備掛載的總線類型
Linux可以通過串口、USB、網(wǎng)口、PCIE等接口與CAN設(shè)備間接進(jìn)行通信。
CAN設(shè)備連接在不同的硬件接口時,意味著CAN設(shè)備被掛載在Linux中不同的總線上,如USB總線、PCI-E總線(直接通過PCI-E與linux通訊),因此Linux采用不同的通訊協(xié)議與CAN設(shè)備進(jìn)行通訊。
3.2 CAN轉(zhuǎn)串口
-
設(shè)備形態(tài):
CAN轉(zhuǎn)串口
- 數(shù)據(jù)傳輸流程:
Linux->串口->單片機(jī)->CAN
- 通訊協(xié)議:
利用linux自帶的串口庫,或ros的serial庫對串口進(jìn)行讀寫。
通過定義串口協(xié)議,如10個字節(jié),前兩個字節(jié)為CAN ID,后面8個字節(jié)為CAN Data。通過此對串口發(fā)送數(shù)據(jù),讓單片機(jī)對其進(jìn)行轉(zhuǎn)換為CAN數(shù)據(jù)。
協(xié)議參考:https://manual.zlg.cn/web/#/64/2607
3.3 CAN轉(zhuǎn)USB
-
設(shè)備形態(tài):
CAN轉(zhuǎn)USB
注意,不是所有的周立功CAN轉(zhuǎn)USB設(shè)備都支持Linux,見表:https://manual.zlg.cn/web/#/146
- 數(shù)據(jù)傳輸流程:
Linux->USB->單片機(jī)->CAN
- 通訊協(xié)議:
與CAN轉(zhuǎn)串口不一樣,利用的庫是libusb和libusbcan,對CAN轉(zhuǎn)USB數(shù)據(jù)直接進(jìn)行讀寫
libusb是linux驅(qū)動,可以操作USB接口對USB設(shè)備進(jìn)行讀寫操作。
libusbcan是CAN轉(zhuǎn)USB設(shè)備廠商給的驅(qū)動,其中定義了can數(shù)據(jù)幀的數(shù)據(jù)結(jié)構(gòu)體,填充相應(yīng)的CAN ID和CAN Data,最后通過USB進(jìn)行讀寫。
協(xié)議參考:https://manual.zlg.cn/web/#/55/2282
3.4 CAN轉(zhuǎn)網(wǎng)
-
設(shè)備形態(tài):
CAN轉(zhuǎn)網(wǎng) 數(shù)據(jù)傳輸流程:
Linux->TCP(UDP)/IP->單片機(jī)->CAN
- 通訊協(xié)議:
利用Linux的socket庫對網(wǎng)絡(luò)設(shè)備進(jìn)行讀寫操作。
Linux通過IP地址利用Socket與CAN設(shè)備通信,一個TCP/UDP幀包中包含若干個連續(xù)的CAN幀,每個CAN幀包含若干個字節(jié),包含CAN ID和CAN Data。
協(xié)議參考:https://manual.zlg.cn/web/#/67/2693
3.5 CAN轉(zhuǎn)PCI-E
-
設(shè)備形態(tài):
PCI-E轉(zhuǎn)CAN
注意周立公CAN轉(zhuǎn)PCI-E設(shè)備,型號PCI-5010-P不支持Linux:https://manual.zlg.cn/web/#/146
- 數(shù)據(jù)傳輸流程:
Linux->SocketCAN->單片機(jī)->CAN
- 通訊協(xié)議:
Linux提供了SocketCAN接口,使得CAN總線通信近似于和以太網(wǎng)的通信。網(wǎng)絡(luò)設(shè)備通過IP確定地址,CAN設(shè)備通過CAN ID確定地址,如can0,can1...
CAN ID可以通過ifconfig -a查看
Linux利用SocketCAN庫根據(jù)CAN的設(shè)備ID對其進(jìn)行讀寫操作,CAN的數(shù)據(jù)結(jié)構(gòu)在Socket CAN中被結(jié)構(gòu)體定義,填充相應(yīng)數(shù)據(jù)后發(fā)送即可。
協(xié)議參考:https://manual.zlg.cn/web/#/77/3180
3.6 USB轉(zhuǎn)CAN(CANable)
-
設(shè)備形態(tài):
USB轉(zhuǎn)CAN(SocketCAN)
開源地址:https://canable.io/
淘寶有售賣
- 數(shù)據(jù)傳輸流程:
注意與上面周立功的CAN轉(zhuǎn)USB設(shè)備數(shù)據(jù)傳輸流程不同,利用的是SocketCAN,不是USB。
Linux->SocketCAN->單片機(jī)->CAN
- 通訊協(xié)議:
Linux利用SocketCAN庫根據(jù)CAN的設(shè)備ID對其進(jìn)行讀寫操作,CAN的數(shù)據(jù)結(jié)構(gòu)在Socket CAN中被結(jié)構(gòu)體定義,填充相應(yīng)數(shù)據(jù)后發(fā)送即可。
SocketCAN簡介:https://en.wikipedia.org/wiki/SocketCAN
協(xié)議參考1:https://canable.io/getting-started.html#socketcan-linux
協(xié)議參考2:https://manual.zlg.cn/web/#/77/3180
四、ROS中的CAN通信
4.1 ROS提供三個層次的驅(qū)動庫

-
ros_canopen:canopen包,http://wiki.ros.org/ros_canopen -
socketcan_bridge:將SocketCAN接口封裝為ROS的topic,http://wiki.ros.org/socketcan_bridge -
socketcan_interface:直接操作SocketCAN接口,http://wiki.ros.org/socketcan_interface
socketcan_interface是較為底層的包,與Linux的socketcan打交道。
socketcan_bridge是ros中最常用的包,通過將接收到的topic轉(zhuǎn)換為can數(shù)據(jù)發(fā)出去,或者把接收到的can數(shù)據(jù)轉(zhuǎn)換為ros的topic。
ros_canopen是基于canopen應(yīng)用協(xié)議的包,是上層協(xié)議。
4.2 socketcan_bridge包
該功能以三個節(jié)點的形式提供:socketcan_bridge_node、socketcan_to_topic_node和topic_to_socketcan_node。要從同一個 CAN 設(shè)備接收和發(fā)送幀,需要使用socketcan_bridge_node來防止每個發(fā)送的消息都回顯到接收主題。
4.2.1 socketcan_bridge_node
從 SocketCAN 設(shè)備接收幀并將這些幀發(fā)布到主題上,同時它偵聽 CAN 消息并將這些消息發(fā)送到 SocketCAN。發(fā)送到 CAN 設(shè)備的幀不會作為接收消息發(fā)布。
訂閱的話題名稱:
sent_messages(can_msgs/Frame),此處收到的消息將被發(fā)送到 SocketCAN 設(shè)備。發(fā)布的話題名稱:
received_messages(can_msgs/Frame),在 SocketCAN 設(shè)備上接收到的幀在本主題中發(fā)布。ROS節(jié)點參數(shù):~
can_device(string, default: can0),SocketCAN 設(shè)備的名稱,默認(rèn)情況下這些設(shè)備被命名為can0及以上。
4.2.2 socketcan_to_topic_node
將在 SocketCAN 設(shè)備上接收到的幀發(fā)布到topic。
發(fā)布的話題名稱:received_messages (can_msgs/Frame),在 SocketCAN 設(shè)備上接收到的幀在本主題中發(fā)布。
ROS節(jié)點參數(shù):~
can_device(string, default: can0),SocketCAN 設(shè)備的名稱,默認(rèn)情況下這些設(shè)備被命名為can0及以上。
4.2.3 topic_to_socketcan_node
將收到的topic的 CAN 消息發(fā)送到 SocketCAN 設(shè)備。
訂閱的話題名稱:
sent_messages(can_msgs/Frame),此處收到的消息將被發(fā)送到 SocketCAN 設(shè)備。ROS節(jié)點參數(shù):~
can_device(string, default: can0),SocketCAN 設(shè)備的名稱,默認(rèn)情況下這些設(shè)備被命名為can0及以上。
4.3 基于ROS中轉(zhuǎn)節(jié)點松耦合的CAN通信
4.3.1 松耦合需求的原因
對于一些成熟的包,或者比較復(fù)雜的包,要想把其中的topic轉(zhuǎn)為CAN數(shù)據(jù)發(fā)出去,有兩種修改方式:
- 直接修改源碼中相應(yīng)topic的數(shù)據(jù)類型,或者在源碼中新發(fā)布一個can_msgs/Frame數(shù)據(jù)類型,名稱為sent_messages的topic
- 新建一個中轉(zhuǎn)節(jié)點,接收該復(fù)雜節(jié)點的topic(消息類型為該復(fù)雜節(jié)點的消息類型),將其轉(zhuǎn)換為一個can_msgs/Frame數(shù)據(jù)類型,名稱為sent_messages的topic,將反饋的can信息,通過指定的topic,反饋給該復(fù)雜節(jié)點。
第二種方式更為簡單和快速,但會增加通信中轉(zhuǎn)延時,ROS的節(jié)點通信是基于TCP/IP,對于本機(jī)節(jié)點通信而言,此延時大概在1ms以內(nèi)(百兆網(wǎng)卡以上)。
4.3.2 中轉(zhuǎn)節(jié)點
對于該中轉(zhuǎn)節(jié)點,需要訂閱兩個topic,發(fā)布兩個topic
- 訂閱:
- 復(fù)雜節(jié)點發(fā)布的topic
- socketcan_bridge_node節(jié)點的topic:
received_messages
- 發(fā)布:
- 復(fù)雜節(jié)點訂閱的topic
- socketcan_bridge_node節(jié)點的topic:
sent_messages
4.3.3 CAN數(shù)據(jù)幀
socketcan_bridge_node的話題的消息類型都為ROS內(nèi)置消息類型can_msgs/Frame:
Header header
uint32 id
bool is_rtr
bool is_extended
bool is_error
uint8 dlc
uint8[8] data
需要填充的數(shù)據(jù)是
-
id:CAN ID -
dlc:數(shù)據(jù)長度,一般為8 -
is_extended:是否是拓展幀,一般為0 -
uint8[8] data:can數(shù)據(jù)
4.3.4 使用
一般把該中轉(zhuǎn)節(jié)點與socketcan_bridge_node放在一個launch文件中。
如果socketcan_bridge_node節(jié)點的名稱沖突了,可以在launch文件中添加group,或者remap話題名稱。
五、總結(jié)
推薦CANable + socketcan_bridge包實現(xiàn)ROS中的CAN通信!
Github:https://github.com/wanghuohuo0716/ros_can_driver
ROS socketcan_bridge包使用參考:
https://blog.csdn.net/zyf_to_utopia/article/details/116209605
https://blog.csdn.net/m0_58322903/article/details/121630592
https://blog.csdn.net/wangrunhuan/article/details/117962857
SocketCAN介紹:
https://en.wikipedia.org/wiki/SocketCAN




