基于ROS的上下位机CAN通讯

目录

  • 序言
  • 虚拟 CAN 体验
    • 安装 can-utils 包(主要用其中包含的 candump 工具)
    • 具体体验步骤
  • 使用 ros can
    • 安装相关依赖(此处以 Ubuntu18.04 为例,具体 ROS 版本号对应需要更改)
    • 具体使用步骤
  • 在 ROS 代码中使用 CAN 通讯
    • 涉及到的包以及话题消息
    • 简单示例
    • 查看发布的数据
  • 实体 CAN 使用
    • CAN 口回环检测
      • 简单含义及作用
      • 操作流程
      • 测试结果
    • CAN 测试
  • 参考文章


序言

先前采用了串口进行上下位机通讯,由于需要USB转串口,会出现通讯不上的问题。
之后了解了CAN通讯,发现相比串口通信更稳定,在编程上也相对自由些


虚拟 CAN 体验

安装 can-utils 包(主要用其中包含的 candump 工具)

sudo apt install can-utils

具体体验步骤

  1. 加载虚拟 CAN 模块
    sudo modprobe vcan
  2. 添加 can0 网卡
    sudo ip link add dev can0 type vcan
    此时通过 ifconfig -a 命令可以看到比平时多了一个 can0,但此时状态为:
  3. 启动 can0
    sudo ip link set dev can0 up
    再次查看 can0 状态,此时为:
  4. 启动 candump 命令,监控 can0 端口的数据
    candump can0
  5. 使用 cansend 命令,向 can0 端口发布数据:123#456789
    cansend can0 123#456789
    此时,candump 窗口接收到数据:can0 123 [3] 45 67 89

注意
使用 cansend 发布数据需要有一定格式,否则会报错,官方声明如下

cansend - send CAN-frames via CAN_RAW sockets.

Usage: cansend <device> <can_frame>.

<can_frame>:
 <can_id>#{data}          for 'classic' CAN 2.0 data frames
 <can_id>#R{len}          for 'classic' CAN 2.0 data frames
 <can_id>##<flags>{data}  for CAN FD frames

<can_id>:
 3 (SFF) or 8 (EFF) hex chars
{data}:
 0..8 (0..64 CAN FD) ASCII hex-values (optionally separated by '.')
{len}:
 an optional 0..8 value as RTR frames can contain a valid dlc field
<flags>:
 a single ASCII Hex value (0 .. F) which defines canfd_frame.flags

Examples:
  5A1#11.2233.44556677.88 / 123#DEADBEEF / 5AA# / 123##1 / 213##311223344 /
  1F334455#1122334455667788 / 123#R / 00000123#R3
  1. 关闭 can0
    sudo ip link set dev can0 down
    此时通过 ifconfig -a 命令可以看到 can0 状态变回:
  2. 删除 can0
    sudo ip link del dev can0
    此时使用 ifconfig -a 命令已经看不到 can0 了

使用 ros can

安装相关依赖(此处以 Ubuntu18.04 为例,具体 ROS 版本号对应需要更改)

sudo apt-get install ros-melodic-can-msgs

sudo apt-get install ros-melodic-socketcan-bridge

sudo apt-get install libmuparser-dev

具体使用步骤

  1. 启动 master 节点
    roscore
  2. 启动 ROS 的 CAN 数据转ROS话题节点
    rosrun socketcan_bridge socketcan_to_topic_node
  3. 启动 ROS CAN数据转换后的话题节点
    rostopic echo /received_messages
  4. 启动 candump 监控 can0
    camdump can0
  5. 使用 cansend 命令发布数据
    cansend can0 123#456789
  6. 此时可以在 3.4. 窗口中分别接收到 CAN 转换后的数据和 CAN 的原生数据。

在 ROS 代码中使用 CAN 通讯

涉及到的包以及话题消息

  1. socketcan_bridge 包:主要是两个用于转换 CAN 数据和 ROS 话题数据的节点(topic_to_socketcan_node,socketcan_to_topic_node)
  2. can_msgs::Frame 消息:用于存储 CAN 消息,官方数据结构如下
std_msgs/Header header
  uint32 seq
  time stamp
  string frame_id
uint32 id
bool is_rtr
bool is_extended
bool is_error
uint8 dlc
uint8[8] data

主要成员变量解析
id:CAN 通讯的 ID
dlc:数据字节长度
data:用于 CAN 数据存储的数组

简单示例

  • launch文件,用于启动数据转换节点和示例节点
