ros发布和订阅图像的压缩——高效图传(适用带宽不足问题)

ros话题机制默认通过TCP进行传输
因此特别容易堵塞。。。。。。
必须要有compressed的图像, img_raw传输速度非常慢
参考:https://blog.csdn.net/qq_30460905/article/details/107301868?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2allfirst_rank_v2~rank_v25-1-107301868.nonecase&utm_term=ros%E5%8E%8B%E7%BC%A9%E5%9B%BE%E5%83%8F

1 订阅和发布简单的压缩图像(不可调压缩比)

1.1 订阅者压缩图像:

https://answers.ros.org/question/314979/decompress-jpeg-depth-data/

#include <image_transport/image_transport.h>

void saveIMage3(const sensor_msgs::Image::ConstPtr& msg)
{
}

ros::NodeHandle n;
image_transport::ImageTransport it(n);
image_transport::Subscriber itSub = it.subscribe( "usb_cam/image_raw",1,&saveIMage3,image_transport::TransportHints("compressed"));

在qt中要加入LIBS += /opt/ros/melodic/lib/libimage_transport.so 不用qt的忽略这句话。。

这样在ros中就直接产生了此话题:/usb_cam/image_raw/compressed 就是压缩图像

但是这里的回调函数没法使用boost::bind,使用后会报错,只能简单订阅sensor_msgs::Image::ConstPtr这一个参数给回调函数。

1.2 发布者压缩图像:
  ros::NodeHandle nh;
  image_transport::ImageTransport it(nh);
  image_transport::Publisher image_pub;
  image_pub = it.advertise("image_compressed",1);
  image_pub.publish( ... );

其中…代表一个sensor_msgs::Image或者sensor_msgs::Image::ConstPtr
因此如果需要cv::Mat的发布,需要有优先转换为sensor_msgs::Image
具体转换方法:https://blog.csdn.net/weixin_44684139/article/details/108759860

2 rqt_reconfigure设置压缩比

可以用rqt_reconfigure设置压缩比:

rosrun rqt_reconfigure rqt_reconfigure

根据format调节quality或level即可。

注意,这种方法并不是只能使用rqt_image_view进行观测才能改画质,而是直接在ros系统中更改的参数。
在这里插入图片描述

3 利用Opencv提供的imencode和imdecode进行图像视频传输(Linux下)

参考https://blog.csdn.net/qq_37406130/article/details/78820176

3.1 发送端,先订阅图像,然后压缩并发布
#ifdef _WIN32	
#define  _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <WinSock2.h>
#pragma comment(lib,"WS2_32.lib")
#else
#include <unistd.h>//Linux系统下网络通讯的头文件集合
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <fcntl.h>
#endif
#include <iostream>
#include <opencv2/opencv.hpp>  
// #include <opencv2/imgproc/imgproc.hpp> 
#include "ros/ros.h"
#include <sensor_msgs/Image.h>
#include <cv_bridge/cv_bridge.h>
#include "sensor_msgs/CompressedImage.h"
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
// #include <opencv2/imgproc/imgproc.hpp>
// #include "opencv2/opencv.hpp"
#include <image_transport/image_transport.h>   //image_transport

using namespace cv;
using namespace std;

enum
{
	PORT = 0X4321
};
sockaddr_in m_servaddr;
int m_sockClient;

