立即咨询

电话咨询

微信咨询

立即试用
商务合作

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

2022-11-21

 

来自专辑

没有机器人,如何学习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机器人完成一个目标追踪的任务,将目标检测和运动控制结合起来,形成一个完整的视觉闭环系统。

下期再见~

 

 

更多产品了解

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

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

都在这里!

 

热门数字化产品

飞画flyDrop飞屏显示控制系统是一款专业的多媒体展览展示控 制管理软件,系统采用先进的软件技术,创新性地将内容、智能设备(声光电)融为一体,为展厅、智慧运营中心、智慧楼宇等展览展示场景提供灵活、简单、 易用的控制解决方案,大大提高对创意内容、屏幕、空间、设备的调度能力,赋能屏幕,赋能智 慧生活。
e签宝e签宝从身份认证数据源、证书核验、可信时间戳、私钥保存位置等多个关键点入手提供技术保障,同时从实名认证、意愿认证、签名、存证等环节提供可靠签署流程,证据实时上链,免除平台客户自证清白的成本,也为用户提供放心的签署服务。
快书编标系统快书编标系统强大易用的专业编标工具,让零基础的人也可以快速上手,轻松完成标书制作。专属企业的编标机器人,企业内部资源共享,有序管理,形成私有且易于管理的企业资源库。快书编标帮助个人提升工作效率,帮助企业实现业绩持续增长,为社会创造更多价值。
闪捷数据库水印系统闪捷数据库水印系统以水印数据为核心,构建数据流转安全路径,实现安全与业务双效平衡。提供丰富的API接口能力,支持用户通过API接口调用执行水印、溯源任务,查看任务执行监控等。最高水印性能可达每小时150G,助力产品满足客户大数据量高性能水印要求。
腾讯乐享企业培训管理系统腾讯乐享连接知识、沉淀经验,整合学习地图、课堂、考试、直播、文档、社群、问卷、员工关怀、项目管理、讲师管理等多应用于一体,帮助团队建立学习型组织、降低沟通成本,提升员工自发性和组织内协同性,助力企业数字化管理升级。
为你推荐
2025腾讯产业合作伙伴大会|云巴巴荣获双项大奖,载誉而归

1月16日,2025腾讯产业合作伙伴大会在三亚召开。云巴巴,荣膺“2024腾讯云卓越合作伙伴奖—星云奖”和“2024腾讯云AI产品突出贡献奖”双项大奖

2025-01-17
数据分析也能和聊天一样简单?海纳嗨数AI助手如何破解企业决策难题?

它凭借多模态融合的上下文感知系统、因果推理赋能的决策解释性以及动态知识蒸馏的预测引擎,为企业提供了全新的解决方案,助力客户业务实现快速增长。

2025-04-27
传统AI代码工具效率低下?TAPD MCP Server让开发效率与质量双倍飙升

腾讯TAPD作为国内领先的敏捷研发管理平台,可以说是最早拥抱MCP的研发管理工具之一,凭借其全生命周期的研发管理能力,成为AI代码助手的“最强外挂”,其创新功能直击开发痛点。

2025-04-25
如何提升政企客户服务效率?协同管理+规模触达成政企服务质量选型新标准

基于预设规则和对象特征,让消息推送更智能更精准,帮助企业打通内外部系统的数据系统,实现更多灵活、更个性化的营销和服务能力开发。

2025-04-25
如何利用数据分析做好活动营销?海纳嗨数让活动策划执行更加高效便捷

海纳嗨数凭借其专业的数据分析能力,为企业提供从数据采集到深度洞察的一站式解决方案,助力活动策划与执行实现质的飞跃。

2025-04-25
查看更多