空飛ぶロボットのつくりかた

ロボットをつくるために必要な技術をまとめます。ロボットの未来についても考えたりします。

ROS x Depthセンサで遊んでみる

やりたいこと

Depthセンサで取得したデータで遊んでみる

f:id:robonchu:20180227205357j:plain

xtionやkinectで使えるros package: rgbd_launch - ROS Wiki

今回はsoftkineticを用いる

設定

robonchu.hatenablog.com

RGBDの情報を使って亀を制御してみる

ピンクと黄色のボールで亀を制御

  • ピンクのボールをカメラに近づけたり遠ざけたりすることで前進、後退を制御

  • 黄色のボールをカメラに近づけたり遠ざけたりすることで右回転、左回転を制御

鬼畜の操作方法...操作鬼難しい笑

f:id:robonchu:20180227211152p:plain

ざっくりとした流れ

  1. 色相抽出

  2. 短形検出

  3. 短形部分の距離計算

  4. 色ごとの距離で制御

構成

├── CMakeLists.txt
├── launch
│   └── depth_turtlesim.launch
├── package.xml
└── scripts
    └── teleop_depth.py

teleop_depth.py

完全に深夜のテンションで書いたとんでもないくらい適当なコード

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import rospy
import message_filters
import numpy as np
from geometry_msgs.msg import Twist
from sensor_msgs.msg import Image
from sensor_msgs.msg import CameraInfo
import cv2
from cv_bridge import CvBridge, CvBridgeError
 
class DepthTeleopTurtle:
    def __init__(self):
        self.bridge = CvBridge()
         
        rgb_sub = message_filters.Subscriber("/softkinetic_camera/rgb/image_color", Image)
        depth_sub = message_filters.Subscriber("/softkinetic_camera/depth/image_raw", Image)

        # # for extract x,y,z positoin from depth image 
        # camera_info = rospy.wait_for_message("/softkinetic_camera/depth/camera_info", CameraInfo)
        # self.invK = np.linalg.inv(np.array(camera_info.K).reshape(3,3))
        # xyz_point = np.matmul(self.invK, image_point) * z  # z = depth_image[x, y]

        self.mf = message_filters.ApproximateTimeSynchronizer([rgb_sub, depth_sub], 30, 0.5)
        self.mf.registerCallback(self.callback)

        self.pub = rospy.Publisher('turtle1/cmd_vel', Twist, queue_size=10)
 
    def pink_yellow_mask(self, frame):
        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

        lower_light_pink = np.array([168, 100, 100])
        upper_light_pink = np.array([188, 255, 255])
        mask_pink = cv2.inRange(hsv, lower_light_pink, upper_light_pink)

        lower_light_yellow = np.array([20, 50, 50])
        upper_light_yellow = np.array([100, 255, 255])
        mask_yellow = cv2.inRange(hsv, lower_light_yellow, upper_light_yellow)

        return mask_pink, mask_yellow

    def detect_rect(self, mask):
        image, contours, hierarchy  = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
        rects = []
        for contour in contours:
            approx = cv2.convexHull(contour)
            rect = cv2.boundingRect(approx)
            rects.append(np.array(rect))

        if len(rects) > 0:
          rect = max(rects, key=(lambda x: x[2] * x[3])) # w x hの一番大きいリストを抽出
          return rect

    def disp(self, frame, rect_pink, rect_yellow):
        if rect_pink is not None and rect_yellow is not None:
            cv2.rectangle(frame, tuple(rect_pink[0:2]), tuple(rect_pink[0:2] + rect_pink[2:4]), (0, 0, 255), thickness=2)
            cv2.rectangle(frame, tuple(rect_yellow[0:2]), tuple(rect_yellow[0:2] + rect_yellow[2:4]), (0, 255, 0), thickness=2)
        return frame

    def callback(self, rgb_data , depth_data):
        twist = Twist()

        try:
            rgb_image = self.bridge.imgmsg_to_cv2(rgb_data, 'passthrough')
            depth_image = self.bridge.imgmsg_to_cv2(depth_data, 'passthrough')
        except CvBridgeError, e:
            rospy.logerr(e)

        depth_array = np.array(depth_image, dtype=np.float32)
        mask_pink, mask_yellow = self.pink_yellow_mask(rgb_image)

        rect_pink = self.detect_rect(mask_pink)
        rect_yellow = self.detect_rect(mask_yellow)

        # for debug
        frame = self.disp(rgb_image, rect_pink, rect_yellow)
        cv2.imshow('frame',frame)
        k = cv2.waitKey(5) & 0xFF
        if k == 27:
            sys.exit()

        low_weight = 0.8
        high_weight = 1.2

        if rect_pink is not None and rect_yellow is not None:
            # trim depth
            trim_depth_pink = depth_array[int(rect_pink[0]/2.0*low_weight):int((rect_pink[0]+rect_pink[2])/2.0*high_weight), int(rect_pink[1]/2.0*low_weight):int((rect_pink[1]+rect_pink[3])/2.0*high_weight)]

            # reject zero and non
            valid_pink = trim_depth_pink[np.nonzero(trim_depth_pink)]
            valid_pink = [x for x in valid_pink if not np.isnan(x)]

            # trim depth
            trim_depth_yellow = depth_array[int(rect_yellow[0]/2.0*low_weight):int((rect_yellow[0]+rect_yellow[2])/2.0*high_weight), int(rect_yellow[1]/2.0*low_weight):int((rect_yellow[1]+rect_yellow[3])/2.0*high_weight)]

            # reject zero and non
            valid_yellow = trim_depth_yellow[np.nonzero(trim_depth_yellow)]
            valid_yellow = [x for x in valid_yellow if not np.isnan(x)]

            # teleop turtle 
            # if you close pink ball, turtle back
            # if you close yellow ball, turtle turn right
            if len(valid_pink) != 0 and len(valid_yellow) != 0:
                z_pink = min(valid_pink)
                print "pink_go:{}".format(z_pink)
                if z_pink > 0.30:
                  twist.linear.x  = 0.0
                elif z_pink > 0.20:
                  twist.linear.x  = 1.5
                elif z_pink > 0.0:
                  twist.linear.x  = -1.5
                else:
                  twist.linear.x  = 0.0

                z_yellow = min(valid_yellow)
                print "yellow_rot:{}".format(z_yellow)
                if z_yellow > 0.30:
                  twist.angular.z  = 0.0
                elif z_yellow > 0.20:
                  twist.angular.z  = 1.5
                elif z_yellow > 0.0:
                  twist.angular.z = -1.5
                else:
                  twist.angular.z  = 0.0

                self.pub.publish(twist)
 
