在ROS中使用OpenCV进行视觉编程(Python版本)

前言

下午翻了一遍《机器人系统设计与制作-Python语言实现》,就是《Learning Robotics Using Python》的中文版。

这本书主要讲的还是从头构建一个ROS机器人。

书的前半部分(第一章到第六章)讲的是机器人的硬件搭建,从设计需求到硬件选择,再到硬件架构,最后是传动转置和传感器的编程实现,还涉及了机器人的二维CAD图和三维模型的制作。

书的后半部分(第七章到第十二章)讲的是环境感知和人机交互,包括视觉传感器的使用,语音识别及合成,使用AIML搭建聊天机器人,最后是进行硬件的对接。还涉及了PyQt用户界面的设计实现和机器人的标定测试。

关于书本章节介绍,YouTube上有个视频专门进行了介绍。

Learning Robotics using Python

内容很丰富,对我帮助最大的还是第七章:视觉传感器在Python和ROS中的编程方法。里面介绍了如何在ROS中结合OpenCV、OpenNI和PCL进行视觉处理。PCL的点云处理这一块作者是拿现有的ros包直接带过的,讲的不多。目前我也不是很急着用PCL,刚好在另外一本书《ROS机器人高效编程》(《Effective Robotics Programming with ROS》的中文版),有涉及到ROS中点云处理这部分。不虚。

安装

OpenCV

安装ros-indigo-desktop-full时就附带安装了OpenCV、Python、封装类等。

如果没有:

sudo apt-get install ros-indigo-vision-opencv

如果只想装OpenCV Python封装:

sudo apt-get install python-opencv

OpenCV 使用案例

使用OpenCV接口读取和显示图像

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env python
import numpy as np
import cv2

img = cv2.imread('robot.jpg', 0)

cv2.imshow('image', img)
cv2.waitKey(0)

cv2.destroyAllWindows()

使用网络摄像头获取图像

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python
import numpy as np
import cv2

cap = cv2.VideoCapture(0)

while (True):
# Capture frame-by-frame
ret, frame = cap.read()
# Display the resulting frame
cv2.imshow('frame', frame)
if cv2.waitKey(10):
break

OpenNI

OpenNI不多介绍,就是一个跨平台的交互框架,通过它就可以访问各种硬件设备了,摄像头啊麦克风啊等等。

Ubuntu下的安装方式

sudo apt-get install ros-indigo-openni-launch

PCL

PCL已经集成在ROS中了,PCL是ROS的3D处理程序的主要构成部分。

使用ROS、OpenCV和OpenNI进行Kinect的Python编程

启动OpenNI驱动

roslaunch openni_launch openni.launch

使用ROS的image_view工具显示RGB图像

rosrun image_view image_view image:=/camera/rgb/image_color

OpenCV的ROS接口

ROS的vision_opencv资源栈包含了完整的OpenCV库及其对ROS的接口。提供的软件包如下:

  • cv_bridge:含有Cv_Bridge类,该类用于实现ROS图像信息和OpenCV图像数据(IplImage和Mat型)之间的转换,类型的转换是双向的。
  • image_geometry:是用于处理图像和像素几何问题的方法的集合。

OpenCV-ROS接口

利用OpenCV创建ROS包

catkin-create-pkg sample_opencv_pkg sensor_msgs cv_bridge rospy std_msgs

使用Python、ROS和cv_bridge显示Kinect图像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# 导入了rospy、sys、cv2、sensor_msgs、cv_bridge、和numpy模块
# sensor_msgs依赖项导入了Image和CameraInfo两种ROS数据类型
# cv_bridge导入Cv_Bridge类,这个类能实现ROS图像数据和OpenCV数据的互相转换

import rospy
import sys
import cv2
import cv2.cv as cv
from sensor_msgs.msg import Image, CameraInfo
from cv_bridge import CvBridge, CvBridgeError
import numpy as np


# 演示cv_bridge功能
class cvBridgeDemo():
def __init__(self):
self.node_name = "cv_bridge_demo"

# 初始化ROS节点
rospy.init_node(self.node_name)

# 关闭过程处理
rospy.on_shutdown(self.cleanup)

# 创建RGB图像的OpenCV显示窗口
self.cv_window_name = self.node_name
cv.NamedWindow(self.cv_window_name, cv.CV_WINDOW_NORMAL)
cv.MoveWindow(self.cv_window_name, 25, 75)

# 创建显示深度图像的窗口
cv.NamedWindow("Depth Image", cv.CV_WINDOW_NORMAL)
cv.MoveWindow("Depth Image", 25, 350)

# 创建cv_bridge对象
self.bridge = CvBridge()

# 订阅相机数据消息
# 回调函数
self.image_sub = rospy.Subscriber("/camera/rgb/image_color", Image, self.image_callback)
self.depth_sub = rospy.Subscriber("/camera/depth/image_raw", Image, self.depth_callback)
rospy.loginfo("Waiting for image topics...")


# 获取彩色图像的回调函数
def image_callback(self, ros_image):
# 使用cv_bridge转换ROS图像至OpenCV格式
try:
frame = self.bridge.imgmsg_to_cv(ros_image, "bgr8")
except CvBridgeError, e:
print
e

# 图像利用cv2函数转换到numpy矩阵
# 获取numpy矩阵
frame = np.array(frame, dtype=np.uint8)

# 使用process_image函数进行处理
display_image = self.process_image(frame)

# 显示图像
cv2.imshow(self.node_name, display_image)

# 处理键盘命令
self.keystroke = cv.WaitKey(5)
if 32 <= self.keystroke and self.keystroke < 128:
cc = chr(self.keystroke).lower()
if cc == 'q':
# 用户按q进行退出
rospy.signal_shutdown("User hit q key to quit.")

# 深度图像回调函数
def depth_callback(self, ros_image):
# 使用cv_bridge转换ROS图像至OpenCV格式
try:
# 深度图像是单通道32位图像
depth_image = self.bridge.imgmsg_to_cv(ros_image, "32FC1")
except CvBridgeError, e:
print e

# 图像利用cv2函数转换到numpy矩阵
# 获取numpy矩阵
depth_array = np.array(depth_image, dtype=np.float32)

# 归一化处理深度图像
cv2.normalize(depth_array, depth_array, 0, 1, cv2.NORM_MINMAX)

# 处理深度图像
depth_display_image = self.process_depth_image(depth_array)

# 显示结果
cv2.imshow("Depth Image", depth_display_image)

# 彩色图像处理函数
def process_image(self, frame):
# Convert to grayscale
grey = cv2.cvtColor(frame, cv.CV_BGR2GRAY)
# Blur the image
grey = cv2.blur(grey, (7, 7))

# Compute edges using the Canny edge filter
edges = cv2.Canny(grey, 15.0, 30.0)
return edges


def process_depth_image(self, frame):
# Just return the raw image for this demo
return frame


def cleanup(self):
print "Shutting down vision node."
cv2.destroyAllWindows()


def main(args):
try:
cvBridgeDemo()
rospy.spin()
except KeyboardInterrupt:
print "Shutting down vision node."
cv.DestroyAllWindows()


if __name__ == '__main__':
main(sys.argv)

开启驱动和节点

roslaunch openni_launch openni.launch

rosrun sample_opencv_pkg cv_bridge_demo.py

具体效果,我也没跑过,写个过程留给后面作参考。