在Gazebo仿真中使用OpenCV进行颜色特征提取和目标定位

来源: 云巴巴 2022-11-21 14:37:35

 

来自专辑

没有机器人,如何学习ROS

上一期实验,我们从ROS机器人的头部相机获取了机器人的视觉图像。这一次我们继续深入,使用OpenCV实现机器人视觉中的颜色特征提取和目标定位功能。

[实验目标]

  • 对机器人视觉图像进行颜色空间转换,从RGB空间转换到HSV空间,排除光照影响;

  • 对转换后的图像进行二值化处理,将目标物体分割提取出来;

  • 对提取到的目标像素进行计算统计,得出目标物的质心坐标。

[源码下载]

ROS Kinetic (Ubuntu 16.04)

cd ~/catkin_ws/src/
git clone https://github.com/6-robot/wpr_simulation.git
git clone https://github.com/6-robot/wpb_home.git
cd ~/catkin_ws/src/wpr_simulation/scripts
./install_for_kinetic.sh
cd ~/catkin_ws/src/wpb_home/wpb_home_bringup/scripts
./install_for_kinetic.sh

ROS Melodic (Ubuntu 18.04)

cd ~/catkin_ws/src/
git clone https://github.com/6-robot/wpr_simulation.git
git clone https://github.com/6-robot/wpb_home.git
cd ~/catkin_ws/src/wpr_simulation/scripts
./install_for_melodic.sh
cd ~/catkin_ws/src/wpb_home/wpb_home_bringup/scripts
./install_for_melodic.sh

注:需将上述指令中的“catkin_ws”替换成实际的工作空间目录。

[实验步骤]

1.首先,新建一个ROS源码包Package,在Ubuntu里打开一个终端程序,输入如下指令进入ROS工作空间:

                                                                                 cd catkin_ws/src/                                                                              

 

按下回车之后,即可进入ROS工作空间,然后输入如下指令新建一个ROS源码包:

                                                             catkin_create_pkg cv_pkg roscpp cv_bridge                                                                

这条指令的具体含义是:

指令

含义

catkin_create_pkg

创建ROS源码包(package)的指令

cv_pkg

新建的ROS源码包命名

roscpp

C++依赖项,本例程使用C++编写,所以需要这个依赖项

cv_bridge

将ROS中的图像数据格式转换成OpenCV数据格式的工具包

按下回车键后,可以看到如下信息,表示新的ROS软件包创建成功。

在VSCode中,可以看到工作空间里多了一个cv_pkg文件夹,在其src子文件夹上右键点击鼠标,选择“New File”新建一个代码文件。

 

新建的代码文件,我们为其命名为“cv_hsv_node.cpp”。

 

命名完毕后,在IDE的右侧可以开始编写cv_hsv_node.cpp的代码,其内容如下:

#include <ros/ros.h>

#include <cv_bridge/cv_bridge.h>

#include <sensor_msgs/image_encodings.h>

#include <opencv2/imgproc/imgproc.hpp>

#include <opencv2/highgui/highgui.hpp>

 

usingnamespace cv;

usingnamespace std;

 

staticint iLowH = 10;

staticint iHighH = 40;

 

staticint iLowS = 90;

staticint iHighS = 255;

 

staticint iLowV = 1;

staticint iHighV = 255;

 

void Cam_RGB_Callback(const sensor_msgs::ImageConstPtr& msg)