def main():
    rospy.init_node('depth_teleop_turtle')
    DepthTeleopTurtle()
    rospy.spin()
 
if __name__ == "__main__":
    main()

参考:

pythonで最大値、最小値を求める色々 - BlankTar

launch

<launch>
    <!-- Turtlesim Node -->
    <node pkg="turtlesim" type="turtlesim_node" name="sim"/>

    <!-- depth node -->
    <include file="$(find softkinetic_camera)/launch/softkinetic_camera_demo.launch" />

    <node pkg="teleop_depth" type="teleop_depth.py" name="teleop_depth" 
    launch-prefix="xterm -font r14 -bg darkblue -geometry 113x30+503+80 -e"
    required="true" 
    />    
</launch>

CMakeLists.txt

cmake_minimum_required(VERSION 2.8.3)
project(teleop_depth)

find_package(catkin REQUIRED COMPONENTS
  rospy
  std_msgs
  geometry_msgs
  sensor_msgs
)

catkin_package(
   CATKIN_DEPENDS std_msgs geometry_msgs sensor_msgs rospy
)

package.xml

  <buildtool_depend>catkin</buildtool_depend>
  <build_depend>geometry_msgs</build_depend>
  <build_depend>rospy</build_depend>
  <build_depend>std_msgs</build_depend>
  <build_depend>sensor_msgs</build_depend>

  <run_depend>geometry_msgs</run_depend>
  <run_depend>rospy</run_depend>
  <run_depend>std_msgs</run_depend>
  <run_depend>sensor_msgs</run_depend>

次のアクション

softkineticのdepthセンサ情報でOpen3Dを動かしてみよう〜楽しみ♪

参考

配布されるTopic

/softkinetic_camera/depth/camera_info
/softkinetic_camera/depth/image_raw
/softkinetic_camera/depth/image_raw/compressed
/softkinetic_camera/depth/image_raw/compressed/parameter_descriptions
/softkinetic_camera/depth/image_raw/compressed/parameter_updates
/softkinetic_camera/depth/image_raw/compressedDepth
/softkinetic_camera/depth/image_raw/compressedDepth/parameter_descriptions
/softkinetic_camera/depth/image_raw/compressedDepth/parameter_updates
/softkinetic_camera/depth/image_raw/theora
/softkinetic_camera/depth/image_raw/theora/parameter_descriptions
/softkinetic_camera/depth/image_raw/theora/parameter_updates
/softkinetic_camera/depth/points
/softkinetic_camera/parameter_descriptions
/softkinetic_camera/parameter_updates
/softkinetic_camera/rgb/camera_info
/softkinetic_camera/rgb/image_color
/softkinetic_camera/rgb/image_color/compressed
/softkinetic_camera/rgb/image_color/compressed/parameter_descriptions
/softkinetic_camera/rgb/image_color/compressed/parameter_updates
/softkinetic_camera/rgb/image_color/compressedDepth
/softkinetic_camera/rgb/image_color/compressedDepth/parameter_descriptions
/softkinetic_camera/rgb/image_color/compressedDepth/parameter_updates
/softkinetic_camera/rgb/image_color/theora
/softkinetic_camera/rgb/image_color/theora/parameter_descriptions
/softkinetic_camera/rgb/image_color/theora/parameter_updates
/softkinetic_camera/rgb/image_mono
/softkinetic_camera/rgb/image_mono/compressed
/softkinetic_camera/rgb/image_mono/compressed/parameter_descriptions
/softkinetic_camera/rgb/image_mono/compressed/parameter_updates
/softkinetic_camera/rgb/image_mono/compressedDepth
/softkinetic_camera/rgb/image_mono/compressedDepth/parameter_descriptions
/softkinetic_camera/rgb/image_mono/compressedDepth/parameter_updates
/softkinetic_camera/rgb/image_mono/theora
/softkinetic_camera/rgb/image_mono/theora/parameter_descriptions
/softkinetic_camera/rgb/image_mono/theora/parameter_updates

