ROS Melodic CAN 通信实战:从 C++ 原生库到 ROS 节点的 3 步移植指南
ROS Melodic CAN 通信实战:从 C++ 原生库到 ROS 节点的 3 步移植指南
在机器人开发中,CAN 总线因其高可靠性和实时性被广泛应用于电机控制、传感器数据采集等场景。本文将手把手教你如何将基于厂商 SDK(如 libcontrolcan.so)的 C++ 原生 CAN 程序封装为标准 ROS 节点,实现与 ROS 生态的无缝集成。
1. 环境准备:构建 Catkin 工作空间
首先需要搭建基础的开发环境。推荐使用 Ubuntu 18.04 和 ROS Melodic 组合,这是目前最稳定的 ROS LTS 版本之一。
# 创建工作空间 mkdir -p ~/can_ws/src cd ~/can_ws catkin_make关键目录结构应如下所示:
can_ws/ └── src/ ├── CMakeLists.txt └── can_pkg/ ├── include/ ├── src/ └── lib/对于第三方 CAN 库的处理,需要特别注意:
- 将
controlcan.h头文件放置于can_pkg/include/can_pkg/ - 将
libcontrolcan.so动态库文件放置于can_pkg/lib/
提示:使用
tree命令可以快速查看目录结构,确保文件放置位置正确。
2. 代码封装:将原生逻辑融入 ROS 节点
原始 CAN 程序通常采用轮询方式接收数据,我们需要将其改造为 ROS 节点。以下是关键改造点:
2.1 初始化 ROS 节点
#include <ros/ros.h> #include "can_pkg/controlcan.h" int main(int argc, char** argv) { ros::init(argc, argv, "can_bridge"); ros::NodeHandle nh; // 原始CAN初始化代码 if(VCI_OpenDevice(VCI_USBCAN2, 0, 0) != 1) { ROS_ERROR("Failed to open CAN device!"); return -1; } // ...其余初始化代码 }2.2 实现消息发布机制
#include <can_msgs/Frame.h> ros::Publisher can_pub = nh.advertise<can_msgs::Frame>("can_raw", 1000); VCI_CAN_OBJ rec[50]; while(ros::ok()) { int reclen = VCI_Receive(VCI_USBCAN2, 0, 0, rec, 50, 200); if(reclen > 0) { can_msgs::Frame msg; msg.header.stamp = ros::Time::now(); msg.id = rec[0].ID; msg.dlc = rec[0].DataLen; std::memcpy(msg.data.data(), rec[0].Data, 8); can_pub.publish(msg); } ros::spinOnce(); }2.3 处理设备权限问题
Linux 系统下操作 USB-CAN 设备需要 root 权限,但 ROS 节点通常以普通用户身份运行。可通过 udev 规则解决:
sudo sh -c 'echo ACTION==\"add\", SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"04d8\", ATTRS{idProduct}==\"0053\", GROUP=\"users\", MODE=\"0777\" > /etc/udev/rules.d/99-myusb.rules' sudo udevadm control --reload-rules3. 编译配置:完善 CMake 和链接设置
正确的编译配置是移植成功的关键。以下是CMakeLists.txt的关键配置:
cmake_minimum_required(VERSION 3.0.2) project(can_pkg) find_package(catkin REQUIRED COMPONENTS roscpp can_msgs ) include_directories( include ${catkin_INCLUDE_DIRS} ) link_directories( lib ${catkin_LIB_DIRS} ) add_executable(can_node src/can_node.cpp) target_link_libraries(can_node ${catkin_LIBRARIES} controlcan )常见编译问题解决方案:
| 错误类型 | 可能原因 | 解决方案 |
|---|---|---|
| 头文件找不到 | 路径配置错误 | 检查include_directories |
| 链接失败 | 库文件缺失 | 确认.so文件在lib/目录 |
| 运行时错误 | 权限不足 | 检查 udev 规则是否生效 |
4. 进阶优化:提升节点可靠性
完成基础移植后,可以考虑以下优化措施:
4.1 实现双通道通信
bool initCANChannel(int channel) { VCI_INIT_CONFIG config; // ...配置参数 if(VCI_InitCAN(VCI_USBCAN2, 0, channel, &config) != 1) { ROS_ERROR("Init CAN%d failed!", channel + 1); return false; } return true; }4.2 添加看门狗机制
ros::Timer watchdog_timer = nh.createTimer( ros::Duration(1.0), [](const ros::TimerEvent&) { if(last_msg_time + ros::Duration(2.0) < ros::Time::now()) { ROS_WARN("CAN communication timeout!"); } } );4.3 支持动态重配置
dynamic_reconfigure::Server<can_pkg::CANConfig> server; server.setCallback(boost::bind(&configCallback, _1, _2));通过以上三步改造,原本独立的 CAN 应用程序就变成了一个标准的 ROS 节点,可以方便地与其他 ROS 模块进行数据交互。在实际机器人项目中,这种集成方式大幅提升了开发效率,使得 CAN 总线设备能够充分利用 ROS 强大的工具链和生态系统。
