七、ROS-CAN通信

1. 测试官方样例

我这里用的是CANalyst-II分析仪(创芯科技) Linux版
我的系统是:Ubuntu18.04.6 LTS
我的ROS版本:melodic
在这里插入图片描述

这个CAN设备是不用装驱动的,直接在终端输入lsusb就能查到CAN设备ID:04d8:0053 Microchip Technology, Inc.

lsusb

在这里插入图片描述

官方提供的Linux资料包里面就有二次开发样例
最重要的是libcontrolcan.so是动态链接库,调用库中的函数需要用到controlcan.h
在这里插入图片描述
将这4个文件复制到一个不含中文路径的目录下,我这里是复制到can_test这个文件夹里
在这里插入图片描述
修改main.cpp的内容:读取CAN1通道的数据(遥控器发给CAN1),并打印到终端

//功能:接收从CAN1发送过来的数据,并打印到终端



#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include "controlcan.h"

#include <ctime>
#include <cstdlib>
#include "unistd.h"


main()
{

	printf(">>this is mytestdemo !\r\n");//指示程序已运行

	if(VCI_OpenDevice(VCI_USBCAN2,0,0)==1)//打开设备
	{
		printf(">>open deivce success!\n");//打开设备成功
	}else
	{
		printf(">>open deivce error!\n");
		exit(1);
	}

	//配置CAN
	VCI_INIT_CONFIG config; 
	config.AccCode = 0x80000000;
	config.AccMask = 0xFFFFFFFF;
	config.Filter = 2;   //只接收标准帧
	config.Timing0 = 0x01; 
	config.Timing1 = 0x1C; //波特率定为250kps  0x01 0x1C
	config.Mode = 0; //正常模式		

	//初始化CAN1:VCI_InitCAN(设备类型、设备索引、CAN通道索引、CAN配置参数)
	if(VCI_InitCAN(VCI_USBCAN2,0,0,&config)!=1)
	{
		printf(">>Init CAN1 error\n");
		VCI_CloseDevice(VCI_USBCAN2,0);
	}

	if(VCI_StartCAN(VCI_USBCAN2,0,0)!=1)
	{
		printf(">>Start CAN1 error\n");
		VCI_CloseDevice(VCI_USBCAN2,0);

	}

	//初始化用来接收的数据帧,帧结构体数组的长度设置为50
    VCI_CAN_OBJ rec[50];
	int reclen=0;
	int i,j;


	while(1)
	{

		//VCI_InitCAN(设备类型、设备索引、CAN通道索引0就是CAN1、接收数组的首指针rec、接收数组的长度50、保留参数)	
		if((reclen = VCI_Receive(VCI_USBCAN2,0,0,rec,50,200)) >= 0)//调用接收函数,如果有数据,则进行处理
		{
			for(i=0; i<reclen; i++)
			{
				//打印数据
				printf("data:0x");	
				for( j= 0; j < rec[i].DataLen; j++ )
				{
					printf(" %02X", rec[i].Data[j]); //%02X表示用2个位置数据一个16进制数
				}
				printf("\n");
			}
		}
	}
}

然后将libcontrolcan.so动态链接库的绝对路径添加到Makefile中
就可以将这个main.cpp编译成可执行文件test2333,可执行文件名可以自定义

all:

	g++ -o test2333 main.cpp /home/yao/My_Ros_WorkSpace/can_test/libcontrolcan.so  -lpthread 

clean:
	rm -f *.o

最后在终端中编译和执行即可

lsusb
cd /home/yao/My_Ros_WorkSpace/can_test
rm test2333
make clean && make
sudo ./test2333

在这里插入图片描述
遥控器↑:02 01 00 00 00 00 00 00
遥控器↓:02 02 00 00 00 00 00 00
遥控器←:02 04 00 00 00 00 00 00
遥控器→:02 08 00 00 00 00 00 00

OK,到这里说明我们的设备和程序都是可以正常工作的,没有问题!

特别注意:CAN1口旁边的2个120Ω上拉电阻,必须保证至少一个是拨到ON档
否则没办法接收CAN1口发送过来的数据
在这里插入图片描述

2. 移植到ROS

接下来,将这部分代码移植到ROS系统下