{

    cv_bridge::CvImagePtr cv_ptr;

    try

    {

        cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::BGR8);

    }

    catch (cv_bridge::Exception& e)

    {

        ROS_ERROR("cv_bridge exception: %s", e.what());

        return;

    }

 

    Mat imgOriginal = cv_ptr->image;

   

    //将RGB图片转换成HSV

    Mat imgHSV;

    vector hsvSplit;

    cvtColor(imgOriginal, imgHSV, COLOR_BGR2HSV);

 

    //在HSV空间做直方图均衡化

    split(imgHSV, hsvSplit);

    equalizeHist(hsvSplit[2],hsvSplit[2]);

    merge(hsvSplit,imgHSV);

    Mat imgThresholded;

 

    //使用上面的Hue,Saturation和Value的阈值范围对图像进行二值化

    inRange(imgHSV, Scalar(iLowH, iLowS, iLowV), Scalar(iHighH, iHighS, iHighV), imgThresholded);

 

    //开操作 (去除一些噪点)

    Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));

    morphologyEx(imgThresholded, imgThresholded, MORPH_OPEN, element);

 

    //闭操作 (连接一些连通域)

    morphologyEx(imgThresholded, imgThresholded, MORPH_CLOSE, element);

 

    //遍历二值化后的图像数据

    int nTargetX = 0;

    int nTargetY = 0;

    int nPixCount = 0;

    int nImgWidth = imgThresholded.cols;

    int nImgHeight = imgThresholded.rows;

    int nImgChannels = imgThresholded.channels();

    for (int y = 0; y < nImgHeight; y++)

    {

        for(int x = 0; x < nImgWidth; x++)

        {

            if(imgThresholded.data[y*nImgWidth + x] == 255)

            {

                nTargetX += x;

                nTargetY += y;

                nPixCount ++;

            }

        }

    }

    if(nPixCount > 0)

    {

        nTargetX /= nPixCount;

        nTargetY /= nPixCount;

        printf("颜色质心坐标( %d , %d )  点数 = %d\n",nTargetX,nTargetY,nPixCount);

        //画坐标

        Point line_begin = Point(nTargetX-10,nTargetY);

        Point line_end = Point(nTargetX+10,nTargetY);

        line(imgOriginal,line_begin,line_end,Scalar(255,0,0),2);

        line_begin.x = nTargetX; line_begin.y = nTargetY-10;

        line_end.x = nTargetX; line_end.y = nTargetY+10;

        line(imgOriginal,line_begin,line_end,Scalar(255,0,0),2);

    }

    else

    {

        printf("目标颜色消失...\n");

    }

 

    //显示处理结果

    imshow("RGB", imgOriginal);

    imshow("Result", imgThresholded);

    cv::waitKey(5);

}

 

int main(intargc, char **argv)

{

    ros::init(argc, argv, "cv_hsv_node");

  

    ros::NodeHandle nh;

    ros::Subscriber rgb_sub = nh.subscribe("kinect2/qhd/image_color_rect", 1 , Cam_RGB_Callback);

 

    ros::Rate loop_rate(30);

 

    //生成图像显示和参数调节的窗口空见

    namedWindow("Threshold", CV_WINDOW_AUTOSIZE);

 

    cvCreateTrackbar("LowH", "Threshold", &iLowH, 179); //Hue (0 - 179)

    cvCreateTrackbar("HighH", "Threshold", &iHighH, 179);

 

    cvCreateTrackbar("LowS", "Threshold", &iLowS, 255); //Saturation (0 - 255)

    cvCreateTrackbar("HighS", "Threshold", &iHighS, 255);

 

    cvCreateTrackbar("LowV", "Threshold", &iLowV, 255); //Value (0 - 255)

    cvCreateTrackbar("HighV", "Threshold", &iHighV, 255);

 

    namedWindow("RGB");

    namedWindow("Result");

    while( ros::ok())

    { 

        ros::spinOnce();

        loop_rate.sleep();

    }

}

(1)代码的开始部分,先include了五个头文件:

ros.h是ROS的系统头文件;

cv_bridge.h是ROS图像格式和OpenCV图像格式相互转换的函数头文件;

image_encodings.h是图像数据编码格式头文件;

imgproc.hpp是OpenCV的图像处理函数头文件;

highgui.hpp是OpenCV里图像数据存储及显示函数的头文件;

(2)“using namespace cv”表示引入“cv”这个函数空间,这样代码中调用的所有OpenCV函数都不用再带上空间名,直接写函数名即可。

(3)“using namespace std”表示引入“std”标准库的函数空间,后面会用到容器vector,可以不用带上空间名,保持简洁方便阅读。

(4)定义了六个变量,分别是HSV颜色空间的三个向量的阈值范围:

iLowH是色调(Hue)的下限值;

iHighH是色调(Hue)的上限值;

iLowS是颜色饱和度(Saturation)的下限值;

iHighS是颜色饱和度(Saturation)的上限值;

iLowV是颜色亮度(Value)的下限值;

iHighV是颜色亮度(Value)的上限值;

HSV即“色调、饱和度、亮度”,是在颜色检测领域常用的一种颜色空间,将传统的RGB(红绿蓝)图像转换到HSV空间后,可以很大程度上排除光照对颜色检测的影响,从而获得更好的颜色检测结果。HSV的颜色空间如下图:

 

