CAN数据转ROS格式的订阅和发布(C++)
一、环境安装
- 安装ROS包:
确保你的ROS环境已经设置好,然后安装ROS中与CAN相关的包,比如ros_canopen 、ros_canopen_chain_node 等。 - 安装SocketCAN:
安装并配置SocketCAN以便与CAN总线通信。
二、ROS环境
1、创建工作空间
mkdir catkin_ws cd catkin_ws mkdir src
2、初始化工作空间
cd src catkin_init_workspace
3、编译(这里我们是空文件编译成功即可)
cd catkin_ws catkin_make
编译成功后会生成build和devel这两个目录
4、创建一个功能包
cd src catkin_create_pkg mytest roscpp std_msgs mytest是我的功能包名字,一个依赖于std::msgs和 roscpp的mytest功能包
mytest下就会存在这几个目录和文件
5、创建msg文件
cd src/mytest mkdir msg touch Can.h 创建一个msg文件,并将数据格式添加进去
msg文件是根据它里面的数据格式来生一个包含这些数据格式的结构体。
Can总线的数据个数如下:
#can_frame.msg uint32 can_id uint8 can_dlc uint8 can_pad uint8 can_res0 uint8 can_res1 uint8[8] data
在src下的package.xml文件中进行如下修改
在src下的CMakeLists.txt文件中
添加message_generation
添加你的msg文件名(可一次加多个)
取消注释,并添加message_runtime
做完以上步骤,返回到catkin_ws目录进行catkin_make编译,编译成功会在devel下的include文件中产生你msg所对应的头文件。
三、订阅和发布的代码
1、 在mytest包中添加两个节点,一个是发布者一个是订阅者
cd catkin_ws/src/mytest/src touch can_date_pub.cpp touch can_date_sub.cpp
2、can_date_pub.cpp
#include "ros/ros.h" #include "mytest/Can.h" //对应自己的包和生成的头文件 #include <linux/can.h> #include <linux/can/raw.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <net/if.h> #include <cstring> int main(int argc, char **argv) { ros::init(argc, argv, "can_publisher"); ros::NodeHandle nh; // 创建一个socket用于CAN通信 int can_socket = socket(PF_CAN,SOCK_RAW,CAN_RAW); if(can_socket < 0) { ROS_ERROR("failed to create CAN sicket"); return -1; } 指定CAN网络接口 struct ifreq ifr; std::strcpy(ifr.ifr_name, "can0"); //对应采集CAN总线的接口名 ioctl(can_socket, SIOCGIFINDEX, &ifr); // 将socket绑定到CAN接口 struct sockaddr_can addr; addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; if (bind(can_socket, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0) { ROS_ERROR("Failed to bind CAN socket to interface"); close(can_socket); return -1; } ros::Publisher can_pub = nh.advertise<mytest::Can>("can0_topic", 10); ros::Rate rate(10); // 调整速率 while (ros::ok()) { // 将CAN数据发布到ROS mytest::Can can_data; ssize_t bytes = recv(can_socket, &can_data, sizeof(mytest::Can), 0); if (bytes > 0) { // 创建一个CAN消息 mytest::Can can_msg; can_msg.can_id = can_data.can_id; can_msg.can_dlc = can_data.can_dlc; can_msg.can_pad = can_data.can_pad; can_msg.can_res0 = can_data.can_res0; can_msg.can_res1 = can_data.can_res1; can_msg.data = can_data.data; can_pub.publish(can_msg); } } // 延时以确保消息被发布 ros::spinOnce(); rate.sleep(); close(can_socket); return 0; }
3、can_date_sub.cpp
#include "ros/ros.h" #include "mytest/Can.h" void canCallback(const mytest::Can::ConstPtr& can_msg) { // 打印接收到的 CAN 消息数据 ROS_INFO("Received CAN message:"); ROS_INFO("can_id: %u", can_msg->can_id); ROS_INFO("can_dlc: %u", can_msg->can_dlc); ROS_INFO("can_pad: %u", can_msg->can_pad); ROS_INFO("can_res0: %u", can_msg->can_res0); ROS_INFO("can_res1: %u", can_msg->can_res1); ROS_INFO("Data: %u %u %u %u %u %u %u %u", can_msg->data[0], can_msg->data[1], can_msg->data[2], can_msg->data[3], can_msg->data[4], can_msg->data[5], can_msg->data[6], can_msg->data[7]); // ROS_INFO("Data:"can_msg->data); // for (uint8_t data_byte : can_msg->data) { // ROS_INFO(" %u", data_byte); // } } int main(int argc, char **argv) { ros::init(argc, argv, "can_subscriber"); ros::NodeHandle nh; ros::Subscriber can_sub = nh.subscribe("can0_topic", 10, canCallback); // 替换成你的话题名 // 循环等待消息 ros::spin(); return 0; }
4、在CMakeLists.txt文件中加上这两句话
add_executable(can_date_pub src/can_date_pub.cpp) add_executable(can_date_sub src/can_date_sub.cpp) target_link_libraries(can_date_pub ${catkin_LIBRARIES}) target_link_libraries(can_date_sub ${catkin_LIBRARIES})
5、添加完成后返回catkin_ws目录下进行编译
注意事项:如果编译时,报头文件错误(这个头文件是指msg数据格式生成的头文件),这个头文件格式是 #include “mytest/Can.h”. Mytest 是你自己创建的消息包,Can.h是Can.msg生成的一个头文件。