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生成的一个头文件。