(6)定义一个回调函数Cam_RGB_Callback,用来处理视频流的单帧图像。其参数msg为ROS里携带图像数据的结构体,机器人每采集到一帧新的图像就会自动调用这个函数。

(7)在Cam_RGB_Callback回调函数内部,我们使用cv_bridge的toCvCopy函数将msg里的图像转换编码成BGR8格式,并保存在cv_ptr指针指向的内存区域。

(8)将cv_ptr指针指向的图像数据复制到imgOriginal,使用cvtColor将其色彩空间转换成HSV,然后进行直方图均衡化,再用咱们设定的阈值进行像素二值化。二值化的目的是将图像中目标物(比如橘色的球)的像素标记出来以便计算位置坐标,二值化结果就是后面咱们会看到的黑白图像,黑的为0,白色为1,其中白色区域就是目标物占据的像素空间。

(9)二值化后使用开操作(腐蚀)去除离散噪点,再闭操作(膨胀)将前一步操作破坏的大连通域再次连接起来。

(10)最后遍历二值化图像的所有像素,计算出橘色像素的坐标平均值,即为该颜色物体的质心。对于圆形这样中心对称的形状物体,可以认为质心坐标就是目标物的中心坐标。这里调用了OpenCV的line()函数,在RGB彩色图像中的目标物质心坐标处,绘制了一个蓝色的十字标记。

(11)在Cam_RGB_Callback回调函数的末尾,使用imshow将原始的彩色图像和最后二值化处理过的图像显示出来。绘制了物体质心坐标的彩色图像显示在一个标题为“RGB”的窗口中,二值化后的黑白图像显示在一个标题为“Result”的窗口中。最后还需要调用一个cv::waitKey(5);延时5毫秒,让上面的两个图像能够刷新显示。

(12)接下来的main(int argc, char** argv)是ROS节点的主体函数,其参数定义和其他C++程序一样。

(13)main函数里,首先调用ros::init(argc, argv, "cv_hsv_node")进行该节点的初始化操作,函数的第三个参数是本实验节点名称。

(14)接下来声明一个ros::NodeHandle对象nh,并用nh生成一个订阅对象rgb_sub,调用的参数里指明了rgb_sub将向主题“/kinect2/qhd/image_color_rect”订阅消息。机器人的摄像头启动后,会将图像数据源源不断的发布到这个主题上,这样我们的程序就能持续激活Cam_RGB_Callback回调函数。

(15)这里使用一个while(ros::ok())循环,以ros::ok()返回值作为循环结束条件可以让循环在程序关闭时正常退出。为了准确控制这个循环的运行周期,生成了一个ros::Rate的频率对象loop_rate,并在构造函数里赋初始值30,表示这个loop_rate对象会让while循环每秒钟循环30次。

(16)在进入while主循环前,先进行一些初始化,使用namedWindow初始化一个“Threshold”窗口。然后用cvCreateTrackbar给这个窗体添加六个滑动条,分别关联到程序开头声明的六个HSV的阈值变量,这样在运行的时候我们就可以用滑动条来实时调节这六个变量的数值大小。

(17)使用namedWindow创建“RGB”和“Result”两个窗体,在前面的回调函数里将会在这两个窗体里显示图像数据。

(18)在while循环中调用ros::spinOnce()函数给其他回调函数得以执行(本例程的回调函数是Cam_RGB_Callback)。

(19)调用loop_rate的sleep函数,确保while循环1秒钟循环30次。

程序编写完后,代码并未马上保存到文件里。这时可以看到VSCode界面右上编辑区的文件名“cv_hsv_node.cpp”右侧有个白色小圆点,标示此文件并未保存。需要按下键盘组合键“Ctrl + S”,界面右上编辑区的文件名标题“cv_hsv_node.cpp”右侧的白色小圆点变成了白色关闭按钮,此时代码文件才算保存成功。

完成保存操作后,还需要将源码文件添加到编译文件里才能进行编译。编译文件在cv_pkg的目录下,文件名为“CMakeLists.txt”。

 

在VSCode界面左侧工程目录中点击该文件,右侧会显示文件内容。对CMakeLists.txt的修改分为三个步骤:

(1)使用find_package()查找并引入OpenCV依赖包:

                                                                         find_package(OpenCV REQUIRED)                                                                 

 

(2)添加OpenCV的函数头文件目录路径:

 

(3)为cv_hsv_node.cpp添加编译规则。内容如下:

