1、zmq环境搭建
普通的socket是端对端的关系,ZMQ是N:M的关系,socket的连接需要显式地建立连接,销毁连接,选择协议(TCP/UDP)和错误处理,ZMQ屏蔽了这些细节,像是一个封装了的socket库,让网络编程变得更简单。ZMQ提供的套接字主要使用三种模式:‘请求-应答’,‘发布-订阅’,‘分布式’。我们采用与ROS一致的‘发布-订阅’模式进行数传。
Publisher-Subscriber模式,消息是单向流动的,发布者只能发布消息,不能接受消息;订阅者只能接受消息,不能发送消息(可参考 Redis 的发布和订阅方式)。服务端发布消息的过程中,如果有订阅者退出,不影响发布者继续发布消息,当订阅者再次连接上来,收到的消息是后来发布的消息。比较晚加入的订阅者,或者中途离开的订阅者,必然会丢掉一部分信息,如果发布者停止,所有的订阅者会阻塞,等发布者再次上线的时候继续接受消息。
1.1安装libzmq:
主机上安装zmq支持:sudo apt-get install libzmq3-dev
安装必须的库:
sudo apt-get install libtool pkg-config build-essential autoconf automake
下载源码:git clone GitHub - zeromq/libzmq: ZeroMQ core engine in C++, implements ZMTP/3.1
./autogen.sh && ./configure && make -j 4
make check && make install && sudo ldconfig
这样就安装在了目录/usr/local下了,可以在/usr/local/lib下找到动态库,可以在/usr/local/include下找到头文件。
1.2安装cppzmq
由于ZMQ把核心和实现分开了。因此只装核心库是不够的。我们开发用C++下面安装语言的绑定库cppzmq
cd ..
git clone https://github.com/zeromq/cppzmq.git
cd cppzmq
sudo cp zmq.hpp /usr/local/include/
cd ..
1.3
安装
libsodium
git clone https://gitee.com/cybermadman/libsodium
cd libsodium
git checkout 1.0.10 //
这里回退了几个版本,因为版本过高编译
zmq
时会出现
-Werror=deprecated-declarations
错误,具体见
zmq
编译
./autogen.sh -f -s
./configure && make check
sudo make install
sudo ldconfig
cd ..
1.4安装zmqpp
git clone GitHub - zeromq/zmqpp: 0mq 'highlevel' C++ bindings
cd ./zmqpp/
make -j4 && sudo make install
1.5
修改
cmakelists.txt
和
package.xml
文件
,
使用第三方包
怎么直接用上面安装的
zmqpp
没有倒腾出来先让工程跑起来吧
,
就换作使用第三方包
zmq_sup
包的方法,这个包是同事给的就拿来用了
cmakelists.txt需要添加的内容如下:
find_package(catkin REQUIRED COMPONENTS
roscpp
zmq_sup
std_msgs
roslib)
catkin_package(
INCLUDE_DIRS include
CATKIN_DEPENDS roscpp std_msgs zmq_sup)
set(LIBS_ZMQ ${zmq_sup_SOURCE_DIR}/lib/x86/libzmq.so.5.2.5
${zmq_sup_SOURCE_DIR}/lib/x86/libzmqpp.so)
include_directories(include
${catkin_INCLUDE_DIRS}
${zmq_sup_SOURCE_DIR}/include)
add_executable(${PROJECT_NAME}_pubnode src/qtros_pubnode.cpp)
target_link_libraries(${PROJECT_NAME}_pubnode
${catkin_LIBRARIES}
${LIBS_ZMQ})
package.xml
需要添加的内容如下:
使用zmq库的时候需要include头文件
#include
2、OPENCV环境搭建:
使用melodic自带的opencv,使用命令pkg-config --modversion opencv查看opencv版本为3.2.0,相应修改
cmakelists.txt
文件添加
opencv
配置
find_package(OpenCV REQUIRED)
include_directories(
include
${catkin_INCLUDE_DIRS}
${OpenCV_INCLUDE_DIRS}
${zmq_sup_SOURCE_DIR}/include
)
target_link_libraries(${PROJECT_NAME}_pubnode
${catkin_LIBRARIES}
${OpenCV_LIBS}
${LIBS_ZMQ}
)
package.xml
不需要更改,但要注意使用
opencv
时候需要
include
头文件
#include
3
、
zmq
数据流的发送与接收
3.1.
发送端的节点将图片
pub
出去
发送节点的实现步骤:
1.
定义
context
2.
设置
socket_type
为
publish
3.
用
context
和
type
定义发送端的
socket
4.
绑定
IP
地址和端口号
zmqpp::context context_;
const std::string addr_port = "tcp://192.168.43.233:5555";
zmqpp::socket_type type = zmqpp::socket_type::publish;
zmqpp::socket socket_ = zmqpp::socket(context_,type);
socket_.bind(addr_port);
5.读取图像,用zmq的socket的send函数发送:
std::string pkg_path = ros::package::getPath("qtros");
cv::Mat img = cv::imread(pkg_path+"/data/zmqpubsub.jpg");
while (ros::ok())//节点持续发图
{
std::vector
cv::imencode(".jpg", img, vec_data);
std::string str_data;
str_data.assign(vec_data.begin(), vec_data.end());
zmqpp::message message;
message << "map" < socket_.send(message); ROS_INFO("image send"); sleep(1);//休眠1秒钟 } #include "mainwindow.h" #include int main(int argc, char *argv[]) { ros::init(argc,argv,"qtshow_node"); ros::NodeHandle nh; QApplication a(argc, argv); mainwindow w;//在mainwindow.cpp中写槽函数接收并界面显示图片 w.show(); return a.exec(); } 在mainwindow.h中定义槽函数: private slots: void showFrame(); 在mainwindow.cpp实现。 定义接收端的zmq socket上下文和类型: #include "mainwindow.h" #include "ui_mainwindow.h" #include #include #include #include #include static zmqpp::context context_; static const std::string addr_port = "tcp://192.168.30.43:5555"; static zmqpp::socket_type type = zmqpp::socket_type::subscribe; static zmqpp::socket socket_ = zmqpp::socket(context_,type); 显示窗口的主函数如下: mainwindow::mainwindow(QWidget *parent) : QWidget(parent), ui(new Ui::mainwindow) { ui->setupUi(this); QTimer *timer = new QTimer(this); timer->start(1000);//定时器的频率设置为1000代表每秒一次 connect(timer, SIGNAL(timeout()), this, SLOT(showFrame())); //定时器触发槽函数持续接收并刷新显示图片 } 槽函数处理接收端zmq数据的步骤: 1.订阅topic为“map”,即发送端添加在图片数据前的内容 2. connect ip地址和端口 3.用receive接收zmq消息 void mainwindow::showFrame() { socket_.subscribe("map"); socket_.connect(addr_port); zmqpp::message message_1; socket_.receive(message_1); std::string topic; std::string data_str; message_1 >> topic >> data_str; std::vector data_vec.assign(data_str.begin(), data_str.end()); cv::Mat frame1 = cv::imdecode(data_vec, CV_LOAD_IMAGE_COLOR); QImage showVideo1 = QImage((const unsigned char*)(frame1.data),frame1.cols,frame1.rows,frame1.step,QImage::Format_RGB888); ui->label->setPixmap(QPixmap::fromImage(showVideo1.scaled(ui->label->size(),Qt::KeepAspectRatio,Qt::SmoothTransformation))); }3.2
接收端的节点将图片
sub
进来
就在之前创建的
mainwindow
中添加一个
Qlable,
用接收节点
qtshow_node.cpp
写
main
函数将图片界面显示
#include