在 C++ 中使用套接字进行快速 CAN 通信
Fast CAN communication using sockets in C++
我正在尝试使用 C/C++ 与 CAN 总线通信。我正在使用套接字在总线上读写。我创建了一个写线程和一个读线程。读取线程不断尝试读取套接字,当写入请求到达时,写入线程使用互斥锁控制套接字并执行写入。
我在使用这种方法时遇到速度问题,因为写请求有时需要 500 毫秒才能完成(这对我的应用程序来说是完全不可行的)。我试图在读取命令上设置超时,以使其在总线上没有任何内容时不阻塞,但如果超时时间太短,读取就会出现可靠性问题。另一方面,如果我做的太长,提速就不够了。
这是我第一次使用CAN。您对在 C/C++ 中实现快速双向 CAN 通信节点有什么建议吗?我应该使用哪个库来连接总线本身?哪种架构会产生最低的读写延迟?
为应用程序提供一些指标,总线比特率为 1MBits/sec,我使用 CAN-Open 和 64 位数据包(每条消息包含 32 位索引和 32 位数据)。我想要一个从 300 到 500hz 的写入频率,读取频率也是如此。
非常感谢您的帮助!
编辑:
非常感谢您的所有评论。下面就我的申请和问题做一些说明。
我正在做一个移动机器人项目,我正在使用 CAN-Open 与电机驱动器和传感器进行通信。该代码将 运行 在 Raspberry Pi CM4 运行ning Raspbian OS 上安装在集成了 MCP2515 CAN 控制器的自定义 IO 板上。我想在 ROS 架构和 CAN 总线之间实现快速通信接口。使用的语言可以是 C 或 C++。
我目前使用的是基于标准C 套接字的自制接口,但速度很慢,是机器人性能的一大瓶颈。所以我正在寻找更好的解决方案:
- 为此目的构建的开源库
- 实施此类程序的架构建议
- 两者的结合
这是我使用的套接字创建、读取和写入函数。在不同线程的 while 循环中分别调用读取和写入(我正在使用 pthread):
bool connectCanBus(int* socketIDOut, std::string canInterfaceName){
// Socket and can variables
struct sockaddr_can addr;
struct ifreq ifr;
// Openning the socket to send commands over the can bus
if ((*socketIDOut = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
perror("[Can Controler] Unable to create socket.");
return false;
}
strcpy(ifr.ifr_name, canInterfaceName.c_str());
ioctl(*socketIDOut, SIOCGIFINDEX, &ifr);
memset(&addr, 0, sizeof(addr));
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
// Setting option to gte errors as frames
can_err_mask_t err_mask = 0xFFFFFFFF;
setsockopt(*socketIDOut, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &err_mask, sizeof(err_mask));
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 10000;
setsockopt(*socketIDOut, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
// Binding Socket
if (bind(*socketIDOut, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
perror("[Can Controler] Unable to bind socket.");
close(*socketIDOut);
return false;
}
ROS_INFO("CAN bus connected.");
return true;
}
int sendCommand(const char* id, const char* message, int socket, std::mutex& mutex)
{
struct canfd_frame frame;
int err = parseFrame(id, message, &frame);
if(err == 0){
ROS_ERROR_STREAM("[Can Utils] Unable to parse frame : " << id << ", " << message);
return 0;
}
mutex.lock();
int res = write(socket, &frame, sizeof(struct can_frame));
mutex.unlock();
if (res != sizeof(struct can_frame)) {
perror("[Can Utils] CAN bus Write error");
return 0;
}
return 1;
}
int readBus(CanFrame *outFrame, int socketID, std::mutex& mutex)
{
struct can_frame frame;
// Reading on bus
mutex.lock();
int nbytes = read(socketID, &frame, sizeof(struct can_frame));
mutex.unlock();
if (nbytes < 0) {
perror("[Can Utils] CAN bus Read error");
return 0;
}
// Converting frame to strings
sprint_canframe(outFrame, &frame);
return nbytes;
}
我希望这能让问题更清楚、更有针对性。
感谢所有对问题的评论,我才得以解决速度问题。我不知道 SocketCan 支持同一总线上的多个套接字。能够为每个进程创建用于读写的套接字,以分布式方式减少与总线的通信,从而大大提高速度。
我正在尝试使用 C/C++ 与 CAN 总线通信。我正在使用套接字在总线上读写。我创建了一个写线程和一个读线程。读取线程不断尝试读取套接字,当写入请求到达时,写入线程使用互斥锁控制套接字并执行写入。
我在使用这种方法时遇到速度问题,因为写请求有时需要 500 毫秒才能完成(这对我的应用程序来说是完全不可行的)。我试图在读取命令上设置超时,以使其在总线上没有任何内容时不阻塞,但如果超时时间太短,读取就会出现可靠性问题。另一方面,如果我做的太长,提速就不够了。
这是我第一次使用CAN。您对在 C/C++ 中实现快速双向 CAN 通信节点有什么建议吗?我应该使用哪个库来连接总线本身?哪种架构会产生最低的读写延迟?
为应用程序提供一些指标,总线比特率为 1MBits/sec,我使用 CAN-Open 和 64 位数据包(每条消息包含 32 位索引和 32 位数据)。我想要一个从 300 到 500hz 的写入频率,读取频率也是如此。
非常感谢您的帮助!
编辑:
非常感谢您的所有评论。下面就我的申请和问题做一些说明。
我正在做一个移动机器人项目,我正在使用 CAN-Open 与电机驱动器和传感器进行通信。该代码将 运行 在 Raspberry Pi CM4 运行ning Raspbian OS 上安装在集成了 MCP2515 CAN 控制器的自定义 IO 板上。我想在 ROS 架构和 CAN 总线之间实现快速通信接口。使用的语言可以是 C 或 C++。
我目前使用的是基于标准C 套接字的自制接口,但速度很慢,是机器人性能的一大瓶颈。所以我正在寻找更好的解决方案:
- 为此目的构建的开源库
- 实施此类程序的架构建议
- 两者的结合
这是我使用的套接字创建、读取和写入函数。在不同线程的 while 循环中分别调用读取和写入(我正在使用 pthread):
bool connectCanBus(int* socketIDOut, std::string canInterfaceName){
// Socket and can variables
struct sockaddr_can addr;
struct ifreq ifr;
// Openning the socket to send commands over the can bus
if ((*socketIDOut = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
perror("[Can Controler] Unable to create socket.");
return false;
}
strcpy(ifr.ifr_name, canInterfaceName.c_str());
ioctl(*socketIDOut, SIOCGIFINDEX, &ifr);
memset(&addr, 0, sizeof(addr));
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
// Setting option to gte errors as frames
can_err_mask_t err_mask = 0xFFFFFFFF;
setsockopt(*socketIDOut, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &err_mask, sizeof(err_mask));
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 10000;
setsockopt(*socketIDOut, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
// Binding Socket
if (bind(*socketIDOut, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
perror("[Can Controler] Unable to bind socket.");
close(*socketIDOut);
return false;
}
ROS_INFO("CAN bus connected.");
return true;
}
int sendCommand(const char* id, const char* message, int socket, std::mutex& mutex)
{
struct canfd_frame frame;
int err = parseFrame(id, message, &frame);
if(err == 0){
ROS_ERROR_STREAM("[Can Utils] Unable to parse frame : " << id << ", " << message);
return 0;
}
mutex.lock();
int res = write(socket, &frame, sizeof(struct can_frame));
mutex.unlock();
if (res != sizeof(struct can_frame)) {
perror("[Can Utils] CAN bus Write error");
return 0;
}
return 1;
}
int readBus(CanFrame *outFrame, int socketID, std::mutex& mutex)
{
struct can_frame frame;
// Reading on bus
mutex.lock();
int nbytes = read(socketID, &frame, sizeof(struct can_frame));
mutex.unlock();
if (nbytes < 0) {
perror("[Can Utils] CAN bus Read error");
return 0;
}
// Converting frame to strings
sprint_canframe(outFrame, &frame);
return nbytes;
}
我希望这能让问题更清楚、更有针对性。
感谢所有对问题的评论,我才得以解决速度问题。我不知道 SocketCan 支持同一总线上的多个套接字。能够为每个进程创建用于读写的套接字,以分布式方式减少与总线的通信,从而大大提高速度。