- 识别平面表面:这项技能使机器人能够检测物体通常所在的位置,如桌子和架子。这是搜索物体的第一步。
- 识别物体:一旦您知道在哪里寻找,就必须在场景中识别不同的物体,并根据机器人的位置(坐标系)定位它们。
3.1 环境介绍
您将使用以下环境:
- PR2 机器人:它是一种多功能机器人,可以全向移动,有两只手臂和一个可移动的躯干。它还配有激光器和点云相机。它是物体识别和操控的完美候选者。
- 桌子上的物体选择:您会在桌子上找到几个物体。它们是为了在检测中提供多样性。物体中有一把枪,因为我们想解释如何检测有武装人员的危险情况。
您可以使用以下列出的键盘遥控和关节命令移动PR2机器人。
roslaunch pr2_tc_teleop keyboard_teleop.launch
使用Q键将速度增加到大约8.0,以查看PR2机器人移动。
点击图形界面图标,打开图形工具,通过简单的GUI移动PR2机器人的关节:
您应该看到与下图类似的图像:
选择 head_joint_controller以移动头部的俯仰和倾斜。这将有助于检查您的物体检测是否正常工作。
3.2 平面检测器
识别物体的第一步是知道这些物体的位置。您将使用 surface_perception 包来检测平面并在RViz中表示检测结果。surface_perception包还可以检测平面上的物体。这是一个优秀的ROS包,制作精良,考虑到它自ROS-indigo以来就没有更新过,并且它在ROS-Noetic中工作。
第一步是创建一个对象识别包:
bash
cd /home/user/catkin_ws/src
catkin_create_pkg my_object_recognition_pkg rospy
cd my_object_recognition
mkdir launch
touch launch/surface_detection.launch
cd /home/user/catkin_ws
catkin_make
source devel/setup.bash
rospack profile
surface_detection.launch
bash
<?xml version="1.0"?>
<launch>
<node name="surface_perception_node" pkg="surface_perception" type="demo" output="screen" args="base_link">
<remap from="cloud_in" to="/camera/depth_registered/points"/>
</node>
</launch>
此二进制文件需要两个元素才能工作:
- 输入我们将以此为基础进行检测的 Tf 坐标框架。在这种情况下,base_link 就足够了。
- 将 cloud_in 重新映射到您的机器人从深度传感器发布点云的主题。在我们的例子中,这是 /camera/depth_registered/points。
让我们启动表面检测包并看看会发生什么:
bash
roslaunch my_object_recognition_pkg surface_detection.launch
您现在应该看到类似下图的图像:
如果您没有看到标记,请确保在 RViz 面板中看到指向主题 /surface_objects 的元素类型标记:
您可以看到,仅使用此系统,您就可以获得:
- 标记代表您的机器人检测到的不同水平表面。它们以紫色标记表示。
- 标记代表检测到的物体。它们以绿色标记表示。
练习 3.2.1
- 创建一个名为surface_data_extraction.py 的Python脚本,该脚本提取由surface_detection.launch生成的标记数据,并仅过滤与桌子高度相对应的水平表面物体。
- 桌子高度约为0.8米,因此您应该过滤该高度周围的表面物体。
- 您可以将表面与物体区分开来,因为它们的名称是surface_X ,而物体是surface_X_object_Y_axes。
稍后,可以使用这些数据仅在该空间区域周围查找物体并用于抓取。
请记住使Python脚本可执行;否则,ROS将无法执行它:
bash
chmod +x surface_data_extraction.py
my_object_recognition_pkg/scripts/surface_data_extraction.py
bash
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import rospy
import numpy
from visualization_msgs.msg import Marker
class SurfaceObjectFilter:
def __init__(self, table_height_init=0.8, error_height=0.2):
self._rate = rospy.Rate(5)
self.table_height = table_height_init
self._error_height = error_height
self.surface_dict = {}
self.surface_topic = "/surface_objects"
self._check_surface_ready()
rospy.Subscriber(self.surface_topic, Marker, self.surface_callback)
rospy.loginfo('Ready to detect Surfaces!')
def _check_surface_ready(self):
self._surface_data = None
while self._surface_data is None and not rospy.is_shutdown():
try:
self._surface_data = rospy.wait_for_message(self.surface_topic, Marker, timeout=1.0)
rospy.logdebug("Current "+self.surface_topic+" READY=>" + str(self._surface_data))
except:
rospy.logerr("Current "+self.surface_topic+" not ready yet, retrying.")
def update_table_height(self,new_table_height):
self.table_height = new_table_height
def look_for_table_surface(self, z_value):
"""
"""
delta_min = z_value - self._error_height
delta_max = z_value + self._error_height
is_the_table = delta_min < self.table_height < delta_max
return is_the_table
def surface_callback(self, msg):
name = msg.ns
surface_pose = msg.pose
if "surface_" in name and not "_axes" in name:
# We check the heigh in z to see if its the table
if self.look_for_table_surface(msg.pose.position.z):
if name in self.surface_dict:
rospy.loginfo("This surface was alreday found")
else:
self.surface_dict[name] = surface_pose
rospy.loginfo("Found New Surface=")
else:
rospy.logdebug("Surface Object Not found "+str(name))
def get_surface_dict_detected(self):
return self.surface_dict
def run(self):
while not rospy.is_shutdown():
table_surfaces_detected = self.get_surface_dict_detected()
rospy.loginfo(str(table_surfaces_detected))
self._rate.sleep()
if __name__ == '__main__':
rospy.init_node('surface_data_extract_node', log_level=rospy.INFO)
try:
SurfaceObjectFilter().run()
except KeyboardInterrupt:
rospy.loginfo('Shutting down')
bash
def update_table_height(self,new_table_height):
self.table_height = new_table_height
...
def look_for_table_surface(self, z_value):
"""
"""
delta_min = z_value - self._error_height
delta_max = z_value + self._error_height
is_the_table = delta_min < self.table_height < delta_max
return is_the_table
我们如何检查一个检测是否是桌子。我们在 self.table_height
中设置了一个特定的高度值,通过 update_table_height
更新,然后确认通过 look_for_table_surface
得到的 z_value
大致等于更新后的高度。
通过同时启动表面检测和 surface_data_extraction.py
,您应该只获得桌子的检测结果。
bash
cd /home/user/catkin_ws
source devel/setup.bash
rospack profile
# Now launch
roslaunch my_object_recognition_pkg surface_detection.launch
bash
cd /home/user/catkin_ws
source devel/setup.bash
rospack profile
# Now launch
rosrun my_object_recognition_pkg surface_data_extraction.py
当系统检测到给定高度周围的表面时,您应该得到类似于以下输出的输出:
bash
[INFO] [1615891002.402422, 276.540000]: {'surface_1': position:
x: 1.5979965925216675
y: 1.1848139762878418
z: 0.9700260162353516
orientation:
x: 0.0
y: 0.0
z: 0.008941666223108768
w: 0.9999600648880005}
3.3 物体检测:扩展物体检测
MoscowskyAnton 创建了这个扩展物体检测系统。该包试图将所有基本的物体和人类识别算法整合到一个统一、全面的结构中,以支持嵌套检测。
它有出色的文档,您可以在Wiki上查看。
您可以做的事情包括:
- 检测斑点
- 使用 Haar 特征级联
- 使用 TensorFlow
- QR 码跟踪
- 特征匹配
- 基本运动检测
- 还有更多。在这一单元中,我们提供了多个示例,并解释如何将它们组合以创建您自己的检测器。
物体检测:简单/复杂物体检测
该系统的主要思想是使用 .xml 文件来定义您将用于检测物体的不同属性。然后,您将这些属性组合起来以检测特定的简单物体。让我们看看在这一单元中将使用的示例:
bash
roscd my_object_recognition_pkg
mkdir -p config/object_base_example
roscd my_object_recognition_pkg/config/object_base_example
touch Simple_Gun.xml
Simple_Gun.xml
bash
<?xml version="1.0" ?>
<AttributeLib>
<Attribute Name="HistColorPortalGun" Type="HistColor" Histogram="histograms/PortalGun.yaml"/>
<Attribute Name="PotalGunSize" Type="Size" MinAreaPc="0.00" MaxAreaPc="100"/>
<Attribute Name="NotFractal" Type="Size" MinAreaPc="0.5" MaxAreaPc="100"/>
<Attribute Name="HSVColorBlackGun" Type="HSVColor" Hmin="0" Hmax="0" Smin="0" Smax="0" Vmin="31" Vmax="161"/>
<Attribute Name="HaarGun" Type="HaarCascade" Cascade="gun_haar/classifier/cascade.xml"/>
<Attribute Name="MyBlobAttribute" Type="Blob" minThreshold="54" maxThreshold="125" blobColor="0" minArea="1500" minCircularity="0.03" minConvexity="0.64" minInertiaRatio="0.00"/>
</AttributeLib>
<SimpleObjectBase>
<SimpleObject Name="PortalGun" ID="1">
<Attribute Type="Detect">HistColorPortalGun</Attribute>
<Attribute Type="Check">PotalGunSize</Attribute>
<Attribute Type="Check">NotFractal</Attribute>
</SimpleObject>
<SimpleObject Name="BlackGun" ID="2">
<Attribute Type="Detect">HSVColorBlackGun</Attribute>
<Attribute Type="Check">NotFractal</Attribute>
</SimpleObject>
<SimpleObject Name="HaarBlackGun" ID="3" Mode="Hard" MergingPolicy="Union">
<Attribute Type="Detect">HaarGun</Attribute>
<Attribute Type="Detect">MyBlobAttribute</Attribute>
</SimpleObject>
</SimpleObjectBase>
<RelationLib>
</RelationLib>
<ComplexObjectBase>
</ComplexObjectBase>
这些 XML 文件有三个主要部分:
- Attributes :在这里,我们定义每个检测器。在这个例子中,我们定义:
- HistColorPortalGun:我们使用颜色直方图(我们将学习如何生成)来追踪特定的颜色轮廓(类似于斑点)。
- PortalGunSize:类型为大小检查,物体的面积大于(MinAreaPc 图像面积)且小于(MaxAreaPc 图像面积)。
- NotFractal:这是为了避免检测到非常小或非常大的斑点。
- HSVColorBlackGun:在这里,我们不是通过直方图,而是通过基本颜色进行检测。
- HaarGun:我们通过 Haar 特征级联进行检测,在这种情况下,检测枪的形态是通过 HaarGunGit 完成的。
- MyBlobAttribute:这是一个黑白斑点检测器。有关斑点检测器的更多信息,请参见 BlobDocs。
- Simple objects:在这里,我们定义我们想要检测的主要物体,使用哪些属性,以及如何将它们组合。这是通过模式(hard, soft)来完成的。基本上是设置 AND 或 OR 条件,如果某个属性检测到东西。还有合并策略 DOCS。这定义了属性的边界框是否合并为最大的边界框、所有框的联合,或任何其他组合。
- Relation.lib and complex objects:这两个标签是一起使用的,因为它们定义了复杂物体的检测和构建方式。复杂物体不超过两个或更多简单物体的组合,并具有某种空间关系,例如检测一个物体在另一个物体内部或相邻特定距离。在这种情况下,我们没有定义任何。
简单物体:Portal Gun
我们使用 HistColorPortalGun 和 NotFractal 。它的工作原理是,HistColorPortalGun 设置为检测类型;因此,我们将图像数据输入给它。如果它检测到某物,我们就通过 NotFractal 检查。如果没有发现任何分形,则检测有效。否则,就是无效的。
在下一部分中,我们将看到如何生成所有这些,但这里有一个例子,展示我们将使用颜色直方图生成的两个物体。您会看到,这作为物体的指纹非常有用。正如您所见,这两个物体具有非常独特的直方图,这使得它们的检测变得容易得多。
这些图是每个对象的颜色直方图。请注意,这是对象最具标志性的区域的直方图。如果我们要为整个对象创建图像的直方图,它们将非常相似,因为白色在两个图像中占主导地位。这些是输入到直方图生成器的图像:
创建直方图
我们将使用您可以在扩展对象检测包中找到的工具。
bash
roscd my_object_recognition_pkg/config/object_base_example
mkdir histograms
# And now we create histogram generator launch:
roscd my_object_recognition_pkg
touch launch/hist_color_params_collector_point.launch
hist_color_params_collector_point.launch
bash
<launch>
<arg name="output" value="screen"/>
<arg name="hist_name" default="PortalGun"/>
<arg name="hist_path" default="$(find my_object_recognition_pkg)/config/object_base_example/histograms/$(arg hist_name).yaml"/>
<node name="hist_color_params_collector_point_node" pkg="extended_object_detection" type="hist_color_params_collector_point_node" output="screen" required="true">
<param name="out_filename" value="$(arg hist_path)"/>
<remap from="image_raw" to="/camera/rgb/image_raw"/>
</node>
</launch>
这些是您必须根据机器人和物体更改的变量:
- hist_name:您将生成的直方图文件的名称。
- hist_path:您希望程序保存直方图文件的位置。
- /camera/rgb/image_raw:这是 RGB 相机图像主题。根据您的机器人,这可能会改变。
现在让我们启动它并生成传送枪的直方图文件。
首先,使用 PR2 机器人尽可能靠近桌子和物体。使用键盘命令,将机器人移近桌子并降低机器人的头部以更好地查看物体。
要让它移动,线速度必须至少为 10.0。所以如果它不动,不要惊慌。只需按 Q 直到线速度达到 10.0 左右即可
bash
roslaunch pr2_tc_teleop keyboard_teleop.launch
左键单击对象上最具代表性的颜色。在本例中,我们单击的是灰色支架。
看到它生成了有意义的轮廓后,无需移动相机,右键单击图像。这将修复值并开始根据直方图值进行检测。当它变成绿色时,您就知道它起作用了,类似于下面的图片:
别担心大小或 thesh 值。它们在这里不使用。
如果一切顺利,当您在图形工具中按下 ESC 键时,选择 histogram point creator GUI 窗口,它应该会生成 PortalGun.yaml
文件并输出这两行。
这里告诉您在 SimpleObject.xml
文件中作为属性放置的内容,用于通过直方图检测 PortalGun。
bash
<Attribute Name="MyHistColorAttribute" Type="HistColor" Histogram="/home/user/catkin_ws/src/perception_course_solutions/perception_unit3_solutions/my_object_recognition_pkg/config/object_base_example/histograms/PortalGun.yaml"/>
<Attribute Name="MySizeAttribute" Type="Size" MinAreaPc="0.00" MaxAreaPc="100"/>
现在,让我们使用 Simple_Gun.xml
文件来测试它是否有效。
我们需要检查 Simple_Gun.xml
中是否设置了 PortalGun Histogram 的正确路径。路径可以是:
- 相对路径:相对于您保存
Simple_Gun.xml
的路径。因此,在这种情况下为histograms/PortalGun.yaml
- 绝对路径:
/home/user/catkin_ws/src/my_object_recognition_pkg/config/object_base_example/histograms/PortalGun.yaml
相对路径通常看起来更简洁,所以我们使用相对路径。
我们还将把属性的名称更改为 HistColorPortalGun
。
并且我们还将把大小属性的名称更改为 PortalGunSize
。
最终的内容应类似于以下示例:
Simple_Gun.xml
XML
<?xml version="1.0" ?>
<AttributeLib>
<Attribute Name="HistColorPortalGun" Type="HistColor" Histogram="histograms/PortalGun.yaml"/>
<Attribute Name="PotalGunSize" Type="Size" MinAreaPc="0.00" MaxAreaPc="100"/>
<Attribute Name="NotFractal" Type="Size" MinAreaPc="0.5" MaxAreaPc="100"/>
<Attribute Name="HSVColorBlackGun" Type="HSVColor" Hmin="0" Hmax="0" Smin="0" Smax="0" Vmin="31" Vmax="161"/>
<Attribute Name="HaarGun" Type="HaarCascade" Cascade="gun_haar/classifier/cascade.xml"/>
<Attribute Name="MyBlobAttribute" Type="Blob" minThreshold="54" maxThreshold="125" blobColor="0" minArea="1500" minCircularity="0.03" minConvexity="0.64" minInertiaRatio="0.00"/>
</AttributeLib>
<SimpleObjectBase>
<SimpleObject Name="PortalGun" ID="1">
<Attribute Type="Detect">HistColorPortalGun</Attribute>
<Attribute Type="Check">PotalGunSize</Attribute>
<Attribute Type="Check">NotFractal</Attribute>
</SimpleObject>
<SimpleObject Name="BlackGun" ID="2">
<Attribute Type="Detect">HSVColorBlackGun</Attribute>
<Attribute Type="Check">NotFractal</Attribute>
</SimpleObject>
<SimpleObject Name="HaarBlackGun" ID="3" Mode="Hard" MergingPolicy="Union">
<Attribute Type="Detect">HaarGun</Attribute>
<Attribute Type="Detect">MyBlobAttribute</Attribute>
</SimpleObject>
</SimpleObjectBase>
<RelationLib>
</RelationLib>
<ComplexObjectBase>
</ComplexObjectBase>
我们创建启动器来使用 Simple_Gun.xml
XML
roscd my_object_recognition_pkg
touch launch/gun_detection.launch
gun_detection.launch
XML
<launch>
<arg name="output" default="screen"/>
<arg name="objectBasePath" default="$(find my_object_recognition_pkg)/config/object_base_example/Simple_Gun.xml"/>
<node name="extended_object_detection" pkg="extended_object_detection" type="extended_object_detection_node" output="screen">
<param name="objectBasePath" value="$(arg objectBasePath)"/>
<param name="videoProcessUpdateRate" value="5"/>
<param name="screenOutput" value="false"/>
<param name="publishImage" value="true"/>
<param name="publishMarkers" value="true"/>
<param name="subscribeDepth" value="false"/>
<param name="maxContourPoints" value="-1"/>
<rosparam param="selectedOnStartSimple">[1]</rosparam>
<rosparam param="selectedOnStartComplex">[-1]</rosparam>
</node>
</launch>
对 gun_detection-launch 文件中几个值得注意的元素的注释:
XML
<arg name="objectBasePath" default="$(find my_object_recognition_pkg)/config/object_base_example/Simple_Gun.xml"/>
这里我们指出了 Simple_Gun.xml 文件,我们将在其中定义所有简单对象和属性。
XML
<rosparam param="selectedOnStartSimple">[1]</rosparam>
<rosparam param="selectedOnStartComplex">[-1]</rosparam>
在这里,我们设置了将在检测中激活和搜索的简单物体和复杂物体。
[]
:如果数组中没有放置任何内容,则所有简单或复杂物体将被激活。[1,4,...,25]
:每个数字表示简单或复杂物体的 ID。例如,如果我们只放置[1]
,这意味着只有 ID=1 的简单物体会被激活,此时是 PortalGun。[-1]
:表示不会激活任何物体。
启动它并查看 RViz:
XML
roslaunch my_object_recognition_pkg gun_detection.launch
点击图形界面图标以查看直方图 GUI 生成器。如果您的 RViz 界面与所示的不一样,您可以在本单元的解决方案中查看 RViz 配置文件,路径为 my_object_recognition_pkg/rviz/object_recognition.rviz
。
练习 3.3.1
- 根据我们用于 PortalGun 的相同步骤,为香蕉创建直方图检测器。
- 将修改添加到 Simple_Gun.xml 和 gun_detector.launch 以检测香蕉和 PortalGun。
- 注意:由于香蕉的形状,您可能需要删除分形属性。
Simple_Gun.xml
XML
<?xml version="1.0" ?>
<AttributeLib>
<Attribute Name="HistColorPortalGun" Type="HistColor" Histogram="histograms/PortalGun.yaml"/>
<Attribute Name="PotalGunSize" Type="Size" MinAreaPc="0.00" MaxAreaPc="100"/>
<Attribute Name="NotFractal" Type="Size" MinAreaPc="0.5" MaxAreaPc="100"/>
<Attribute Name="HistColorBanana" Type="HistColor" Histogram="histograms/Banana.yaml"/>
<Attribute Name="BananaSize" Type="Size" MinAreaPc="0.05" MaxAreaPc="100"/>
<Attribute Name="HSVColorBlackGun" Type="HSVColor" Hmin="0" Hmax="0" Smin="0" Smax="0" Vmin="22" Vmax="61"/>
<Attribute Name="HaarGun" Type="HaarCascade" Cascade="gun_haar/classifier/cascade.xml"/>
<Attribute Name="MyBlobAttribute" Type="Blob" minThreshold="54" maxThreshold="125" blobColor="0" minArea="1500" minCircularity="0.03" minConvexity="0.64" minInertiaRatio="0.00"/>
</AttributeLib>
<SimpleObjectBase>
<SimpleObject Name="PortalGun" ID="1">
<Attribute Type="Detect">HistColorPortalGun</Attribute>
<Attribute Type="Check">PotalGunSize</Attribute>
<Attribute Type="Check">NotFractal</Attribute>
</SimpleObject>
<SimpleObject Name="Banana" ID="2">
<Attribute Type="Detect">HistColorBanana</Attribute>
<Attribute Type="Check">BananaSize</Attribute>
</SimpleObject>
<SimpleObject Name="BlackGun" ID="3">
<Attribute Type="Detect">HSVColorBlackGun</Attribute>
<Attribute Type="Check">NotFractal</Attribute>
</SimpleObject>
<SimpleObject Name="HaarBlackGun" ID="4" Mode="Hard" MergingPolicy="Union">
<Attribute Type="Detect">HaarGun</Attribute>
<Attribute Type="Detect">MyBlobAttribute</Attribute>
</SimpleObject>
</SimpleObjectBase>
<RelationLib>
</RelationLib>
<ComplexObjectBase>
gun_detection.launch
XML
<launch>
<arg name="output" default="screen"/>
<arg name="objectBasePath" default="$(find my_object_recognition_pkg)/config/object_base_example/Simple_Gun.xml"/>
<node name="extended_object_detection" pkg="extended_object_detection" type="extended_object_detection_node" output="screen">
<param name="objectBasePath" value="$(arg objectBasePath)"/>
<param name="videoProcessUpdateRate" value="5"/>
<param name="screenOutput" value="false"/>
<param name="publishImage" value="true"/>
<param name="publishMarkers" value="true"/>
<param name="subscribeDepth" value="false"/>
<param name="maxContourPoints" value="-1"/>
<rosparam param="selectedOnStartSimple">[1,2]</rosparam>
<rosparam param="selectedOnStartComplex">[-1]</rosparam>
</node>
</launch>
一些解决方案的注意事项:
- 如您所见,NotFractal属性未被使用。这是因为在香蕉上用直方图检测到的部分太小,因而被视为分形,无法检测。
- 但是,如果您移除分形属性,您将获得过多的检测结果。这是因为每个与直方图对应的小区域都被视为一个检测。因此,我们必须调整大小。
- 将最小大小设置为
MinAreaPc=0.05
后,我们只考虑大于该值的有效检测区域,从而清理检测结果,只保留一到两个。 - 以下是带有分形属性的结果:
- 请参见以下未调整分形和尺寸的结果:
bash
# This variable set is because of current Accademy system being remote. Localy you wouldn't need to do it.
QT_X11_NO_MITSHM=1
echo $QT_X11_NO_MITSHM
roslaunch my_object_recognition_pkg hist_color_params_collector_point_banana.launch
bash
roslaunch my_object_recognition_pkg gun_detection.launch
简单对象:黑色枪
它的工作原理与上一个练习相同,但使用 HSVColorBlackGun 属性。
创建 HSV 检测
我们将使用您可以在扩展对象检测包中找到的工具。
bash
# We create a HSV sampler launch:
roscd my_object_recognition_pkg
touch launch/hsv_color_params_collector.launch
hsv_color_params_collector.launch
bash
<launch>
<arg name="output" value="screen"/>
<node name="hsv_color_params_collector_node" pkg="extended_object_detection" type="hsv_color_params_collector_node" output="screen" required="true" >
<remap from="image_raw" to="/camera/rgb/image_raw"/>
</node>
</launch>
启动launch文件获取HSV的值:
bash
# This variable set is because of current Accademy system being remote. Localy you wouldn't need to do it.
QT_X11_NO_MITSHM=1
echo $QT_X11_NO_MITSHM
roslaunch my_object_recognition_pkg hsv_color_params_collector.launch
如您所见,如果我们仅调整 HSV 值,它将检测到所有具有黑色枪的点。不仅在枪中,而且在瓶子或甜甜圈的阴影中。
为了初步了解 HSV 的值,我们建议您使用基于 Web 的颜色选择器,例如 ColorZilla 或任何允许您获取图像的 HSV 值的浏览器插件,然后选择此笔记本中显示枪的图像(例如,上面几行)。
再次,我们将使用 size 属性来检测明显较大的区域以避免这种情况。
但这并不能解决巨大的暗区(如桌子的侧面)也会被检测为枪的问题。为了解决这个问题,我们还必须限制最大尺寸。但这必须在 Simple_Gun.xml 属性定义中完成。
一旦您获得了满意的值,请在 GUI 处于焦点状态时按下键盘上的 ESC 键,启动将终止,并输出您需要添加到 Simple_Gun.xml 的属性,以使用 HSV 检测枪。
bash
[ WARN] [1615908901.726154378, 4258.786000000]: YOUR ATTRIBUTES
<Attribute Name="MyHSVColorAttribute" Type="HSVColor" Hmin="0" Hmax="0" Smin="0" Smax="0" Vmin="22" Vmax="61"/>
<Attribute Name="MySizeAttribute" Type="Size" MinAreaPc="0.01" MaxAreaPc="100"/>
Simple_Gun.xml
我们更改了 BlackGunSizeAttribute 中的 MaxAreaPc=20 以避免检测桌子的侧面。
XML
<?xml version="1.0" ?>
<AttributeLib>
<Attribute Name="HistColorPortalGun" Type="HistColor" Histogram="histograms/PortalGun.yaml"/>
<Attribute Name="PotalGunSize" Type="Size" MinAreaPc="0.00" MaxAreaPc="100"/>
<Attribute Name="NotFractal" Type="Size" MinAreaPc="0.5" MaxAreaPc="100"/>
<Attribute Name="HistColorBanana" Type="HistColor" Histogram="histograms/Banana.yaml"/>
<Attribute Name="BananaSize" Type="Size" MinAreaPc="0.05" MaxAreaPc="100"/>
<Attribute Name="HSVColorBlackGun" Type="HSVColor" Hmin="0" Hmax="0" Smin="0" Smax="0" Vmin="22" Vmax="61"/>
<Attribute Name="BlackGunSizeAttribute" Type="Size" MinAreaPc="0.01" MaxAreaPc="20"/>
<Attribute Name="HaarGun" Type="HaarCascade" Cascade="gun_haar/classifier/cascade.xml"/>
<Attribute Name="MyBlobAttribute" Type="Blob" minThreshold="54" maxThreshold="125" blobColor="0" minArea="1500" minCircularity="0.03" minConvexity="0.64" minInertiaRatio="0.00"/>
</AttributeLib>
<SimpleObjectBase>
<SimpleObject Name="PortalGun" ID="1">
<Attribute Type="Detect">HistColorPortalGun</Attribute>
<Attribute Type="Check">PotalGunSize</Attribute>
<Attribute Type="Check">NotFractal</Attribute>
</SimpleObject>
<SimpleObject Name="Banana" ID="2">
<Attribute Type="Detect">HistColorBanana</Attribute>
<Attribute Type="Check">BananaSize</Attribute>
</SimpleObject>
<SimpleObject Name="BlackGun" ID="3">
<Attribute Type="Detect">HSVColorBlackGun</Attribute>
<Attribute Type="Check">NotFractal</Attribute>
<Attribute Type="Check">BlackGunSizeAttribute</Attribute>
</SimpleObject>
<SimpleObject Name="HaarBlackGun" ID="4" Mode="Hard" MergingPolicy="Union">
<Attribute Type="Detect">HaarGun</Attribute>
<Attribute Type="Detect">MyBlobAttribute</Attribute>
</SimpleObject>
</SimpleObjectBase>
<RelationLib>
</RelationLib>
<ComplexObjectBase>
</ComplexObjectBase>
gun_detection.launch
XML
<launch>
<arg name="output" default="screen"/>
<arg name="objectBasePath" default="$(find my_object_recognition_pkg)/config/object_base_example/Simple_Gun.xml"/>
<node name="extended_object_detection" pkg="extended_object_detection" type="extended_object_detection_node" output="screen">
<param name="objectBasePath" value="$(arg objectBasePath)"/>
<param name="videoProcessUpdateRate" value="5"/>
<param name="screenOutput" value="false"/>
<param name="publishImage" value="true"/>
<param name="publishMarkers" value="true"/>
<param name="subscribeDepth" value="false"/>
<param name="maxContourPoints" value="-1"/>
<rosparam param="selectedOnStartSimple">[3]</rosparam>
<rosparam param="selectedOnStartComplex">[-1]</rosparam>
</node>
</launch>
我们仅设置ID=3来稍微清理一下。
XML
roslaunch my_object_recognition_pkg gun_detection.launch
您应该看到,现在桌子边框不再被检测到。当然,限制检测尺寸也有其缺点,因为如果机器人太近,枪将停止检测它。您需要了解机器人的正常工作条件才能做出这些决定。
练习 3.3.2
- 为甜甜圈创建一个 HSV 检测器。
- 将修改添加到 Simple_Gun.xml 和 gun_detector.launch 以检测枪和甜甜圈。
Parts to Add to Simple_Gun.xml
XML
<Attribute Name="HSVColorPinkDonut" Type="HSVColor" Hmin="68" Hmax="179" Smin="79" Smax="173" Vmin="142" Vmax="255"/>
<Attribute Name="DonutSizeAttribute" Type="Size" MinAreaPc="0.00" MaxAreaPc="100"/>
...
<SimpleObject Name="PinkDonut" ID="4">
<Attribute Type="Detect">HSVColorPinkDonut</Attribute>
<Attribute Type="Check">NotFractal</Attribute>
<Attribute Type="Check">DonutSizeAttribute</Attribute>
</SimpleObject>
Parts to Change in the gun_detection.launch
XML
<rosparam param="selectedOnStartSimple">[3,4]</rosparam>
简单对象:HaarBlackGun
这里,它使用 HaarGun 属性,我们还会寻找斑点。如果 BOTH 检测到某些东西(HARD 模式),我们会将两个边界框合并为一个。添加此斑点检测是为了避免 Haar Cascade 检测到任何地方的"枪",例如桌子侧面、桌腿、地平线或非常小的黑色伪影。
Haar in a Nutshell
基本的 Haar 操作是:
- 我们获取一张图像并提取所有基本特征(之前图像中显示的黑白图案)。
- 我们以群集或群组的形式执行此操作,以便更快地处理大图像。
- 然后,我们使用 Adaboost 训练来选择能够提供更好结果(检测我们想要的内容)的特征。
- 它使用级联分类器(本质上是机器学习)来完成繁重的工作。
要了解 Haar 的来源,请查看这篇论文:Paper Haar。
Use Haar
下载已由 Saksham00799 训练并创建的 Haar 分类器,在本例中用于检测枪支:
XML
roscd my_object_recognition_pkg/config/object_base_example
git clone https://github.com/Saksham00799/opencv-gun-detection
mv opencv-gun-detection gun_haar
修改 Simple_Gun.xml 以查找 Haar 文件:
Simple_Gun.xml
XML
<?xml version="1.0" ?>
<AttributeLib>
<Attribute Name="HistColorPortalGun" Type="HistColor" Histogram="histograms/PortalGun.yaml"/>
<Attribute Name="PotalGunSize" Type="Size" MinAreaPc="0.00" MaxAreaPc="100"/>
<Attribute Name="NotFractal" Type="Size" MinAreaPc="0.5" MaxAreaPc="100"/>
<Attribute Name="HistColorBanana" Type="HistColor" Histogram="histograms/Banana.yaml"/>
<Attribute Name="BananaSize" Type="Size" MinAreaPc="0.05" MaxAreaPc="100"/>
<Attribute Name="HSVColorBlackGun" Type="HSVColor" Hmin="0" Hmax="0" Smin="0" Smax="0" Vmin="22" Vmax="61"/>
<Attribute Name="BlackGunSizeAttribute" Type="Size" MinAreaPc="0.01" MaxAreaPc="20"/>
<Attribute Name="HSVColorPinkDonut" Type="HSVColor" Hmin="68" Hmax="179" Smin="79" Smax="173" Vmin="142" Vmax="255"/>
<Attribute Name="DonutSizeAttribute" Type="Size" MinAreaPc="0.00" MaxAreaPc="100"/>
<Attribute Name="HaarGun" Type="HaarCascade" Cascade="gun_haar/classifier/cascade.xml"/>
<Attribute Name="MyBlobAttribute" Type="Blob" minThreshold="54" maxThreshold="125" blobColor="0" minArea="1500" minCircularity="0.03" minConvexity="0.64" minInertiaRatio="0.00"/>
</AttributeLib>
<SimpleObjectBase>
<SimpleObject Name="PortalGun" ID="1">
<Attribute Type="Detect">HistColorPortalGun</Attribute>
<Attribute Type="Check">PotalGunSize</Attribute>
<Attribute Type="Check">NotFractal</Attribute>
</SimpleObject>
<SimpleObject Name="Banana" ID="2">
<Attribute Type="Detect">HistColorBanana</Attribute>
<Attribute Type="Check">BananaSize</Attribute>
</SimpleObject>
<SimpleObject Name="BlackGun" ID="3">
<Attribute Type="Detect">HSVColorBlackGun</Attribute>
<Attribute Type="Check">NotFractal</Attribute>
<Attribute Type="Check">BlackGunSizeAttribute</Attribute>
</SimpleObject>
<SimpleObject Name="PinkDonut" ID="4">
<Attribute Type="Detect">HSVColorPinkDonut</Attribute>
<Attribute Type="Check">NotFractal</Attribute>
<Attribute Type="Check">DonutSizeAttribute</Attribute>
</SimpleObject>
<SimpleObject Name="HaarBlackGun" ID="5" Mode="Hard" MergingPolicy="Union">
<Attribute Type="Detect">HaarGun</Attribute>
<Attribute Type="Detect">MyBlobAttribute</Attribute>
</SimpleObject>
</SimpleObjectBase>
<RelationLib>
</RelationLib>
<ComplexObjectBase>
</ComplexObjectBase>
gun_detection.launch
XML
<launch>
<arg name="output" default="screen"/>
<arg name="objectBasePath" default="$(find my_object_recognition_pkg)/config/object_base_example/Simple_Gun.xml"/>
<node name="extended_object_detection" pkg="extended_object_detection" type="extended_object_detection_node" output="screen">
<param name="objectBasePath" value="$(arg objectBasePath)"/>
<param name="videoProcessUpdateRate" value="5"/>
<param name="screenOutput" value="false"/>
<param name="publishImage" value="true"/>
<param name="publishMarkers" value="true"/>
<param name="subscribeDepth" value="false"/>
<param name="maxContourPoints" value="-1"/>
<rosparam param="selectedOnStartSimple">[5]</rosparam>
<rosparam param="selectedOnStartComplex">[-1]</rosparam>
</node>
启动检测器并查看其性能:
XML
roslaunch my_object_recognition_pkg gun_detection.launch
这是没有属性的:
XML
<Attribute Type="Detect">MyBlobAttribute</Attribute>
正如你所见,Haar 探测器在甜甜圈、香蕉上,甚至桌子侧面都看到了很多可能的枪。
这就是我们添加 MyBlobAttribute 属性的原因。这将检测限制为仅检测大块黑白斑点。
练习 3.3.3
- 下载 Haar 瓶分类器,看看它是否适用于 monster_can 饮料。
- 您可以从 https://github.com/jemgunay/bottle-classifier.git 下载。您还可以在本课程的解决方案 Git 中找到 lint 版本。在完整的 Git 中,您可以检测杰克丹尼、伏特加、红牛和其他饮料。
XML
roscd my_object_recognition_pkg/config/object_base_example
git clone https://github.com/jemgunay/bottle-classifier.git
- 另外,请注意,当您尽可能靠近并且 PR2 机器人的躯干最大程度降低时,分类器的效果最佳。使用控制器 GUI 降低躯干并稍微向下倾斜头部以获得最佳性能。
Simple_Gun.xml 的新增内容
XML
<Attribute Name="HaarBottle" Type="HaarCascade" Cascade="bottle-classifier/classifier/classifier_monster/classifier/cascade.xml"/>
...
<SimpleObject Name="HaarBottle" ID="6" Mode="Hard" MergingPolicy="Union">
<Attribute Type="Detect">HaarBottle</Attribute>
</SimpleObject>
编辑 gun_detection.launch
XML
<rosparam param="selectedOnStartSimple">[5,6]</rosparam>
请注意,在这种情况下,我们不使用 blob。这是因为它不是进行检测所必需的,并且没有误报。如果一切顺利,您应该会得到类似于下图的结果:
复杂对象:
但在项目中,我们会使用复杂对象。它本质上是结合简单物体来检测更复杂的物体,例如,持枪的人。但那是最终项目。
请参阅下面的示例,了解如何完成此操作:
ArmedPerson.xml
XML
<?xml version="1.0" ?>
<AttributeLib>
<Attribute Name="HaarGun" Type="HaarCascade" Cascade="gun_haar/classifier/cascade.xml"/>
<Attribute Name="MyBlobAttribute" Type="Blob" minThreshold="54" maxThreshold="125" blobColor="0" minArea="1500" minCircularity="0.03" minConvexity="0.64" minInertiaRatio="0.00"/>
<Attribute Name="CnnPerson" Type="Dnn" framework="tensorflow" weights="ssd_mobilenet_v1_coco_2017_11_17/frozen_inference_graph.pb" config="ssd_mobilenet_v1_coco_2017_11_17/config.pbtxt" labels="ssd_mobilenet_v1_coco_2017_11_17/mscoco_label_map.pbtxt" inputWidth="300" inputHeight="300" Probability="0.5" obj_id="1"/>
</AttributeLib>
<SimpleObjectBase>
<SimpleObject Name="HaarBlackGun" ID="3" Mode="Hard" MergingPolicy="Union">
<Attribute Type="Detect">HaarGun</Attribute>
<Attribute Type="Detect">MyBlobAttribute</Attribute>
</SimpleObject>
<SimpleObject Name="CnnPerson" ID="67">
<Attribute Type="Detect">CnnPerson</Attribute>
</SimpleObject>
</SimpleObjectBase>
<RelationLib>
<RelationShip Type="SpaceIn" Name="in"/>
</RelationLib>
<ComplexObjectBase>
<ComplexObject ID="10" Name="ArmedPerson">
<SimpleObject Class="CnnPerson" InnerName="Person"/>
<SimpleObject Class="HaarBlackGun" InnerName="Gun"/>
<Relation Obj1="Gun" Obj2="Person" Relationship="in"/>
</ComplexObject>
</ComplexObjectBase>