相机和毫米波雷达数据融合1–流程框架

相机和毫米波雷达数据融合2–SocketCan编写

相机和毫米波雷达数据融合3–Simulink解析Can信号

参考文章

linux下的SocketCan编写
Linux socket CAN编程示例

工控机Can接口激活

工控机的can卡需要激活,一般厂家会有执行文件,你每次开机然后执行这个文件就好了。也可以把这个文件设置成开机自动执行,这样就不要每次都运行了。

在这里插入图片描述
里面的内容没有太大借鉴意义,不同厂家写的不一样。运行成功之后可以通过ifconfig命令查看是否出现了相应的can口注意这里面的can口命名(can0,can1)很重要,后面的SocketCan代码中的名字和这里的一致

算了还是看看这个**.sh**文件里写了什么吧。

在这里插入图片描述
重点就是设置的语句“set up”等等,你会看到参考文章链接中的代码前面会有类似的设置语句,这时你就知道我的**Can_Init()**里面为什么注释掉这几行设置代码了。总结就是别重复激活,一个位置激活了,另外就没必要再次激活了。

在这里插入图片描述
Canutils,这是linux下一个查看can报文的软件,你可以在你的linux里下一个,网上教程很多。用candump命令可以很好地查看指定can口传输的报文。

项目SocketCan编写

本人对c++基础不好,在编写的时候对相应类的编写可能不合适,希望大家一起交流,指导。
在这里插入图片描述
里面的错误可以忽略,因为这是linux下的代码,但是为了方便写文章我在windows下打开了。
箭头所指的两个文件用来实现SocketCan功能。

头文件 can_vehicle_class.h

#ifndef CAN_VEHICLE_CLASS_H
#define CAN_VEHICLE_CLASS_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <vector>

using namespace std;

class CanVehicleClass
{
private:
    int Can_Init();


public:
    CanVehicleClass(char* name);
    char* can_name;//绑定的can口
    int s;//socketcan套接字

    unsigned char CAN_Receive_Msg(can_frame& receive_msg,vector<unsigned int> &receive_id);
    int Can_Send_Msg(vector<can_frame>& send_msg_vec);

    vector<can_frame> send_msg_vec;//打算发送的报文
    vector<unsigned int> receive_id_vec;//接受过滤的报文id
    can_frame receive_msg;//接收到的报文
};


#endif

这个头文件声明了一个CanVehicleClass类,类中声明了构造函数can初始化函数can报文发送函数can报文接收函数指定的can口名称想要过滤的报文id想要发送的报文本身接收到的报文本身

源文件 can_vehicle_class.cpp

在这里插入图片描述
源文件中对四个函数进行定义,最上面的是CanvehicleClass类的构造函数。
下面来看看这几个函数。

构造函数

CanVehicleClass::CanVehicleClass(char* name)
{
    can_name=name;
    printf("%s","In class constructor of CanVehicleClass.");
    Can_Init();
}

构造函数在创建类的时候会自己运行,所以我把can口名字定义,和Can_Init()函数都写在里面了。

Can_Init()

int CanVehicleClass::Can_Init()
{
    int ret;
    struct sockaddr_can addr;
    struct ifreq ifr;
    // system("sudo ip link set can0 type vcan bitrate 100000");
    // system("sudo ifconfig can0 up");
    // printf("this is a can send demo\r\n");
//1.Create socket
    s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    if (s < 0)
    {
        perror("socket PF_CAN failed");
        return -1;
    }
//2.Specify can0 device
    strcpy(ifr.ifr_name, can_name);//这里写入can口名字,我的是can_name
    ret = ioctl(s, SIOCGIFINDEX, &ifr);
    if (ret < 0) 
    {
        perror("ioctl failed");
        return -1;
    }
//3.Bind the socket to can0
    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    ret = bind(s, (struct sockaddr *)&addr, sizeof(addr));
    if (ret < 0) 
    {
        perror("bind failed");
        return -1;
    }
//4.Disable filtering rules, do not receive packets, only send
    setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);  
//5.return socket
    return s;
}

这个代码我也不太懂,但是不需要怎么改,注意在里面指定can口就好了。想要了解相关知识可以自己去查或者看文章开头的参考文章。

