运动控制

1.视觉处理

本章我们来学习如何控制小车运动,我们首先看下下面的这个需求.在小车的前面摆放着一个黄色的雪糕筒,我们希望当我移动雪糕筒的时候,小车也能够跟着移动,这个该如何来做呢!

我们可以用所学过的知识来做, 首先我们要通过摄像来识别出黄色的雪糕筒,这个可能得用到我们前面所学的HSV颜色空间.

根据雪糕筒,小车可做的运动有如下3种情况:

  1. 若雪糕筒,出现在画面的左边,则小车左转.
  2. 若雪糕筒出现在画面的中间区域,则小车直行
  3. 若雪糕筒出现在画面的右边,则小车右转

依据上面的分析,我们其实可以先来编写机器视觉部分的代码,示例参考如下:

import cv2 as cv

capture = cv.VideoCapture(0)

ok,frame = capture.read()

lowerb = (23,43,46)
upperb = (34,255,255)

while ok:

    hsv_frame =cv.cvtColor(frame,cv.COLOR_BGR2HSV)

    mask = cv.inRange(hsv_frame,lowerb,upperb)

    cv.imshow("mask",mask)
    cv.imshow("frame",frame)
    cv.waitKey(10)
    ok, frame = capture.read()

2. 小车跟随颜色块

这个案例,我们让小车跟随黄色块进行运动,当然如果大家在地面上贴一根黄色的线,小车也可以跟随黄色线进行运动.

当前这个案例相对来说,还是比较容易,后面我们会在这个案例的基础上进一步学习.

#! /usr/bin/env python
# encoding: utf-8
import rospy
from geometry_msgs.msg import Twist
import cv2 as cv

def shutdown():
    twist = Twist()
    twist.linear.x = 0
    twist.angular.z = 0
    cmd_vel_Publisher.publish(twist)
    print "stop car..."

if __name__ == '__main__':
    rospy.init_node("yellow_follow")

    # 当程序退出
    rospy.on_shutdown(shutdown);

    # ros控制的频率
    rate = rospy.Rate(100)

    # 定义publisher : cmd_vel
    cmd_vel_Publisher = rospy.Publisher("/cmd_vel",Twist,queue_size=1)



    capture = cv.VideoCapture(0)
    print capture.isOpened()

    ok,frame = capture.read()

    lowerb = (23,43,46)
    upperb = (34,255,255)

    height,width = frame.shape[0:2]
    screen_center = width / 2
    offset = 50
    while not rospy.is_shutdown():

        # 将图像转成HSV颜色空间
        hsv_frame = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
        # 基于颜色的物品提取
        mask = cv.inRange(hsv_frame,lowerb,upperb)
        # 找出面积最大的区域
        _,contours,_ = cv.findContours(mask,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)

        maxArea = 0
        maxIndex = 0
        for i,c in enumerate(contours):
            area = cv.contourArea(c)
            if area > maxArea:
                maxArea = area
                maxIndex = i
        # 绘制
        cv.drawContours(frame,contours,maxIndex,(0,0,255),2)
        # 获取外切矩形
        x,y,w,h = cv.boundingRect(contours[maxIndex])
        cv.rectangle(frame,(x,y),(x+w,y+h),(255,255,0),2)

        # 获取外切矩形的中心像素点
        center_x = int(x + w/2)
        center_y = int(y + h/2)
        cv.circle(frame,(center_x,center_y),5,(0,0,255),-1)

        # 判断当前小车应该是左转还是右转还是直行
        twist = Twist()
        if center_x < screen_center - offset:
            twist.linear.x = 0.05
            twist.angular.z = 0.2
            print "turn left"
        elif center_x >= screen_center - offset and center_x <= screen_center + offset:
            twist.linear.x = 0.1
            twist.angular.z = 0.0
            print "go"
        elif center_x >  screen_center + offset:
            twist.linear.x = 0.05
            twist.angular.z = -0.2
            print "turn right"
        else:
            twist.linear.x = 0
            twist.angular.z = 0
            print "stop"

        # 将速度信息发送出去
        cmd_vel_Publisher.publish(twist)

        cv.imshow("mask",mask)
        cv.imshow("frame",frame)
        cv.waitKey(1)
        rate.sleep()

        ok, frame = capture.read()