<launch>
    
    <node pkg="socketcan_bridge" name="topic_to_socket_node" type="topic_to_socket_node" output="screen">
        <param name="can_device" value="can0" />
    node>
    
    <node pkg="socketcan_bridge" name="socket_to_topic_node" type="socket_to_topic_node" output="screen">
        <param name="can_device" value="can0" />
    node>
    
    <node  pkg="can_test" name="can_test" type="can_test_node" output="screen" />
launch>
  • C++文件,示例节点
#include 
#include 


void canCallBack(const can_msgs::Frame::ConstPtr &msg)
{
    static int times = 0;
	std::cout << times++ << "ID: " << msg->id << " data: " ;    for (int i = 0; i < msg->dlc; i++)
    {
        printf("%X ", msg->data[i]);
    }
    std::cout << std::endl;
}

int main(int argc, char **argv)
{
    setlocale(LC_ALL, "");
    ros::init(argc, argv, "can_test_node");
    ros::NodeHandle nh;
    ros::Publisher pub = nh.advertise<can_msgs::Frame>("sent_messages", 100);

    can_msgs::Frame can_frame_msg;
    can_frame_msg.id =  0x521;
    can_frame_msg.dlc = 8;
    // 分开赋值
    can_frame_msg.data[0] = 0x00;
    can_frame_msg.data[1] = 0x11;
    can_frame_msg.data[2] = 0x22;
    can_frame_msg.data[3] = 0x33;
    can_frame_msg.data[4] = 0x44;
    can_frame_msg.data[5] = 0x55;
    can_frame_msg.data[6] = 0x66;
    can_frame_msg.data[7] = 0x77;
    // can_frame_msg.data = {00, 11, 22, 33, 44, 55, 66, 77};   // 一次性全部赋值

    ros::Subscriber sub = nh.subscribe("/received_messages", 100, &canCallBack);

    ros::Rate r(1);
    while (ros::ok())
    {
        pub.publish(can_frame_msg);
        r.sleep();
        ros::spinOnce();
    }

    return 0;
}

  • 使用 cansend 命令发布数据,进行 CAN 数据转 ROS 话题数据的方法验证

查看发布的数据

  • candump can0 :查看CAN 数据
  • rostopic echo /received_messages :查看话题数据

实体 CAN 使用

CAN 口回环检测

简单含义及作用

  • 回环检测模式又称自检测模式,主要是 CAN 口自己发出的数据自己接收,数据不流经 CAN 总线
  • 这样做可以检测 CAN 口通讯是否正常,是否存在问题,排除 CAN 总线及总线上的干扰

操作流程

  • 关闭 CAN
    sudo ip link set down can0
  • 设置 CAN 口波特率,此处设置为 500Kbps
    sudo ip link set can0 type can bitrate 500000
  • 设置CAN 为回环检测模式
    sudo ip link set can0 type can loopback on
  • 使能 CAN
    sudo ip link set up can0
  • 检测并发布 CAN 数据
    candump can0
    cansend can0 521#010301000001

测试结果

  • 可以在 candump 窗口看到在发布 CAN 数据的同时又接收到一个相同的 CAN 数据
  • 如果出现上面的情况,就表示 CAN 口无误,反之需要针对输出的问题进行相应处理
    当前未遇到过问题,若有朋友遇到欢迎在评论区讨论

CAN 测试

  • 查看当前 CAN 口状态
    ip -details -statistics link show can0
  • 根据实际需求修改设置 CAN 口:波特率、是否为回环测试、时钟同步精度等,如下示例
    sudo ip link set can0 type can bitrate 500000 sjw 4 restart-ms 100 berr-reporting on

命令解析:
ip link: 这是用于操作Linux内核中网络设备链接的工具。它可以用来查看、修改或操作网络接口的各种设置。
set can0 type can: 这部分命令设置can0接口的类型为CAN接口。can0是CAN接口的名字。
bitrate 500000: 这设置了CAN接口的数据传输速率为500,000比特每秒(500 kbps)。
sjw 4: 这设置了同步跳转宽度(sjw)为4。同步跳转宽度决定了时钟同步的精度,它影响时钟同步的延迟和抖动。
restart-ms 100: 这设置了在检测到错误后重新启动通信的时间间隔为100毫秒。
berr-reporting on: 这启用了位错误报告。当这个选项被启用时,如果检测到位错误,系统会发送一个错误报告。

  • 关闭 CAN 回环检测模式
    sudo ip link set can0 type can loopback off

  • 检测并发布 CAN 数据
    candump can0
    cansend can0 521#010301000001
    此时可以使用其他 CAN 设备向 CAN 总线发布数据,使用candump查看是否存在数据


参考文章

[1] ros_canopen使用心得
[2] ROS采用SocketCAN进行通信
[3] TX2/Linux下can总线的接收与发送详解!

你可能感兴趣的:(机器人)