CAN_Receive_Msg(parameters)

unsigned char CanVehicleClass::CAN_Receive_Msg(can_frame &receive_msg,vector<unsigned int> &receive_id_vec)
{
    //Define receive rules
	struct can_filter rfilter[receive_id_vec.size()];
    for(int i=0;i<receive_id_vec.size();++i)
    {
        rfilter[i].can_id = receive_id_vec[i];
        rfilter[i].can_mask = CAN_SFF_MASK;
    }
	setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));

	int nbytes=0;


    nbytes = read(s, &receive_msg, sizeof(receive_msg));
    if(nbytes > 0)
    {
        return receive_msg.can_id;
    }
    if(nbytes == -1)
    {
        printf("%s","receive error msg");
        close(s);
        return -1;
    }
}

传入的参数:receive_msg用来存储read函数在总线上读到的can报文。receive_id_vec是你打算获取的报文id列表。这个函数运行之后,receive_msg就被赋值了,这是CanVehicleClass类的成员数据。

Can_Send_Msg(parameters)

int CanVehicleClass::Can_Send_Msg(vector<can_frame>& send_msg_vec)
{
    int nbytes;
    //  struct can_frame frame;
    //  frame.can_id  = send_id;
    //  frame.can_dlc = send_len;
    //  for(int i=0;i<send_len;i++)
    //  frame.data[i]=send_msg[i];
//Send message

    for(int i = 0; i < send_msg_vec.size(); ++i)
    {
        nbytes = write(this->s, &send_msg_vec[i], sizeof(send_msg_vec[i]));
        if(nbytes != sizeof(send_msg_vec[i]))
        {
        printf("%s,%s","send error frame ",strerror(errno));
        close(this->s);
        return -1;
        }
    }
    return nbytes;
}

传入的参数:你想要发送的报文列表send_msg_vec是一个存储了很多你这个周期想要发送的报文的vector,这个函数的关键在于用write函数将vector中的can_frame们发送出去。

总结

至此,SocketCan的编写内容就讲完了,代码中有一些不太需要的我没有删除,遇到不懂的可以百度,或者查看参考文章链接。这篇文章主要是完成了具有SocketCan功能类的编写,这样可以方便在之后的程序进行调用。 写完这些文件之后你可以在can_vehicle_class.cpp中继续编写一个main()函数来测试编写的代码是否正确,或者是重新写一个文件,但是需要注意头文件的包含,还有多源文件编译的命令。(去百度linux的编译命令行)

大概就是这样的一段代码吧,我没跑过如果编译有错看看是不是多源文件编译没弄好,头文件没弄好之类的。如果生成了可执行文件,你可有如下选择进行测试:

  • 利用canutils,这是最简单的测试方式。首先利用candump can0,来监听can0口的报文,然后运行可执行文件,会发现窗口出现相应的报文,说明发送报文没问题。然后用cansend 发送0x17 0x18的报文,代码窗口如果只显示0x17的id说明接收没问题。
  • 用usbcan,用usbcan连接你的电脑和工控机的can口,然后用相应的测试软件TSmaster来发送和接收can,与上文类似。
int main()
{

    CanVehicleClass test_can("can0");
    for (int i = 0; i < 10; ++i)
    {
        can_frame temp;
        temp.can_id=0x16;

        temp.can_dlc=8;
        for(int j=0;j<temp.can_dlc;++j)
        {
            temp.data[j]=i;
        }
        test_can.send_msg_vec.push_back(temp);
    }
    test_can.send_msg_vec(test_can.send_msg_vec);
    while(1)
    {
        test_can.receive_id_vec = {0x17};
        test_can.CAN_Receive_Msg(rec_msg,test_can.receive_id_vec);
        cout << test_can.receive_msg.can_id << endl;
    }
    return 0;
}

如果代码这里有问题,可以在评论区提出来,最好先看参考文章的内容,百度之后再问,最后的测试代码是我写文章的时候写的没有测试,不知道有没有问题,但是你可以看到,写成类之后,这几行代码就完成需要的功能。

Logo

更多推荐