ROS2基于ros2_socketcan进行CAN总线简单收发

1. can_utils工具

can-utils 是一套用于Linux操作系统的开源工具,专门用来处理与CAN(Controller Area Network)总线相关的任务。CAN总线广泛应用于汽车和工业自动化中,用于设备之间的通讯。 这个工具集提供了多种命令行工具,用于发送、接收和处理CAN网络上的数据。

can_utils 的基本使用:

  • candump
    • 功能 :捕获并显示通过CAN接口的数据帧。
    • candump can0 显示 can0 接口上的所有CAN数据。
    • candump can0,123:7FF 仅显示ID范围在 0x1230x7FF 之间的CAN数据。
    • candump -l can0记录数据到文件,默认文件名格式为 candump-日期.log
  • cansend
    • 功能 :向CAN总线发送CAN消息。
    • cansend can0 000#1122334455667788can0 接口发送一个ID为 0x000 的CAN帧,数据为 1122334455667788
  • cangen
    • 功能 :生成随机CAN数据包并发送到CAN总线上。
    • cangen can0 生成并发送随机CAN数据到 can0 接口。
  • canplayer
    • 功能 :重放 candump 记录的数据。
    • canplayer -I log.txt 重放存储在 log.txt 文件中的CAN数据。
  • cansniffer
    • 功能 :显示CAN数据的变化,类似于Wireshark。
    • cansniffer can0 显示 can0 接口上的CAN数据变化。

配置启动CAN接口:

# 关闭CAN0接口
sudo ip link set can0 down
# 配置CAN接口波特率 500000
sudo ip link set can0 type can bitrate 500000
# 配置回环测试(自发自收)模式
sudo ip link set can0 type can loopback on
# 启动CAN0接口
sudo ip link set can0 up

CAN通信原理推荐阅读:第十二章 CAN - 小李 的知识库

2. ros2_socketcan收发示例

2.1 安装ROS2功能包

sudo apt install ros-humble-can-msgs
sudo apt install ros-humble-ros2-socketcan

进入工作空间src目录,创建CAN通信的功能包

ros2 pkg create can_bridge_cpp --build-type ament_cmake --dependencies rclcpp ros2_socketcan ros2_socketcan_msgs --license Apache-2.0

2.2 CAN发送

#include "rclcpp/rclcpp.hpp"
#include "ros2_socketcan/socket_can_sender.hpp"
#include "can_msgs/msg/frame.hpp"
// can发送者工作空间
using namespace drivers::socketcan;
// 定时器时间参数工作空间
using namespace std::chrono_literals;

class CanSender : public rclcpp::Node
{
public:
    /* 构造函数 */
    CanSender(std::string name) : Node(name) {
        RCLCPP_INFO(this->get_logger(), "节点的名称为:%s.",name.c_str());
        /* 创建发送者 */
        can_sender_ = std::make_shared<SocketCanSender>(
            // can接口
            "can0",
            // 启用can_fd
            false,
            // 默认配置
            CanId(
                // 默认ID
                0x123, 
                // 时间戳,0表示立即发送
                0,
                // 数据帧、遥控帧、错误帧
                FrameType::DATA,
                // 标准格式、扩展格式
                StandardFrame_{}) 
        );
        /* 创建定时器 */
        timer_ = this->create_wall_timer(1s, std::bind(&CanSender::TimerCallback, this));
    }

private:
    /* 定时器回调函数 */
    void TimerCallback(void) {
        RCLCPP_INFO(this->get_logger(), "回调函数!");
        /* 初始化发送的消息 */
        unsigned char messages[] = {0xCE, 0xBD, 0xEE, 0xFF};
        can_sender_->send(
            // 必要参数
            messages,
            // 灵活配置(选配)
            CanId(
                // 默认ID
                0x126, 
                // 时间戳,0表示立即发送
                0,
                // 数据帧、遥控帧、错误帧
                FrameType::DATA,
                // 标准格式、扩展格式
                StandardFrame_{}),
            // 选配
            100ms
        );
    }
    std::shared_ptr<drivers::socketcan::SocketCanSender> can_sender_;
    std::shared_ptr<drivers::socketcan::SocketCanReceiver> can_receiver_;
    rclcpp::TimerBase::SharedPtr timer_;
};

