PS:本文需要的先行知识请自行补充
本文通过linux的socket,完成CAN通讯的封装,并部署于ROS中;
Socket Socket(套接字)是一种用于实现网络通信的编程接口(API),它提供了一种标准化的方式,使得不同操作系统和编程语言之间的应用程序能够相互通信。
环境部署 工程环境:RK3588 + Ubuntu 22.04.2 LTS + ROS2-humble
默认工作空间已建立,环境变量已导入脚本;
创建功能包:catkin_create_pkg mysocketcan_pkg roscpp rospy std_msgs
1.linux-socket 部分源码 代码需包含的头文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <cstdio> #include <cstdlib> #include <cstring> #include <iostream> #include <stdexcept> #include <sstream> #include <unistd.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <linux/can.h> #include <linux/can/raw.h> #include <net/if.h> #include <termios.h>
部分宏定义:
在本次应用中,linux中CAN的接口需要通过shell系统指令打开,并进行设置,因此对部分指令进行了宏定义
1 2 3 4 5 6 #define CAN_DEV "can0" #define up "ifconfig can0 up" #define down "ifconfig can0 down" #define command "/sbin/ip link set can0 type can bitrate 1000000" #define CAN_Loop "/sbin/ip link set can0 type can loopback on" #define CAN_Loop_OFF "/sbin/ip link set can0 type can loopback off"
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Socket_Can { public: Socket_Can(); ~Socket_Can(); void Init(); void send_can_frame(const struct can_frame& xframe); void receive_can_frame(struct can_frame& xframe); void close_socket(); private: struct ifreq ifr = {0}; // struct can_bittiming btr = {0}; struct sockaddr_can can_addr = {0}; struct can_frame frame = {0}; int socket_fd = -1; int ret; struct can_filter rfilter[2]; };
对应封装:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 Socket_Can::Socket_Can() { /*打开套接字*/ socket_fd = socket(PF_CAN,SOCK_RAW, CAN_RAW); if(0 > socket_fd) { throw std::runtime_error("socket error!");//std::system_error } } Socket_Can::~Socket_Can() { close_socket(); } void Socket_Can::close_socket() { ::close(socket_fd);// 关闭套接字 } void Socket_Can::Init() { system(down); //这里用到了上方提到的宏定义/* system(command); // system(CAN_Loop); system(up); /*指定CAN设备*/ strcpy(ifr.ifr_name,CAN_DEV); ret = ioctl(socket_fd,SIOCGIFINDEX, &ifr); can_addr.can_family = AF_CAN; /*填充数据*/ can_addr.can_ifindex = ifr.ifr_ifindex; if(ret < 0) { std::stringstream ss; ss << "Failed to get index for " << CAN_DEV << " device"; throw std::runtime_error(ss.str()); } /* 将can0与套接字进行绑定 */ ret = bind(socket_fd, (struct sockaddr *)&can_addr, sizeof(can_addr)); if(ret < 0) { throw std::runtime_error("bind error!"); } // ifr.ifr_flags &= ~IFF_UP; // // ret = ioctl(socket_fd, SIOCSIFFLAGS, &ifr); // ifr.ifr_flags |= IFF_UP; // 开启接口 // ret = ioctl(socket_fd, SIOCSIFFLAGS, &ifr); // if(ret < 0) // { // throw std::runtime_error("Error bringing up CAN interface"); // } /* 设置过滤规则:不接受任何报文、仅发送数据 */ setsockopt(socket_fd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); // rfilter[0].can_id = 0x60A; // rfilter[0].can_mask = 0x7FF; // rfilter[1].can_id = 0x60B; // rfilter[1].can_mask = 0x7FF; // // 调用 setsockopt 设置过滤规则 // setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter)); } void Socket_Can::send_can_frame(const struct can_frame& xframe) { ret = write(socket_fd,&xframe,sizeof(xframe)); if(ret != sizeof(xframe)) { throw std::runtime_error("CAN_Send error!"); } } void Socket_Can::receive_can_frame( struct can_frame& xframe) { ret = read(socket_fd,&xframe,sizeof(xframe)); // if(ret != sizeof(xframe)) // { // throw std::runtime_error("CAN_Read error!"); // } }
节点测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 #include "rclcpp/rclcpp.hpp" #include "std_msgs/msg/string.hpp" #include "socket_can.h" using namespace std::chrono_literals; class Can_Publisher : public rclcpp::Node { public: Can_Publisher():Node("can_publisher") { try{ socket_can.Init(); } catch(const std::runtime_error& e) { socket_can.close_socket(); } timer_ = this->create_wall_timer(1000ms, std::bind(&Can_Publisher::timer_callback, this)); } private: void timer_callback() { my_frame.can_id = 0x123; my_frame.can_dlc = 8; my_frame.data[0]= 0x10; my_frame.data[1]= 0x20; my_frame.data[2]= 0x30; my_frame.data[3]= 0x40; my_frame.data[4]= 0x50; my_frame.data[5]= 0x60; my_frame.data[6]= 0x70; my_frame.data[7]= 0x80; socket_can.send_can_frame(my_frame); } Socket_Can socket_can; struct can_frame my_frame; // rclcpp::Publisher<ImuMsg>::SharedPtr can_publisher_; rclcpp::TimerBase::SharedPtr timer_; }; int main(int argc, char *argv[]) { rclcpp::init(argc, argv); rclcpp::spin(std::make_shared<Can_Publisher>()); rclcpp::shutdown(); return 0; }
工程概述 本例程中使用 “can0”
CAN波特率设置为10000000(1M)
功能: 通过调用以socket的API封装的函数,完成对CAN接口的调用,并在ROS2的节点中完成了对应的CAN通讯;