ros opencv 之三(双目视觉图像发布与接收)
1 引言
小博在前两次已经介绍过ROS的安装使用以及基础的基础知识了,我的研究方向是计算机视觉,所以我的大部分文章基本都是视觉.所以本节将结合视觉和ROS写一篇博客,来为大家详细讲解一下,并附上教程.
通常我们在机器人项目中都会涉及到进程间通讯,亦或是好多人老是问我python 怎么调用C++,其实我认为他很有可能是遇到了进程间通讯的问题,或是图像检测使用python做的,无法将检测到的结果发给打的大程序框架.其实ROS很好的解决了进程间通信的问题,他将每一个进程可以看做一个节点,本节就新建两个节点,一个用来发布一张图像信息,另一个节点用来接收图像.具体图像处理算法可因各位具体情况而定,博客其他章节使用算法也比较多,可多多参考.
2 ROS节点
首先参考我的这篇博客,保证你的环境是正常的,可以打出hello world消息才能继续往下走.
https://blog.****.net/xiao__run/article/details/84675245
这时候我们可以看到我们的文件夹情况src文件里是我们的每一个项目,当你做完(一)上面的步骤之后,已经有一个了,接下来我们创建一个关于图像的,我们这里同样使用opencv,后面我们才使用caffe,tensorflow.
cd ROS /src //ROS位置里的src目录下
catkin_create_pkg tag_detector image_transport cv_bridge //catkin_create_pkg包名与后面两个是图像转换依赖
cd ..
catkin_make
source ./devel/setup.bash
如果编译没错我们再进入下一步
进入cd tag_detector/src
文件,开始我们的代码,先写一个发布图像的节点
nano image_raw.cpp
填入以下代码
#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <opencv2/highgui/highgui.hpp>
#include <cv_bridge/cv_bridge.h>
int main(int argc, char** argv)
{
ros::init(argc, argv, "image_publisher");
ros::NodeHandle nh;
image_transport::ImageTransport it(nh);
//在camera/image话题上发布图像,这里第一个参数是话题的名称,第二个是缓冲区的大小
image_transport::Publisher pub = it.advertise("camera/image", 1);
cv::Mat image = cv::imread("/home/lenovo/darknet/000079.jpg");
//////////////////////////////////////////////////////////////////
/////////////////////////算法 ////////////////////////////////////
/////////////////////////算法 ////////////////////////////////////
/////////////////////////////////////////////////////////////////
cv::imshow("image",image);
ROS_INFO("Hello ROS!");
sensor_msgs::ImagePtr msg = cv_bridge::CvImage(std_msgs::Header(), "bgr8", image).toImageMsg();
//将opencv格式的图像转化为ROS所支持的消息类型,从而发布到相应的话题上。
ros::Rate loop_rate(5);
cv::waitKey(0);
while (nh.ok())
{
pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
}
}
接下来新建另一个节点,接收这张图片.
nano listen_image.cpp
复制如下代码
#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <opencv2/opencv.hpp>
#include <cv_bridge/cv_bridge.h>
void imageCallback(const sensor_msgs::ImageConstPtr& msg)
{
try
{
cv::imshow("view", cv_bridge::toCvShare(msg, "bgr8")->image);
}
catch (cv_bridge::Exception& e)
{
ROS_ERROR("Could not convert from '%s' to 'bgr8'.", msg->encoding.c_str());
}
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "listen_image");
ros::NodeHandle nh;
cv::namedWindow("view");
cv::startWindowThread();
image_transport::ImageTransport it(nh);
image_transport::Subscriber sub = it.subscribe("camera/image", 1, imageCallback);
ros::spin();
cv::destroyWindow("view");
}
接下来我们配置ROS/src/tag_detector
的cmakelixt.txt
东西,打开,复制以下几段,其实就是opencv的依赖,不会cmake 的同学可以百度稍微学习下.
find_package(OpenCV REQUIRED)
include_directories(${catkin_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS})
#build my_publisher and my_subscriber
add_executable(tag_detector_pub src/image_raw.cpp)
add_executable(tag_detector_listen src/listen_image.cpp)
target_link_libraries(tag_detector_pub ${OpenCV_LIBS})
target_link_libraries(tag_detector_pub ${catkin_LIBRARIES})
target_link_libraries(tag_detector_listen ${OpenCV_LIBS})
target_link_libraries(tag_detector_listen ${catkin_LIBRARIES})
接下来我们编译一下,记住是在你的ROS路径下
catkin_make
不报错证明编译通过,接下来你就可以运行了.
还是一样,跟运行小乌龟程序一样三步走,记住是在三个不同命令窗口哈.
roscore
rosrun tag_detector tag_detector_pub
rosrun tag_detector tag_detector_listen
运行之后你可以通过rqt查看你的节点情况.
当然你可以将你的算法加入到我的图像传输中标记的算法模块里.
3 视频的发布与订阅
原理基本一样,只是代码稍微有点变换,我这里使用的是双目摄像头,结果图如下:代码我直接粘贴啦,各位自取
消息发布
#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <opencv2/highgui/highgui.hpp>
#include <cv_bridge/cv_bridge.h>
#include <std_msgs/String.h>
using namespace cv;
int main(int argc, char** argv)
{
// ROS节点初始化
ros::init(argc, argv, "image_publisher");
ros::NodeHandle n;
ros::Time time = ros::Time::now();
ros::Rate loop_rate(5); // 定义节点句柄
image_transport::ImageTransport it(n);
image_transport::Publisher pub = it.advertise("video_image", 1);
sensor_msgs::ImagePtr msg; // opencv准备读取视频
cv::VideoCapture video;
video.open(1);
if( !video.isOpened() )
{
ROS_INFO("Read Video failed!\n");
return 0;
}
cv::Mat frame;
int count = 0;
while(1)
{
video >> frame;
if( frame.empty() )
break;
count++;
msg = cv_bridge::CvImage(std_msgs::Header(), "bgr8", frame).toImageMsg();
pub.publish(msg);
ROS_INFO( "read the %dth frame successfully!", count );
loop_rate.sleep();
ros::spinOnce();
}
video.release();
return 0;
}
消息订阅
#include<ros/ros.h>
#include<cv_bridge/cv_bridge.h>
#include<sensor_msgs/image_encodings.h>
#include<image_transport/image_transport.h>
#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<stdio.h>
#include<math.h>
#include<vector>
void imageCalllback(const sensor_msgs::ImageConstPtr& msg)
{
ROS_INFO("Received \n");
try
{
cv::imshow( "video", cv_bridge::toCvShare(msg, "bgr8")->image );
cv::waitKey(30);
}
catch( cv_bridge::Exception& e )
{
ROS_ERROR( "Could not convert from '%s' to 'bgr8'.", msg->encoding.c_str() );
}
}
int main(int argc, char** argv)
{
ros::init(argc, argv, "image_listener");
ros::NodeHandle n; cv::namedWindow("video");
cv::startWindowThread();
image_transport::ImageTransport it(n);
image_transport::Subscriber sub = it.subscribe( "video_image", 1, imageCalllback );
ros::spin();
cv::destroyWindow("video");
return 0;
}