前言

第一节:
基于paddledetection在ROS中搭建红绿灯检测控制车模运动(1)—— 环境搭建准备以及软件安装
第二节:
基于paddledetection在ROS中搭建红绿灯检测控制车模运动(2)—— 数据集制作以及训练
第三节:
基于paddledetection在ROS中搭建红绿灯检测控制车模运动(3)—— 使用训练结果在ros中仿真红绿灯控制小车运动

项目地址

训练部分aistudio地址:传动门
racecar仿真软件下载地址:传送门
本地项目下载地址:传送门

本地部署paddledetection

本文假设你已经完成了前两节的操作,在本地创建了proj6_lightContrl工程文件夹。在aistudio平台上完成了模型的训练,训练完成后在下面路径下会有训练好的模型文件:

home/aistudio/PaddleDetection-release-0.3/output/ssd_mobilenet_v1_voc/


在aistudio上执行文件转换命令

#转换模型
%cd ~/PaddleDetection-release-0.3/
!python -u tools/export_model.py -c configs/ssd/ssd_mobilenet_v1_voc.yml

文件输出在下面目录中:

/home/aistudio/PaddleDetection-release-0.3/output/ssd_mobilenet_v1_voc

将.pdparams文件转换并从aistudio上下载这些模型文件下来:

__model__ __params__ yml

在这里插入图片描述
本地在工程文件下下载paddledetection,打开pycahrm的终端:

cd 你的路径/proj6_lightContrl
mkdir -p /PaddleDetection-release-0.3output/light/my_light_best_model
git clone https://github.com/PaddlePaddle/PaddleDetection.git
pip install -r requirements.txt

将下载的模型文件拷贝到/PaddleDetection-release-0.3output/light/my_light_best_model下(我这里还放了几个我搭建别的模型下载的文件)

然后像第二节一样修改configs/ssd/ssd_mobilenet_v1_voc.yml这个文件,主要修改位置:

  • trainReader下的anno_path和dataset_dir

anno_path: train.txt
dataset_dir: dataset/my_light

  • EvalReader下的anno_path和dataset_dir

anno_path: val.txt
dataset_dir: dataset/my_light

  • TestReader下的anno_path和dataset_dir:

dataset_dir: dataset/my_light
anno_path: label_list.txt

然后本地的dataset文件里面像aistudio上一样放置dataset文件(其实只要一个label_list.txt文件就行):
在这里插入图片描述
最后修改PaddleDetection-release-0.3/deploy/python/infer.py该文件:
在main中添加ros代码:

    while not rospy.is_shutdown():
        if follower.flag == 1:
            results = detector.predict("temp.jpg", FLAGS.threshold)
            if len(results['boxes']) != 0:
                x1, y1, x2, y2 = results['boxes'][0][2:]
                cv2.rectangle(follower.img, (x1, y1), (x2, y2), (0, 255, 0), 5)
                if results['boxes'][0][0] - 1 < 0.5:
                    print("绿灯")
                    follower.stop = False
                elif results['boxes'][0][0] - 1 > 0.5:
                    print("红灯")
                    follower.stop = True
            else:
                print("无灯")
                follower.stop = False

            if follower.stop is False:
                follower.deal_image(follower.img)
            cv2.imshow("tu", follower.img)
            cv2.waitKey(3)
            follower.flag = 0

    cv2.destroyAllWindows()
    rospy.spin()

创建一个巡线类:

class Follower:

    def __init__(self):
        self.bridge = cv_bridge.CvBridge()
        # cv2.namedWindow("window", 1)
        self.image_sub = rospy.Subscriber('/camera/zed/rgb/image_rect_color',
                                          sensorImage, self.image_callback)
        self.img = np.zeros([528,400,3],np.uint8)
        self.ack = AckermannDriveStamped()
        self.cmd_vel_pub = rospy.Publisher(
            "/vesc/low_level/ackermann_cmd_mux/input/teleop", AckermannDriveStamped, queue_size=1)
        self.stop = False
        self.flag = 0
    def image_callback(self, msg):
        # END CONTROL
        self.img = self.bridge.imgmsg_to_cv2(msg, desired_encoding='bgr8')
        if self.flag == 0:
            cv2.imwrite("temp.jpg",self.img)
            self.flag = 1




    def speed_contrl(self,_speed,_angle):
        self.ack.drive.speed = _speed
        angle = _angle
        max_angle = 0.6
        if angle > max_angle:
            angle = max_angle
        elif angle < -max_angle:
            angle = -max_angle

        self.ack.drive.steering_angle = angle
        self.cmd_vel_pub.publish(self.ack)

    def deal_image(self,img):
        hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
        lower_yellow = numpy.array([0, 0, 100])
        upper_yellow = numpy.array([180, 30, 255])
        mask = cv2.inRange(hsv, lower_yellow, upper_yellow)
        h, w, d = img.shape
        search_top = int(h / 2 + 80)
        search_bot = int(h - 20)
        mask[0:search_top, 0:w] = 0
        mask[search_bot:h, 0:w] = 0
        M = cv2.moments(mask)
        if M['m00'] > 0:
            cx = int(M['m10'] / M['m00'])
            cy = int(M['m01'] / M['m00'])
            cv2.circle(mask, (cx, cy), 20, (0, 0, 255), -1)
            # BEGIN CONTROL
            err = cx - w / 2
            self.ack.drive.speed = 0.3
            angle = -float(err) / 500

            max_angle = 0.6
            if angle > max_angle:
                angle = max_angle
            elif angle < -max_angle:
                angle = -max_angle

            self.ack.drive.steering_angle = angle
            self.cmd_vel_pub.publish(self.ack)

新增包含头文件:

sys.path.append("~/catkin_workspace/install/lib/python3/dist-packages/")
import cv_bridge
from sensor_msgs.msg import Image as sensorImage
from ackermann_msgs.msg import AckermannDriveStamped

代码讲解

上面新增的三段代码其实很简单:

  • 第一段代码主要实在main中加了一个死循环来一直处理图像数据,只有用户ctrl c杀死了ros的进程才退出并摧毁cv2创建的窗口,在死循环中主要调用了paddle的pridect函数来处理在中断中存储在本地的temp.jpg图像,然后根据预测结果修改对应的标志位,红灯停车绿灯继续走,无灯也继续走。
  • 第二段代码主要是处理ros消息,接收图像进入回调函数,处理图像控制小车巡线,还有一个小车速度和方向控制的底层函数,小车根据图像巡线的原理也很简单,对跑道进行色相环处理提取白色和蓝色中的白色然后二值化得到黑白的跑道,白色因为是1所以图像的重心位置就是白色的重心位置也就是跑道的重心位置,然后根据这个反馈控制小车一直走在跑道的中心上。
  • 第三段代码就是包含头文件了

    运行测试

    启动环境:
    roslaunch racecar_gazebo racecar_normal_light_runway.launch
    
    启动红绿灯控制:
    python -u deploy/python/infer.py --model_dir output/light/my_light_best_model