Ubuntu14.04 ROS indigo ,Qt 5.9.1 Creator,已安插件ros_qtc_plugin,可以直接建一个包含Qt界面的ROS package,不太明白?没关系,那就看我上一篇博客<利用Qt Creator 如何在ROS 项目中从一个界面调用另一个界面?>怎么用的.
要想实现这个目标非常简单,就是将ROS 发出图片消息和Qt显示图片结合起来,大致可分为三步,每一步都有相关文章,我就是做了一个结合,毕竟QT-ROS这方面资料太少,分享出来大家共同进步!在下也是刚接触,有不对之处,欢迎大家指正!
step1:利用opencv将图片转化为ros可以接受的消息 ,参考链接
step2:在qt下可以打开图片,利用opencv显示在界面上,参考链接
step3:根据子线程和主线程通信,将消息通过信号与槽的机制传到qt界面显示,参考链接
结合以上文章,下面到了揉合阶段,如果你看到这里,大致明白了怎么回事,那么你就不要往下看了,自己去调,去改,那样自己做出来会蛮有成就感的。
1.根据step1 链接建了两个普通package:image_node_a和image_node_b ,注意配置CMakeLists 文件,没什么问题,不想多说,注意将image_noe_a中载入图片文件的路径改为自己的,千万不要包含中文!!!此步完成证明发消息没问题了!
cv::Mat image = cv::imread("/home/sun/Dragon.png", CV_LOAD_IMAGE_COLOR);
//chage the directory to your own filepath
2.根据step2的链接建了个包含qt界面的package gui_subsrciber
catkin_create_qt_pkg gui_subscriber roscpp
在main_window.hpp文件中添加opencv需要的相关头文件,在public中添加displayMat(cv::Mat),并在private中提前声明
public:
void displayMat(cv::Mat image);
private:
Ui::MainWindowDesign ui;
QNode qnode;
cv::Mat image;
之后在main_window.cpp中实现,注意提前在main_window.ui 中提前添加一个label用于显示图片
void MainWindow::displayMat(cv::Mat image)
{
cv::Mat rgb;
QImage img;
if(image.channels()==3)
{
//cvt Mat BGR 2 QImage RGB
cv::cvtColor(image,rgb,CV_BGR2RGB);
img =QImage((const unsigned char*)(rgb.data),
rgb.cols,rgb.rows,
rgb.cols*rgb.channels(),
QImage::Format_RGB888);
}
else
{
img =QImage((const unsigned char*)(image.data),
image.cols,image.rows,
image.cols*image.channels(),
QImage::Format_RGB888);
}
ui.label_4->setPixmap(QPixmap::fromImage(img));//label_4就是你添加的label的对象名
ui.label_4->resize(ui.label_4->pixmap()->size());
运行,没什么问题!见下图!
3.到现在为止还是ROS的ROS,QT的归qt,没什么联系,那么现在这一步就重要了,在gui_subscriber package的qnode文件中建一个订阅image_node_a的节点,就是将image_node_b的内容在gui_subscriber中再实现一遍,先保证在这个package中能正常接收到image_node_a发的图片,没什么很大的区别,现在qnode.hpp文件中声明回调函数,private添加订阅者声明
pubiic:
...
void myCallback_img(const sensor_msgs::ImageConstPtr& msg);
...
private:
int init_argc;
char** init_argv;
image_transport::Subscriber image_sub;
之后在qnode.cpp文件中实现回调函数就OK了,另外字啊qnode.cpp文件的两个init()和run()函数中分别添加以下句柄
image_transport::ImageTransport it(n);
image_transport::Subscriber image_sub;
image_sub = it.subscribe("node_a",1,&QNode::myCallback_img,this);
然后修改CMakeLists文件和package.xml文件中的build和run depend啊!CMakeLists中按照image_node_b里面需要添加的拷贝过来就可以了, 千万注意顺序!!!看gui_subscriber可以订阅到了!
4.最后一步,这才是核心!根据step3中传送自定义消息的方法,将qnode中接收到的消息给main_window显示啊!
(1) 在回调函数中,结束时要将imge以信号的形式发出去,你就要在qnode.hpp中定义一个信号
public:
...
void myCallback_img(const sensor_msgs::ImageConstPtr& msg);
cv::Mat img;//发送的图片type
...
Q_SIGNALS:
void loggingUpdated();
void rosShutdown();
void imageSignal(cv::Mat);//定义发送图片的信号
(2) 在回调函数最后添加Q_EMIT 如下:
void QNode::myCallback_img(const sensor_msgs::ImageConstPtr &msg)
{
cv_bridge::CvImagePtr cv_ptr;
try
{ /*change to CVImage*/
cv_ptr = cv_bridge::toCvCopy(msg, enc::BGR8);
//cv::imshow("gui_subscriber listener from node_a",cv_ptr->image);
img = cv_ptr->image;//呼应public中的img 啊!
Q_EMIT imageSignal(img) //将信号发出去
cv::waitKey(30);
}
...
}
(3) 回main_window.hpp中,将之前的displayMat改为Q_SLOTS用于接收qnode发来的imageSignal
public Q_SLOTS:
void displayMat(cv::Mat image);
(4) 最后的最后在main_window.cpp的实现函数中添加信号和槽的连接,ok了就!
//Be sure register the message type
qRegisterMetaType("cv::Mat");
...
//Next is the key siganl and slot
QObject::connect(&qnode,SIGNAL(imageSignal(cv::Mat)),this,SLOT(displayMat(cv::Mat)));
编译,有问题,那就查 头文件是否包含,CMakeLists文件修改好没,依赖库有没有添加,基本就没别的问题了!大功告成!先运行image_node_a,再运行gui_subscriber,直接就会显示下面界面!
有什么 问题欢迎留言交流!源码见github