add_executable(cv_hsv_node

  src/cv_hsv_node.cpp

)

add_dependencies(cv_hsv_node

 ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

target_link_libraries(cv_hsv_node

  ${catkin_LIBRARIES}

  ${OpenCV_LIBS}

)

 

同样,修改完需要按下键盘快捷键Ctrl+S进行保存,代码上方的文件名右侧的小白点会变成白色关闭按钮,说明保存文件成功。下面开始进行代码文件的编译,启动一个终端程序,键入如下命令进入ROS的工作空间:

                                                                                               cd catkin_ws/                                                                                

 

然后再执行如下命令开始编译:

                                                                                       catkin_make                                                                                       

 

执行这条指令之后,会出现滚动的编译信息,直到出现“[100%] Built targetcv_hsv_node”信息,此时新的cv_hsv_node节点已经编译成功。

 

2、下面我们需要启动运行这个cv_hsv_node节点的虚拟仿真环境。打开一个终端程序,键入如下命令:

                                                                         roslaunch wpr_simulation wpb_balls.launch                                                      

 

回车执行,会启动一个Gazebo窗口,一台ROS机器人面前摆着四个颜色球,机器人的头部相机俯视着这四个球。

 

3、运行我们编写的cv_hsv_node节点。启动一个新的终端程序,输入以下指令:

                                 rosrun cv_pkg cv_hsv_node                        

 

按下回车键,cv_hsv_node节点就启动起来了。此时会弹出三个窗口:

“RGB”窗体程序,里面显示的是机器人头部相机所看到的四个颜色球的图像。

 

“Threshold”窗体程序,里面显示的是当前使用的HSV颜色阈值,我们可以使用鼠标直接拖动窗体中的滑杆来改变阈值大小,在其他窗体中会实时显示阈值变化的效果。

 

“Result”窗体程序,里面显示的是转换颜色空间并二值化后的结果。白色的部分是检测到目标物的像素区域,黑色的部分是被剔除掉的非目标物的像素区域。

 

这时候我们切换到运行cv_hsv_node节点的终端窗口,可以看到检测到的目标物的中心坐标值:

 

机器人头部使用的相机是Kinect2,我们采集它的QHD图像分辨率是960×540。颜色质心的坐标是以图像的左上角为(0,0)原点,对照前面“RGB”彩色中绘出的目标位置(蓝色十字标记),可以看到最后计算的目标物质心坐标和图像显示结果大致相同。

4、我们可以借助wpr_simulation附带的程序让中间的桔色球动起来,以观察目标物运动时,咱们的检测算法是否继续奏效。在Ubuntu桌面再开一个新的终端程序,输入如下指令:

                                                                        rosrun wpr_simulation ball_random_move                                                         

 

执行之后,可以看到Gazebo里的橘色球开始随机运动。

 

此时再切换到“RGB”窗口程序,可以看到图像中的桔色球也跟着运动。

 

回到运行cv_hsv_node节点的终端窗口,可以看到目标中心坐标值也在相应变化。

 

5、实验中我们编写的cv_hsv_node.cpp在wpr_simulation中有个对应的例程,同学们在遇到编译问题时可以在VSCode中打开这个例程源码文件进行对比参考,文件位置:

                                                         ~/catkin_ws/src/wpr_simulation/src/demo_cv_hsv.cpp                                                     

其运行指令为:

                                                                             rosrun wpr_simulation demo_cv_hsv                                                            

 

6、可以在“Threshold”窗体中改变目标颜色的HSV阈值范围,锁定其他颜色的目标球。然后使用下面这些指令,让其他的颜色球也随机运动,验证这种检测方法的普适性。

红色球

rosrun wpr_simulation ball_random_move red

绿色球

rosrun wpr_simulation ball_random_move green

蓝色球

rosrun wpr_simulation ball_random_move blue

 

 

 

 

[实验结果]

在这个实验中,我们使用OpenCV的颜色空间转换函数,实现了目标物体的位置检测,在这基础上可以扩展出很多的应用。下一次我们会尝试着让ROS机器人完成一个目标追踪的任务,将目标检测和运动控制结合起来,形成一个完整的视觉闭环系统。

下期再见~

 

 

更多产品了解

欢迎扫码加入云巴巴企业数字化交流服务群

产品交流、问题咨询、专业测评

都在这里!

 

评论列表

为你推荐

黑虎小工单助力双枪:“中国筷子第一股”的数字化转型

黑虎小工单助力双枪:“中国筷子第一股”的数字化转型

现在有了小工单,员工实时录入工序数据,等到了月底,财务就可以一键导出工资和绩效报表,省去了大量收集和导入的时间精力,而且准确度也更高了,杜绝了工人和财务之间的来回扯皮。

2022-08-24 11:07:14

OA办公、研发安全?腾讯云桌面随时随地轻松“拿捏”云上办公

OA办公、研发安全?腾讯云桌面随时随地轻松“拿捏”云上办公

腾讯云桌面为您提供随需快捷交付的虚拟桌面服务。云桌面可以帮助您轻松构建安全的数字化工作空间,满足移动办公、安全开发、在线设计等场景需求,提升业务访问的安全性和连续性。通过自适应传输协议,终端用户可以获得优质的云桌面访问体验。

2022-07-28 17:56:56

有关企业信息化的主要内容的简单介绍

有关企业信息化的主要内容的简单介绍

众所周知,我们生活在一个动态的世界里,面临很多困难。我们只能面对他们,所迈出的第一步路是关键,企业信息化这个产品成熟后经过多次调试的验证才对外输出。本文,就对企业信息化的相关内容进行简单介绍。 企业通过设立专门的信息机构和信息监督员,配备自动化,智能化,

2022-11-21 10:24:24

制造企业的设备管理,晨科助力广东美特搭建管理系统

制造企业的设备管理,晨科助力广东美特搭建管理系统

设备是工厂生产中的主体,设备的稳定运行,是生产稳定进行的前提。

2023-06-27 17:35:08

如何构建高可用跨境电商网络?微云网络让跨境电商更轻松

如何构建高可用跨境电商网络?微云网络让跨境电商更轻松

然而,跨境电商在运营过程中也会面临一些网络问题,这些问题可能会影响企业的运营效率和消费者的购物体验。

2024-04-16 17:14:26

腾讯云SOC方案入选CIC工信安全优秀解决方案

腾讯云SOC方案入选CIC工信安全优秀解决方案

腾讯“云原生安全Cloud SOC智能安全检测响应预测全局态势感知解决方案 ”凭借在“技术水平、应用示范效果、产业带动性”方面的突出优势,在众多申报方案中脱颖而出,成功入选2021年优秀解决方案。

2021-12-22 17:54:43

严选云产品

阿商订货宝连锁餐饮升级供应链平台 阿商订货宝连锁餐饮升级供应链平台阿商订货宝连锁餐饮升级供应链平台,业务更合规、资金更安全,可以提升效率、降低成本、增加利润。同时对接ERP、WMS、物流公司等系统,消除信息孤岛,实现业务全程信息化管理。
卓豪自动化补丁管理系统 支持跨平台打补丁!从单个界面跨台式机、笔记本电脑、服务器、漫游设备和虚拟机无缝地部署补丁。全面支持250余种第三方应用程序打补丁,过在第三方应用程序中打补丁以弥补漏洞,维持100%安全。针对常见应用程序(例如,Adobe、Java、WinRAR及更多)的大型补丁库。
威努特数据备份与恢复系统 威努特数据备份与恢复系统,通过自动化的调度、监控,以及提供简易、人性化的的管理功能,使数据保护不再需要较高的学习门槛,有助于组织专注于其核心业务,而无需担忧数据的安全性。 病毒,木马,勒索软件,黑客攻击导致的数据破坏;误操作删除等人为原因导致数据的破坏;系统、业务软件BUG导致数据的破坏。
腾讯云智慧停车解决方案 腾讯云智慧停车解决方案,打造城市级车场、车位、停车行为数据资源池,提供端到端、覆盖路内停车与路外停车等多场景的停车业务解决方案。
Radware CNP云原生安全防护服务 Radware CNP为全面保护用户的亚马逊云科技云资产提供了一种无需代理端的云原生解决方案,不仅可以保护云环境的整体安全态势,还可以保护单个云工作负载免受云原生攻击矢量的影响,满足用户在云端的安全和合规需求。
珞安科技石油石化行业解决方案 珞安科技石油石化行业解决方案,在流程化生产过程中,控制系统设备多,异构性强,集成度高,—般采用DCS、PLC等工控设备,其中DCS通过网络接口将各个工作站链接起来,工作站可以独立对数据进行采集、处理、监控、操作和控制等。

甄选10000+数字化产品 为您免费使用

申请试用