void saveIMage(const sensor_msgs::CompressedImage::ConstPtr& msg)
{

    try
    {

        cv_bridge::CvImagePtr cv_ptr = cv_bridge::toCvCopy(msg,  sensor_msgs::image_encodings::TYPE_8UC3);
    //    std::cout<<"转换成功!!!!!!!!!!!!!!!!!!!!!!!"<<std::endl;
        cv::Mat img__ = cv_ptr->image;
        cv::cvtColor(img__, img__, CV_BGR2RGB);

		// -----------------------------------------------------------------------------------------
		std::vector<uchar> data_encode;
		std::vector<int> quality;
		quality.push_back(CV_IMWRITE_JPEG_QUALITY);
		quality.push_back(50);//进行50%的压缩
		imencode(".jpg", img__, data_encode,quality);//将图像编码
		char encodeImg[65535];

		int nSize = data_encode.size();
		for (int i = 0; i < nSize; i++)
		{
			encodeImg[i] = data_encode[i];
		}
		m_servaddr.sin_addr.s_addr = inet_addr("192.168.43.103");
		m_servaddr.sin_port = htons(0x1234);//设置需要发送的IP和端口号
		sendto(m_sockClient, encodeImg, nSize, 0, (const sockaddr*)& m_servaddr, sizeof(m_servaddr));
		std::cout<<"发送成功"<<std::endl;
		memset(&encodeImg, 0, sizeof(encodeImg));  //初始化结构体
	// -----------------------------------------------------------------------------------------
    }
    catch (cv_bridge::Exception& e)
    {
      ROS_ERROR("cv_bridge exception: %s", e.what());
      return;
    }

}


int main(int argc, char** argv)
{
#ifdef _WIN32
	WSADATA wsaData;
	WSAStartup(0x01, &wsaData); //创建初始化句柄
#endif

	if ((m_sockClient = socket(AF_INET, SOCK_DGRAM, 0)) < 0)    //创建socket句柄,采用UDP协议
	{
		printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
		return -1;
	}
	memset(&m_servaddr, 0, sizeof(m_servaddr));  //初始化结构体
	m_servaddr.sin_family = AF_INET;           //设置通信方式
	m_servaddr.sin_port = htons(PORT);         //设置端口号

	bind(m_sockClient, (sockaddr*)&m_servaddr, sizeof(m_servaddr));//绑定端口号
	// VideoCapture capture(0);//打开摄像头
	// Mat image;

    ros::init(argc, argv, "talker_main");
    ros::NodeHandle n;

   //ros::Subscriber image_sub_ = n.subscribe<sensor_msgs::CompressedImage>("her/usb_cam/image_raw/compressed", 1,saveIMage);
   ros::Subscriber image_sub_ = n.subscribe<sensor_msgs::CompressedImage>("/usb_cam/image_raw/compressed", 1,saveIMage);

	ros::spin();

	return 0;
}

发送端CMakeLists.txt随便写了以下,可能不是特别严格

set( CMAKE_CXX_FLAGS "-std=c++11")

cmake_minimum_required( VERSION 2.8 )
project( send_receive )

set(OpenCV_DIR "usr/local/opencv3/share/OpenCV")

# 寻找OpenCV库
find_package( OpenCV 3 REQUIRED )
# 添加头文件
include_directories( ${OpenCV_INCLUDE_DIRS} )


find_package(catkin REQUIRED COMPONENTS geometry_msgs sensor_msgs std_msgs robot_state_publisher urdf move_base_msgs visualization_msgs cv_bridge  image_transport)

catkin_package(
    CATKIN_DEPENDS robot_state_publisher urdf geometry_msgs sensor_msgs std_msgs move_base_msgs visualization_msgs sensor_msgs cv_bridge image_transport
)

include_directories(
  ${catkin_INCLUDE_DIRS}
)

# 可执行程序
add_executable( send send.cpp )
# 链接OpenCV库
target_link_libraries( send ${OpenCV_LIBS} ${catkin_LIBRARIES})
3.2 接收端,接受图像并显示
#include <iostream>
#include <opencv2/opencv.hpp>  
#include <opencv2/imgproc/imgproc.hpp> 
#include <unistd.h>//Linux系统下网络通讯的头文件集合
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <fcntl.h>

using namespace cv;
using namespace std;

