JMU黑客松 - 不喜欢ROS也不喜欢C++的大保底完赛代码

使用残差神经网络ResNet18检测车道线关键点,关键点与中心的偏差作为PID控制器的输入,随后计算出一个角速度,线速度固定0.2m/s,跑圈时间约45秒,发布twist类型的消息控制小车底盘。

关于相机数据流:mipi相机数据自动经过VPS得到512_512像素(拉伸),再申请一个VPS模块将512_512缩小到224*224,送给BPU推理。

可调节的参数在第38行,self.Kp,参数P,比例控制;第49行,self.Kd,参数D,微分控制;第64行,self.twist.linear.x,线速度。

群内演示视频跑圈时间为22秒,调节这三个参数,同样的代码也可达到22秒的成绩,这里就给各个参赛队留一些发挥空间了。(22几乎是45的二分之一,线速度怎么调就不用我多说了吧,只能暗示到这里了!!

代码第36行需要修改模型文件的相对路径,模型文件和源代码在本帖末尾的附件中。

注意:不建议使用这么不讲道理的代码,此时这个节点独占了摄像机资源,正确的做法是使用nodeHub的mipi相机节点,将采集的图像数据以ROS标准图像消息或者零拷贝(hbmem)图像消息进行发布,供需要使用图像数据的其他模块订阅。并且这样就可以多个节点订阅摄像头画面,作不同的视觉分析。-
另外NodeHub有基于C++实现的推理和控制,源码也托管在了github,有进一步要求的同学可参考。

赛道检测:https://developer.horizon.cc/nodehubdetail/184587678518566919-
巡线控制:https://developer.horizon.cc/nodehubdetail/184587678518566923-
MIPI相机驱动:https://developer.horizon.cc/nodehubdetail/168958376283445781

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

import rclpy                                     # ROS2 Python接口库
from rclpy.node import Node                      # ROS2 节点类
from geometry_msgs.msg import Twist              

import cv2
import numpy as np
from time import time
from hobot_vio import libsrcampy
from hobot_dnn import pyeasy_dnn as dnn

class ResNetControlNode(Node):
    def __init__(self, name):
        super().__init__(name)                                          # ROS2节点父类初始化
        self.cmd_vel_pub = self.create_publisher(Twist, "/cmd_vel", 10)   # 创建发布者对象(消息类型、话题名、队列长度)
        self.timer = self.create_timer(0.066, self.timer_callback)  # 创建一个定时器(单位为秒的周期,定时执行的回调函数)
        self.twist = Twist()

        ## 创建VPS和camera等对象,开始图像采集
        # camera对象
        self.cap_mipi = libsrcampy.Camera()
        self.height1, self.width1 = 512, 512
        self.height2, self.width2 = 720, 1280
        self.height, self.width = 1440, 2560
        ret = self.cap_mipi.open_cam(0, -1, 30, [self.width1, self.width2, self.width], [self.height1, self.height2, self.height])
        print("cap_mipi.open_cam State: ", end="")
        print(ret)
        # vps对象
        self.vps = libsrcampy.Camera()
        ret = self.vps.open_vps(1, 1, 512, 512, 224, 224)
        print("vps State: ", end="")
        print(ret)
        ## 导入ResNet18模型(绝对路径)
        self.models = dnn.load('/root/00_com_ws/DataSet3_train1.bin')
        ## PID相关参数
        self.Kp = 2.0
        self.Kd = 3.0
        self.Target_value = 100.0   # 设定值初始化
        self.last_Err = 0.0         # 上一个误差值初始化
        self.total_Err = 0.0        # 误差累加初始化
        self.output = 0.0           # PID输出初始化


    def timer_callback(self):  # 创建定时器周期执行的回调函数
        # 获取512的图
        mipi_data_512 = self.cap_mipi.get_img(2, self.width1, self.height1)

        # 转化为224_img
        self.vps.set_img(mipi_data_512)
        mipi_data_224 = self.vps.get_img(2, 224, 224)
        outputs = self.models[0].forward(np.frombuffer(mipi_data_224, dtype=np.uint8))
        outputs = outputs[0].buffer
        x, y = int(224*outputs[0][0][0][0]), int(224*outputs[0][1][0][0]) 

        ## PID角速度控制目标计算
        self.Error = self.Target_value - 1.0*x        # 计算偏差
        self.total_Err = self.total_Err + self.Error    # 偏差累加
        self.output = self.Kp * self.Error + self.Kd * (self.Error - self.last_Err)   # PID运算
        self.last_Err = self.Error     # 将本次偏差赋给上次一偏差

        print("( %03d, %03d )  output = %03d"%(x,y,int(self.output)))
        self.twist.linear.x = 0.2
        self.twist.angular.z = self.output/114.0
        self.cmd_vel_pub.publish(self.twist)
        
def main(args=None):                                 # ROS2节点主入口main函数
    rclpy.init(args=args)                            # ROS2 Python接口初始化
    node = ResNetControlNode("resnet_control_node")     # 创建ROS2节点对象并进行初始化
    rclpy.spin(node)                                 # 循环等待ROS2退出
    node.destroy_node()                              # 销毁节点对象
    rclpy.shutdown()                                 # 关闭ROS2 Python接口

# 偷个懒,省得编译
if __name__ == "__main__":
    main()

resnet_control_node_open.py-
ResNet18模型文件.7z

1 个赞

补充调参思路:self.timer = self.create_timer(0.066, self.timer_callback) # 创建一个定时器(单位为秒的周期,定时执行的回调函数)-
此函数限制了控制频率为15Hz,如果PID调参效果不明显,可能是此参数限制了速度极限,可将时间调整到0.033秒,刷满30fps

1 个赞

超哥,摄像头换成单目usb需要修改什么?