int main(int argc, char * argv[])
{
    rclcpp::init(argc, argv);
    auto node = std::make_shared<CanSender>("can_sender");
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

配置CMakeLists.txt文件,如下是需添加内容:

add_executable(can_sender_cpp src/can_sender.cpp)

ament_target_dependencies(
  can_sender_cpp
  "rclcpp"
  "ros2_socketcan"
  "ros2_socketcan_msgs"
)

install(TARGETS
can_sender_cpp
  DESTINATION lib/${PROJECT_NAME}
)

运行结果:

在这里插入图片描述

2.3 CAN接收

#include "rclcpp/rclcpp.hpp"
#include "ros2_socketcan/socket_can_receiver.hpp"
#include "can_msgs/msg/frame.hpp"

// can接收者者工作空间
using namespace drivers::socketcan;
// 定时器时间参数工作空间
using namespace std::chrono_literals;

class CanReceiver : public rclcpp::Node
{
public:
    /* 构造函数 */
    CanReceiver(std::string name) : Node(name)
    {
        RCLCPP_INFO(this->get_logger(), "节点的名称为:%s.", name.c_str());
        can_receiver_ = std::make_shared<SocketCanReceiver>(
            "can0",
            // 是否使用高速CAN
            false
        );
        timer_ = this->create_wall_timer(500ms, std::bind(&CanReceiver::TimerCallback, this));
    }


private:
    void TimerCallback(void)
    {
        try {
            RCLCPP_INFO(this->get_logger(), "回调函数!");
            unsigned char message[4] = {0};
            can_id = can_receiver_->receive(message, 100us);
            RCLCPP_INFO(
                this->get_logger(),
                "ID:%d, 是否扩展格式:%s, 帧格式:%d, 数据长度:%u",
                can_id.get(), 
                can_id.is_extended() ? "是" : "否", 
                static_cast<int>(can_id.frame_type()),
                can_id.length()
            );
            uint32_t length = can_id.length();
            for (uint32_t i = 0 ; i < length ; i++) {
                RCLCPP_INFO(
                this->get_logger(), 
                "数据[%d]:%d",
                i,
                message[i]
                );
            }
        }
        catch (const SocketCanTimeout& e) {
            RCLCPP_DEBUG(this->get_logger(), "接收超时(正常现象)");
        }
        catch (const std::exception& e) {
            RCLCPP_ERROR(this->get_logger(), "接收错误: %s", e.what());
        }
    }
    std::shared_ptr<drivers::socketcan::SocketCanReceiver> can_receiver_;
    rclcpp::TimerBase::SharedPtr timer_;
    CanId can_id;
};

int main(int argc, char *argv[])
{
    rclcpp::init(argc, argv);
    auto node = std::make_shared<CanReceiver>("can_receiver");
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

⚠有一个问题就是,unsigned char message[4] = {0};接收数据的数组要和发送的字节数一致,否则会报一个错:

在这里插入图片描述

当然这样做个人认为并非好的处理方式,但暂时还未找到合适的解决方案。只能发送方按传统CAN协议的最大字节数 8 ,没有用的位置 0x00

运行结果,291为报文ID:

在这里插入图片描述

<think>嗯,用户想了解如何在ROS中使用SocketCAN。首先,我需要回忆一下SocketCAN是什么。SocketCANLinux内核提供的一种CAN总线接口实现,允许通过标准的套接字API来访问CAN设备。而ROS是机器人操作系统,常用于机器人通信和控制。用户可能是在开发机器人项目,需要将CAN设备集成到ROS中,比如控制电机、传感器等。 接下来,我得考虑SocketCANROS集成的常见方法。通常有两种方式:一种是用现有的ROS驱动包,比如socketcan_bridge或者ros_canopen;另一种是自己编写节点,使用SocketCAN的接口读取和发送CAN数据,然后转换成ROS消息。 首先应该检查是否有现成的ROS包可用,这样可以节省时间。比如,socketcan_bridge包可能已经提供了CANROS消息的转换功能。用户需要安装这个包,然后配置launch文件启动相关节点,指定CAN接口如can0。然后,他们就可以订阅或发布ROS话题来收发数据了。 如果现有的包不满足需求,用户可能需要自己写节点。这时候需要用到C++或Python的SocketCAN库。例如,在Python中,可以使用python-can库来与CAN总线通信,然后在ROS节点中循环读取CAN帧,将其转换为ROS标准消息(比如can_msgs/Frame)并发布到话题上。同时,可能需要订阅某个话题来接收控制命令,再转换成CAN帧发送出去。 另外,用户需要注意权限问题,可能需要配置udev规则或使用sudo来访问CAN接口。还有,确保CAN接口已经正确配置,比如使用ip link set can0 up type can bitrate 500000这样的命令来启动can0接口。 可能还需要考虑数据解析的问题。CAN数据通常是原始字节,需要根据具体设备的协议进行解析。比如,电机的转速可能存储在特定的CAN ID和数据段中,用户需要编写相应的解码和编码函数。 用户可能遇到的问题包括:无法找到CAN接口、权限不足、数据解析错误、实时性问题等。需要逐步排查,检查接口状态、权限设置,使用工具如candump测试CAN总线通信是否正常,再检查ROS节点的日志信息。 最后,应该提醒用户参考相关的文档和示例,比如socketcan_bridge的官方文档,或者查找社区中的教程和案例。同时,测试时可以先使用虚拟CAN接口进行模拟,确保代码逻辑正确后再连接到实际硬件。</think>### SocketCANROS的集成方法 #### 1. 安装依赖与驱动 首先确保系统已加载SocketCAN驱动模块: ```bash sudo modprobe can sudo modprobe can_raw sudo ip link set can0 up type can bitrate 500000 # 配置CAN0接口比特率[^2] ``` #### 2. 使用现成ROS包 推荐使用`ros_canopen`或`socketcan_bridge`包: ```bash sudo apt-get install ros-<distro>-socketcan-bridge ``` 在launch文件中配置CAN接口: ```xml <node name="socketcan_bridge" pkg="socketcan_bridge" type="socketcan_to_topic_node"> <param name="can_device" value="can0"/> </node> ``` #### 3. 自定义节点开发 ##### C++示例(需包含`can_msgs`包): ```cpp #include <ros/ros.h> #include <can_msgs/Frame.h> #include <linux/can.h> #include <sys/socket.h> int main(int argc, char **argv) { ros::init(argc, argv, "can_node"); ros::NodeHandle nh; ros::Publisher can_pub = nh.advertise<can_msgs::Frame>("can_tx", 1000); int s = socket(PF_CAN, SOCK_RAW, CAN_RAW); struct sockaddr_can addr; addr.can_family = AF_CAN; addr.can_ifindex = if_nametoindex("can0"); bind(s, (struct sockaddr *)&addr, sizeof(addr)); while(ros::ok()) { struct can_frame frame; read(s, &frame, sizeof(frame)); can_msgs::Frame msg; msg.id = frame.can_id; msg.dlc = frame.can_dlc; memcpy(msg.data.data(), frame.data, 8); can_pub.publish(msg); } } ``` ##### Python示例(需`python-can`库): ```python #!/usr/bin/env python import rospy from can_msgs.msg import Frame import can def can_listener(): rospy.init_node('can_receiver') pub = rospy.Publisher('can_rx', Frame, queue_size=10) bus = can.interface.Bus(channel='can0', bustype='socketcan') while not rospy.is_shutdown(): msg = bus.recv(0.1) if msg: ros_msg = Frame(id=msg.arbitration_id, dlc=msg.dlc, data=list(msg.data)) pub.publish(ros_msg) if __name__ == '__main__': can_listener() ``` #### 4. 数据解析建议 对于复杂协议建议: 1. 创建自定义消息类型 2. 使用`canopen_chain`处理标准CANopen协议 3. 使用DBC文件解析汽车总线数据
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值