cd ~/My_Ros_WorkSpace
mkdir can_workspace
cd  can_workspace
mkdir src
cd src
catkin_init_workspace
cd ..
catkin_make
catkin_make install
code .
  • 然后创建功能包
    选中src,右键Create Catkin Package
    录入功能包名字"can_pkg",回车
    录入依赖"roscpp rospy std_msgs",再回车

  • 创建节点:读取CAN1的数据
    选中can_pkg→src,右键新建一个发布者的实现代码mycan_pub.cpp
    修改 .vscode/c_cpp_properties.json,设置 "cppStandard"为 “c++17”并保存,否则会报错

重中之重:
因为要用到libcontrolcan.so和controlcan.h,所以要将这两个文件添加到这个工作空间下

controlcan.h:

  1. 先放到can_workspace/src/can_pkg/include/can_pkg目录下
    在这里插入图片描述

  2. 然后配置includePath路径,在c_cpp_properties.json文件中添加controlcan.h所在的绝对路径 “/home/yao/My_Ros_WorkSpace/can_workspace/src/can_pkg/include/**”
    在这里插入图片描述

  3. 然后就可以在mycan_pub.cpp中调用这个头文件了,注意引用头文件的写法,需要将头文件的相对路径加进来,否则会报错,应该这样写:#include “can_pkg/controlcan.h”

  4. 这样就可以调用controlcan.h中声明的函数编写代码了:

#include <ros/ros.h>
#include <std_msgs/String.h>

#include "can_pkg/controlcan.h"

using namespace std; //声明命名空间

int main(int argc, char** argv)
{
    //初始化,节点名为can_publisher
    ros::init(argc, argv,"can_publisher");

	if(VCI_OpenDevice(VCI_USBCAN2,0,0)==1)//打开设备
	{
        ROS_INFO_STREAM(">>open deivce success!");//打开设备成功
	}else
	{
        ROS_INFO_STREAM(">>open deivce error!");//打开设备成功
		exit(1);
	}

	//配置CAN
	VCI_INIT_CONFIG config; 
	config.AccCode = 0x80000000;
	config.AccMask = 0xFFFFFFFF;
	config.Filter = 2;   //只接收标准帧
	config.Timing0 = 0x01; 
	config.Timing1 = 0x1C; //波特率定为250kps  0x01 0x1C
	config.Mode = 0; //正常模式		

	//初始化CAN1:VCI_InitCAN(设备类型、设备索引、CAN通道索引、CAN配置参数)
	if(VCI_InitCAN(VCI_USBCAN2,0,0,&config)!=1)
	{
        ROS_INFO_STREAM(">>Init CAN1 error");
		VCI_CloseDevice(VCI_USBCAN2,0);
	}

	if(VCI_StartCAN(VCI_USBCAN2,0,0)!=1)
	{
        ROS_INFO_STREAM(">>Start CAN1 error");
		VCI_CloseDevice(VCI_USBCAN2,0);
	}

	//初始化用来接收的数据帧,帧结构体数组的长度设置为50
    VCI_CAN_OBJ rec[50];
	int reclen=0;
	int i,j;


	while(1)
	{

		//VCI_InitCAN(设备类型、设备索引、CAN通道索引0就是CAN1、接收数组的首指针rec、接收数组的长度50、保留参数)	
		if((reclen = VCI_Receive(VCI_USBCAN2,0,0,rec,50,200)) >= 0)//调用接收函数,如果有数据,则进行处理
		{
			//顺便将接收到的数据打印到终端
			for(i=0; i < reclen; i++)
			{
                printf("data:0x");
				for( j= 0; j < rec[i].DataLen; j++ )
				{
                    printf(" %02X", rec[i].Data[j]); //%02X表示用2个位置数据一个16进制数
				}
				printf("\n");
			}
		}
	}
    return 0;
}

libcontrolcan.so:

  1. 在功能包can_pkg目录下创建一个lib文件夹
  2. 将libcontrolcan.so复制到刚刚新建的lib文件夹下面
    在这里插入图片描述
  • 配置CMakeLists.txt
  1. 解除#include的注释:可以让编译器去include下找到can_pkg/controlcan.h
## 解除#include的注释:去找头文件
include_directories(
include
  ${catkin_INCLUDE_DIRS}
)
  1. 指定动态链接库libcontrolcan.so的访问路径
## 手动添加link_directories():指定动态链接库的访问路径为功能包can_pkg下的lib
link_directories(
  lib
  ${catkin_LIB_DIRS}
)
  1. 最后就是指定生成可执行文件和链接库
# 生成可执行文件
add_executable(mycan_pub src/mycan_pub.cpp)
# 链接库
target_link_libraries(mycan_pub  
  ${catkin_LIBRARIES}
  controlcan #链接动态链接库libcontrolcan.so,但是要注意这里写的时候仅保留创建时所用到的名称,所以要去掉前缀lib和后缀.so
  )
  • 编译执行
    Ctrl + S保存,Ctrl + Shift +B编译
  1. 终端1
roscore
  1. 终端2
source devel/setup.bash
rosrun can_pkg mycan_pub

报错:error setting config #1: could not set config 1: Operation not permitted
原因:这是因为Linux系统下将涉及到usb底层驱动的调用,运行时,一定要加sudo获取权限运行,否则USB
设备没有权限操作,比如之前运行那个用gcc编译的可执行文件test2333时,我也是加了sudo的

sudo ./test2333

但是用rosrun can_pkg mycan_pub启动节点的时候,貌似没办法加sudo,所以需要配置USB权限:

方法一:

  1. 创建一个新的 udev 规则,名称取为:99-myusb.rules
    注意:数字 99 最好不要改动,否则可能设置失败,而且要加 sudo
sudo vi /etc/udev/rules.d/99-myusb.rules
  1. 把以下两行代码复制到新建的 99-myusb.rules 文件中
    注意:按键盘上 Insert 键切换到“代替”输入模式,然后粘贴
##
ACTION=="add",SUBSYSTEMS=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="0053",
GROUP="users", MODE="0777"
  1. 按一次“Esc”键
  2. 直接输入“:wq”回车,即保存退出
  3. 插拔一下 USBCAN 设备或重启一下电脑
  4. 就可以不加 sudo 权限运行程序了,或者ROS下用rosrun启动节点也不会报错了

方法二:

cd /etc/udev/rules.d
sudo touch 99-myusb.rules
sudo gedit 99-myusb.rules
## 将下面这两句代码复制到文件中
ACTION=="add",SUBSYSTEMS=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="0053",
GROUP="users", MODE="0777"
## 重启电脑即可生效
  • 升级节点:读取完CAN1的数据,根据遥控器发过来的数据,将控制指令输出到CAN2,以便后续的控制操作
  1. 硬件连接:CANalyst-II的CAN1连接遥控器、CAN2可以再外接一个CANalyst-II并连到Windows系统,这样就可以将CAN2输出的控制指令在Windows系统上的USB_CAN TOOL调试软件上显示出来。注意:所有CAN口的上拉电阻都需要拨到ON档
  2. 代码如下:
#include <ros/ros.h>
#include <std_msgs/String.h>

#include "can_pkg/controlcan.h"

using namespace std; //声明命名空间

//初始化启动CAN1并启动
bool Init_CAN1(int nDeviceType, int nDeviceInd, int nCANInd, VCI_INIT_CONFIG config) {
	VCI_ResetCAN(nDeviceType, nDeviceInd, nCANInd);
    if (VCI_InitCAN(nDeviceType, nDeviceInd, nCANInd, &config) != 1)
    {
        VCI_CloseDevice(nDeviceType, nDeviceInd);
        ROS_INFO_STREAM(">>Init CAN1 error");
        return(0);
    }
    VCI_ClearBuffer(nDeviceType, nDeviceInd, nCANInd);
    if (VCI_StartCAN(nDeviceType, nDeviceInd, nCANInd) != 1)
    {
        VCI_CloseDevice(nDeviceType, nDeviceInd);
        ROS_INFO_STREAM(">>Start CAN1 error");
        return(0);
    }
	else
	{
    	ROS_INFO_STREAM(">>Start CAN1 success");
    	return(1);
	}
}

//初始化启动CAN2并启动
bool Init_CAN2(int nDeviceType, int nDeviceInd, int nCANInd, VCI_INIT_CONFIG config) {
	VCI_ResetCAN(nDeviceType, nDeviceInd, nCANInd);
    if (VCI_InitCAN(nDeviceType, nDeviceInd, nCANInd, &config) != 1)
    {
        VCI_CloseDevice(nDeviceType, nDeviceInd);
        ROS_INFO_STREAM(">>Init CAN2 error");
        return(0);
    }
    VCI_ClearBuffer(nDeviceType, nDeviceInd, nCANInd);
    if (VCI_StartCAN(nDeviceType, nDeviceInd, nCANInd) != 1)
    {
        VCI_CloseDevice(nDeviceType, nDeviceInd);
        ROS_INFO_STREAM(">>Start CAN2 error");
        return(0);
    }
	else
	{
    	ROS_INFO_STREAM(">>Start CAN2 success");
    	return(1);
	}
}


int main(int argc, char** argv)
{
    //初始化,节点名为can_publisher
    ros::init(argc, argv,"can_publisher");

	//根据遥控器指令发送相应的控制指令
	const unsigned char a0[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; //默认
    const unsigned char a1[] = { 0xF4,0x01,0x00,0x00,0x00,0x00,0x00,0x00 }; //↑
    const unsigned char a2[] = { 0x00,0x00,0xF4,0x01,0x00,0x00,0x00,0x00 }; //↓
    const unsigned char a3[] = { 0x00,0x00,0x00,0x00,0xF4,0x01,0x00,0x00 }; //←
    const unsigned char a4[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0xF4,0x01 }; //→

	//打开和初始化设备的一些参数配置
	int nDeviceType = 4; //设备类型:CANalyst-II就用4
    int nDeviceInd = 0; //设备索引:1个USB-CAN适配器就是0
    int nCANInd0 = 0;//CAN1
    int nCANInd1 = 1;//CAN2

    //打开设备:注意一个设备只能打开一次
	if(VCI_OpenDevice(nDeviceType,nDeviceInd,0)==1)
	{
        ROS_INFO_STREAM(">>open deivce success!");//打开设备成功
	}else
	{
        ROS_INFO_STREAM(">>open deivce error!");//打开设备失败
		exit(1); //退出整个程序,终止进程:返回1给操作系统说明是非正常运行导致程序退出
	}

	//配置CAN
	VCI_INIT_CONFIG config; 
	config.AccCode = 0x80000000;
	config.AccMask = 0xFFFFFFFF;
	config.Filter = 2;   //只接收标准帧
	config.Timing0 = 0x01; 
	config.Timing1 = 0x1C; //波特率定为250kps  0x01 0x1C
	config.Mode = 0; //正常模式		

	
	//初始化用来接收的数据帧,帧结构体数组的长度设置为50
	int num = 50;
    VCI_CAN_OBJ rec[num];
	int reclen = 0;
	int i,j,k, order;

	//初始化用来发送的数据帧,帧结构体数组的长度设置为1
	VCI_CAN_OBJ send[1];
	send[0].ID = 0; //帧ID
	send[0].SendType = 0; //发送帧类型:0为正常发送
	send[0].RemoteFlag = 0; //0为数据帧,1为远程帧
	send[0].ExternFlag = 0; //0为标准帧,1为拓展帧
	send[0].DataLen = 8; //数据长度8字节

    //CAN1和CAN2双通道初始化(设备类型、设备索引、CAN通道索引、CAN配置参数)
    if (Init_CAN1(nDeviceType, nDeviceInd, nCANInd0, config) && Init_CAN2(nDeviceType, nDeviceInd, nCANInd1, config))
	{
		while(1)
		{
			//延时20ms:在满足应用的时效性情况下,尽量降低调用VCI_Receive频率,每隔30ms调用一次VCI_Receive为宜
			usleep(20000);

			//VCI_Receive(设备类型、设备索引、CAN通道索引0就是CAN1、接收数组的首指针rec、接收数组的长度50、保留参数)	
			if((reclen = VCI_Receive(nDeviceType,nDeviceInd,nCANInd0,rec,num,200)) >= 0)//调用接收函数,如果有数据,则进行处理
			{

				for(i=0; i < reclen; i++)
				{
					//接收到的遥控器指令
					order = (int)rec[i].Data[1] + (int)rec[i].Data[2];

					//先将接收到的遥控器指令打印到终端
					printf("data:0x");
					for( j= 0; j < rec[i].DataLen; j++ )
					{
						printf(" %02X", rec[i].Data[j]); //%02X表示用2个位置数据一个16进制数
					}
					printf("\n");

					//然后根据遥控器的指令,让CAN2发布相应的控制指令
					switch (order) 
					{
						case 0: //默认
						{
							for (k = 0; k < 8; k++)
							{
								send[0].Data[k] = a0[k];
							}
							VCI_Transmit(nDeviceType, nDeviceInd, nCANInd1, send, 1);
							break;
						}
						case 1: //↑
						{
							for (k = 0; k < 8; k++)
							{
								send[0].Data[k] = a1[k];
							}
							VCI_Transmit(nDeviceType, nDeviceInd, nCANInd1, send, 1);
							break;
						}
						case 2: //↓
						{
							for (k = 0; k < 8; k++)
							{
								send[0].Data[k] = a2[k];
							}
							VCI_Transmit(nDeviceType, nDeviceInd, nCANInd1, send, 1);
							break;
						}
						case 4: //←
						{
							for (k = 0; k < 8; k++)
							{
								send[0].Data[k] = a3[k];
							}
							VCI_Transmit(nDeviceType, nDeviceInd, nCANInd1, send, 1);
							break;
						}
						case 8: //→
						{
							for (k = 0; k < 8; k++)
							{
								send[0].Data[k] = a4[k];
							}
							VCI_Transmit(nDeviceType, nDeviceInd, nCANInd1, send, 1);
							break;
						}
						default: //检查和处理错误情况
						{
							for (k = 0; k < 8; k++)
							{
								send[0].Data[k] = a0[k];
							}
							VCI_Transmit(nDeviceType, nDeviceInd, nCANInd1, send, 1);
							break;
						}
					}

				}
			}
		}
	}
    return 0;
}
// roscore
// source devel/setup.bash
// rosrun can_pkg mycan_pub
### 回答1: ros-melodic-can-msgs 是一个 ROS 包,用于在 ROS 中使用和操作 Controller Area Network (CAN) 协议进行通信。其中包含了相关的消息和服务类型,用于在 ROS 系统中传输 CAN 相关的数据。你可以在这里找到 ros-melodic-can-msgs 的源代码:https://github.com/ros-drivers/can_msgs ros-melodic-can-msgs 是针对 ROS Melodic 版本开发的,Melodic 是 ROS 的一个发行版本。如果你正在使用其他版本的 ROS,你需要使用对应版本的 ros-can-msgs 包。 ### 回答2: ros-melodic-can-msgs是ROS(机器人操作系统)的一个软件包,它是用于CAN(控制器局域网络)通信的消息定义和工具。CAN是一种常用于车辆和其他实时控制系统中的网络通信协议。 这个软件包包含了一些重要的消息类型,用于在ROS中进行CAN数据的传输和解析。例如,它定义了CAN消息的ID、数据格式和长度等信息。这些消息类型可以让ROS系统和连接在CAN总线上的设备进行通信。 在ros-melodic-can-msgs源码中,我们可以看到定义了各种用于CAN通信的消息类型,比如CAN_Frame、CAN_FD_Frame和CAN_Error等。这些消息类型可以用于接收和发送CAN数据,并且还包括了一些附加的属性,比如时间戳等。 此外,源码中还提供了一些工具和函数,用于CAN数据的解析和处理。例如,它提供了一个解析CAN帧的函数,可以从CAN消息中提取数据和控制信息。同时,还可以将ROS中的消息转换为CAN消息,并将CAN消息转换为ROS消息,方便在ROS系统中使用CAN数据。 总之,ros-melodic-can-msgs是一个用于CAN通信ROS软件包,提供了消息定义和工具,用于在ROS系统中处理和交换CAN数据。通过使用这个软件包,开发人员可以方便地使用ROS构建与CAN设备之间的通信,实现车辆控制和实时系统的开发。 ### 回答3: ros-melodic-can-msgs是ROS的一个软件包,用于处理Controller Area Network(CAN)通信协议的消息。这个软件包提供了在ROS系统中处理CAN消息的功能。 CAN是一种广泛应用于汽车、工业控制和机器人等领域的通信协议,它允许不同的设备通过总线共享数据。ros-melodic-can-msgs软件包的目的是为ROS系统添加对CAN通信的支持。 这个软件包包含了一系列的消息类型,用于定义在ROS系统中传输CAN消息。这些消息类型包括CAN帧消息类型、CAN控制器状态消息类型以及一些其他辅助消息类型。通过这些消息类型,ROS系统可以接收和发送CAN消息,实现与CAN总线上其他设备的通信。 除了消息类型,ros-melodic-can-msgs还提供了一些辅助工具和函数,用于处理CAN消息。这些工具和函数可以用于解析CAN帧消息、生成CAN帧消息以及读取和修改CAN控制器的状态。 使用ros-melodic-can-msgs,开发者可以将CAN通信集成到ROS系统中。他们可以通过订阅CAN消息话题来接收其他设备发送的CAN消息,并可以通过发布CAN消息话题来向其他设备发送CAN消息。这样,开发者可以方便地与CAN总线上的其他设备进行数据交换,并能够利用ROS系统提供的各种功能进行数据处理和分析。 总之,ros-melodic-can-msgs是一个在ROS系统中处理CAN通信的软件包,它提供了一系列的消息类型、工具和函数,使得开发者可以方便地与CAN总线上的设备进行数据交换和通信

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值