提供するサービス

/softkinect_rviz/get_loggers
/softkinect_rviz/reload_shaders
/softkinect_rviz/set_logger_level
/softkinect_tf/get_loggers
/softkinect_tf/set_logger_level
/softkinetic_camera/depth/image_raw/compressed/set_parameters
/softkinetic_camera/depth/image_raw/compressedDepth/set_parameters
/softkinetic_camera/depth/image_raw/theora/set_parameters
/softkinetic_camera/get_loggers
/softkinetic_camera/rgb/image_color/compressed/set_parameters
/softkinetic_camera/rgb/image_color/compressedDepth/set_parameters
/softkinetic_camera/rgb/image_color/theora/set_parameters
/softkinetic_camera/rgb/image_mono/compressed/set_parameters
/softkinetic_camera/rgb/image_mono/compressedDepth/set_parameters
/softkinetic_camera/rgb/image_mono/theora/set_parameters
/softkinetic_camera/set_logger_level
/softkinetic_camera/set_parameters

利用できるパラメータ

/softkinetic_camera/camera_link
/softkinetic_camera/color_compression
/softkinetic_camera/color_frame_format
/softkinetic_camera/color_frame_rate
/softkinetic_camera/confidence_threshold
/softkinetic_camera/depth/image_raw/compressed/format
/softkinetic_camera/depth/image_raw/compressed/jpeg_quality
/softkinetic_camera/depth/image_raw/compressed/png_level
/softkinetic_camera/depth/image_raw/compressedDepth/depth_max
/softkinetic_camera/depth/image_raw/compressedDepth/depth_quantization
/softkinetic_camera/depth/image_raw/compressedDepth/png_level
/softkinetic_camera/depth/image_raw/theora/keyframe_frequency
/softkinetic_camera/depth/image_raw/theora/optimize_for
/softkinetic_camera/depth/image_raw/theora/quality
/softkinetic_camera/depth/image_raw/theora/target_bitrate
/softkinetic_camera/depth_frame_format
/softkinetic_camera/depth_frame_rate
/softkinetic_camera/depth_mode
/softkinetic_camera/depth_optical_frame
/softkinetic_camera/enable_color
/softkinetic_camera/enable_depth
/softkinetic_camera/far_plane
/softkinetic_camera/hfov
/softkinetic_camera/limit_max
/softkinetic_camera/limit_min
/softkinetic_camera/min_neighbours
/softkinetic_camera/near_plane
/softkinetic_camera/rgb/image_color/compressed/format
/softkinetic_camera/rgb/image_color/compressed/jpeg_quality
/softkinetic_camera/rgb/image_color/compressed/png_level
/softkinetic_camera/rgb/image_color/compressedDepth/depth_max
/softkinetic_camera/rgb/image_color/compressedDepth/depth_quantization
/softkinetic_camera/rgb/image_color/compressedDepth/png_level
/softkinetic_camera/rgb/image_color/theora/keyframe_frequency
/softkinetic_camera/rgb/image_color/theora/optimize_for
/softkinetic_camera/rgb/image_color/theora/quality
/softkinetic_camera/rgb/image_color/theora/target_bitrate
/softkinetic_camera/rgb/image_mono/compressed/format
/softkinetic_camera/rgb/image_mono/compressed/jpeg_quality
/softkinetic_camera/rgb/image_mono/compressed/png_level
/softkinetic_camera/rgb/image_mono/compressedDepth/depth_max
/softkinetic_camera/rgb/image_mono/compressedDepth/depth_quantization
/softkinetic_camera/rgb/image_mono/compressedDepth/png_level
/softkinetic_camera/rgb/image_mono/theora/keyframe_frequency
/softkinetic_camera/rgb/image_mono/theora/optimize_for
/softkinetic_camera/rgb/image_mono/theora/quality
/softkinetic_camera/rgb/image_mono/theora/target_bitrate
/softkinetic_camera/rgb_calibration_file
/softkinetic_camera/rgb_optical_frame
/softkinetic_camera/search_radius
/softkinetic_camera/serial
/softkinetic_camera/use_frustum_culling_filter
/softkinetic_camera/use_passthrough_filter
/softkinetic_camera/use_radius_outlier_filter
/softkinetic_camera/use_serial
/softkinetic_camera/use_voxel_grid_filter
/softkinetic_camera/vfov
/softkinetic_camera/voxel_grid_size