enum
{
	PORT = 0x1234
};
int main(int argc, char** argv)
{
	//WSADATA wsaData;
	//WSAStartup(0x01, &wsaData); //创建初始化句柄

	int m_sockClient;
	if ((m_sockClient = socket(AF_INET, SOCK_DGRAM, 0)) < 0)    //创建socket句柄,采用UDP协议
	{
		printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
		return -1;
	}
	sockaddr_in m_servaddr;
	memset(&m_servaddr, 0, sizeof(m_servaddr));  //初始化结构体
	m_servaddr.sin_family = AF_INET;           //设置通信方式
	m_servaddr.sin_port = htons(PORT);         //设置端口号

	bind(m_sockClient, (sockaddr*)&m_servaddr, sizeof(m_servaddr));//绑定套接字
	Mat image;
	char buf[65536];
	while (true)
	{
		std::vector<uchar> decode;

		int n = recv(m_sockClient, buf, sizeof(buf), 0);//接受缓存
		int pos = 0;
		while (pos < n)
		{
			decode.push_back(buf[pos++]);//存入vector
		}
		buf[n] = 0;
		image = imdecode(decode, CV_LOAD_IMAGE_COLOR);//图像解码
		imshow("image", image);
		waitKey(30);
	}
	return 0;
}


接收端CMakeLists.txt

set( CMAKE_CXX_FLAGS "-std=c++11")

cmake_minimum_required( VERSION 2.8 )
project( receive )

set(OpenCV_DIR "usr/local/opencv3/share/OpenCV")

# 寻找OpenCV库
find_package( OpenCV REQUIRED )
# 添加头文件
include_directories( ${OpenCV_INCLUDE_DIRS} )
# 可执行程序
add_executable( receive receive.cpp )
# 链接OpenCV库
target_link_libraries( receive ${OpenCV_LIBS} )

4 ROS传输图像带宽不够用的解决方法(realsenseD415压缩图像)

https://blog.csdn.net/qq_23670601/article/details/98939712?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1.channel_param

  • 7
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,我知道你想了解ROS中的发布订阅话题。在ROS中,节点之间通过话题(Topic)来进行通信,话题是一种发布者-订阅者(Publisher-Subscriber)模式的通信机制。 节点可以发布一个话题,其他节点可以订阅该话题以接收发布发布的消息。消息可以是任何类型的数据,比如图像、激光数据、传感器数据等。 下面是ROS发布订阅话题的基本步骤: 1. 创建一个发布者或订阅者对象,指定话题名称和消息类型。 2. 启动节点,开始发布订阅该话题。 3. 发布者将消息发布到话题中,订阅者从话题中接收消息。 例如,在Python中创建一个发布者对象的代码如下: ```python import rospy from std_msgs.msg import String rospy.init_node('publisher_node') pub = rospy.Publisher('my_topic', String, queue_size=10) ``` 这个例子创建了一个名为"publisher_node"的节点,创建了一个名为"my_topic"的话题,并指定了消息类型为String。queue_size指定了发布者的消息缓冲队列大小。 然后,我们可以在节点中调用发布者的publish()方法来发布消息,例如: ```python msg = String() msg.data = "Hello, ROS!" pub.publish(msg) ``` 这个例子发布了一个值为"Hello, ROS!"的字符串消息到"my_topic"话题中。 同样地,我们可以在Python中创建一个订阅者对象的代码如下: ```python import rospy from std_msgs.msg import String def callback(msg): rospy.loginfo("Received message: %s", msg.data) rospy.init_node('subscriber_node') sub = rospy.Subscriber('my_topic', String, callback) rospy.spin() ``` 这个例子创建了一个名为"subscriber_node"的节点,创建了一个名为"my_topic"的话题,并指定了消息类型为String。callback函数是接收到消息后的回调函数,rospy.spin()用于让节点保持运行状态。 当订阅者接收到消息时,callback函数将被调用。例如,当我们运行上面的发布者代码时,将会在终端中看到类似如下的输出: ``` [INFO] [1630675978.253078]: Received message: Hello, ROS! ``` 希望这个例子能够帮助你理解ROS发布订阅话题的基本步骤。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值