第十八届全国大学生智能汽车竞赛室外赛5g室外专项赛(湘大队)开源

这篇具有很好参考价值的文章主要介绍了第十八届全国大学生智能汽车竞赛室外赛5g室外专项赛(湘大队)开源。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

序言:

        感谢十八届全国大学生智能汽车竞赛-室外赛的主办方和组委会让我们有了参加这次比赛的机会!当我们在参加5g室外专项赛时,我们发现很多的组又很多的同学都是第一次参加智能车竞赛对于智能车的了解还不是很深,对于代码如何编写需要哪些模块也不是很熟悉,所以我们打算开源本方案,为未来的智能车参赛者提供一丝丝帮助!如果代码方面的问题可以私信我,若能够提供帮助,一定竭尽全力!

        后续项目的源码会在github更新,同时我们打算更新GPS和陀螺仪利用惯导技术来完成比赛,尽情期待!

项目简介

        1.使用c++编程实现整体代码

        2.纯竞速赛道可以将实现车体机械结构的最快速度巡线

        3.可以将官方的摄像头帧率跑满

1.相机矫正程序

        官方规定使用的摄像头太过于抽象,如果不对相机的图片进行矫正那么对于赛道的巡线效果会非常差。相机矫正这在csdn上已经有很多教程,我的建议还是使用matlab去标定摄像头这样的数据更加标准。

cv::Mat undistort(const cv::Mat &frame)
{
    double k1 = -0.340032906324299;
    double k2 = 0.101344757327394;
    double p1 = 0.0;
    double p2 = 0.0;
    double k3 = 0.0;

    cv::Mat K = (cv::Mat_<double>(3, 3) << 162.063205442089, 0.0, 154.707845362265,
                 0.0, 162.326264903804, 129.914361509615,
                 0.0, 0.0, 1.0);

    cv::Mat D = (cv::Mat_<double>(1, 5) << k1, k2, p1, p2, k3);
    cv::Mat mapx, mapy;
    cv::Mat undistortedFrame;

    cv::initUndistortRectifyMap(K, D, cv::Mat(), K, frame.size(), CV_32FC1, mapx, mapy);
    cv::remap(frame, undistortedFrame, mapx, mapy, cv::INTER_LINEAR);

    return undistortedFrame;
}

2.赛道线提取

                本方案采用sobel算子,霍夫线段检测等方法对赛道线进行提取,我们通过对图片的剪裁提高了程序的运行速度,同时我们也使用了sobel算子的一些增强方式已便于更好的适应赛道环境,无论赛道线的清晰程度,只要是在人眼可分辨的范围内,都可以进行赛道线段提取。

Mat ImagePreprocessing(const cv::Mat &frame_a)
{
    int width = 320;
    int height = 240;

    binaryImage_1 = cv::Mat::zeros(height, width, CV_8U);
    //--------这一段代码中包含了canny算子和一些图像处理的方案-------
    // cv::Mat grayImage;
    // cv::cvtColor(frame, grayImage, cv::COLOR_BGR2GRAY);
    // cv::Mat edgeImage;
    // cv::Canny(grayImage, edgeImage, 50, 150);
    // vector<Vec4i> lines;
    // HoughLinesP(edgeImage, lines, 1, CV_PI / 180, 180, 100, 10);

    // for (size_t i = 0; i < lines.size(); i++) {
    //     Vec4i l = lines[i];
    //     line(binaryImage, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(255), 4);
    // }
    // // GaussianBlur(frame, img, cv::Size(7, 7), 0);
    // cvtColor(img, hsv_image, cv::COLOR_BGR2HSV);
    // // Scalar lower_white(10, 43, 46);
    // // Scalar upper_white(180, 255, 255);
    // // inRange(hsv_image, lower_white, upper_white, white_mask);
    // // bitwise_not(white_mask, final_img);
    // // cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(1, 1));

    // // cv::morphologyEx(final_img, delite_frame, cv::MORPH_OPEN, kernel);
    // Mat img_edges;
    // Canny(frame, img_edges, 100, 150);
    // cv::Mat blurred;
    // cv::Mat edges;
    // cv::GaussianBlur(frame_a, blurred, cv::Size(5, 5), 0);
    // int roiYStart = 100;
    // int roiYEnd = 155;
    // // 创建感兴趣区域(ROI)
    // cv::Mat roiImage = blurred(cv::Range(roiYStart, roiYEnd), cv::Range(0, frame_a.cols));
    // // Canny边缘检测
    // cv::Canny(roiImage, edges, 100, 150);

    // // 创建全黑图像
    // cv::Mat blackImage = cv::Mat::zeros(frame_a.size(), CV_8UC1);

    // // 将Canny边缘叠加到全黑图像的感兴趣区域中
    // edges.copyTo(blackImage(cv::Range(roiYStart, roiYEnd), cv::Range(0, frame_a.cols)));
    // imshow("canny1",edges);
    // imshow("canny2",blackImage);
    //------------------------------------------------------
    //-------------------赛道条件好的情况--------------------
    // int kernelSize = 5;
    // double sigma = 1.0;
    // cv::Mat blurredImage;
    // cv::GaussianBlur(frame_a, blurredImage, cv::Size(kernelSize, kernelSize), sigma);
    // cv::Mat grad_x, grad_y;
    // cv::Mat sobel_x,sobel_y;
    // cv::Mat abs_grad_x, abs_grad_y;
    // int xWeight = 1;
    // int yWeight = 1;
    // cv::Sobel(blurredImage, grad_x, CV_16S, 1, 0, 3, xWeight);
    // cv::Sobel(blurredImage, grad_y, CV_16S, 0, 1, 3, yWeight);
    // cv::convertScaleAbs(grad_x, abs_grad_x);
    // cv::convertScaleAbs(grad_y, abs_grad_y);
    // cv::Mat edges;
    // cv::addWeighted(abs_grad_x, 0.5, abs_grad_y,0.5, 0, edges);
    // imshow("edges",edges);
    // cv::threshold(edges, binaryImage, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU);
    // imshow("binaryImage",binaryImage);
    //---------------------赛道条件差的情况------------------
    // Sobel边缘检测
    Mat originalImage;
    cvtColor(frame, originalImage, cv::COLOR_BGR2GRAY);
    // float alpha = 0.2;  // 调整这个值以控制对比度增强的强度
    // Mat enhancedImage = customEqualizeHist(originalImage, alpha);
    Mat enhancedImage = originalImage;
    // Sobel边缘检测
    Mat sobelx, sobely;
    Sobel(enhancedImage, sobelx, CV_64F, 1, 0, 3);
    Sobel(enhancedImage, sobely, CV_64F, 0, 1, 3);

    Mat gradientMagnitude = abs(sobelx) + abs(sobely);
    convertScaleAbs(gradientMagnitude, gradientMagnitude);

    // 调整阈值
    Mat binaryImage12 = Mat::zeros(enhancedImage.size(), CV_8U);
    // threshold(gradientMagnitude, binaryImage12, 50, 255, THRESH_BINARY);
    cv::threshold(gradientMagnitude, binaryImage12, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU);
    cv::Mat kernel1 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
    cv::dilate(binaryImage12, binaryImage, kernel1, cv::Point(-1, -1), 1); // 这个地方也要修改
    // cv::dilate(binaryImage, binaryImage, kernel1, cv::Point(-1, -1), 1);
    int x_roi = 1;
    int y_roi = 109;
    int width_roi = 318;
    int height_roi = 46;
    cv::Rect roi(x_roi, y_roi, width_roi, height_roi);

    cv::Mat croppedObject = binaryImage(roi);
    vector<Vec4i> lines;
    HoughLinesP(croppedObject, lines, 1, CV_PI / 180, 25, 15, 10);
    for (size_t i = 0; i < lines.size(); i++)
    {
        Vec4i l = lines[i];
        double angle = atan2(l[3] - l[1], l[2] - l[0]) * 180.0 / CV_PI;
        double length = sqrt(pow(l[3] - l[1], 2) + pow(l[2] - l[0], 2));
        double aspect_ratio = length / abs(l[3] - l[1]);
        if (abs(angle) > 15)
        {
            Vec4i l = lines[i];
            l[0] += x_roi;
            l[1] += y_roi;
            l[2] += x_roi;
            l[3] += y_roi;
            line(binaryImage_1, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(255), 2, LINE_AA);
        }
    }
    return binaryImage_1;
}

3.斑马线识别

        本方案采用室内智能车的斑马线识别方案,选取了图像中的ROI区域,用来提高斑马线的识别率和减少对其的误判!

int crossroad(Mat frame)
{
    flag_cross = 0;
    int height = frame.rows;
    int width = frame.cols;
    Mat hsv;
    cvtColor(frame, hsv, COLOR_BGR2HSV);

    Scalar lower_white = Scalar(0, 0, 221);
    Scalar upper_white = Scalar(180, 30, 255);

    Mat mask1;
    inRange(hsv, lower_white, upper_white, mask1);
    Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
    dilate(mask1, mask1, kernel);
    erode(mask1, mask1, kernel);
    Mat src(mask1, Rect(100, 85, 120, 60));
    int cout1 = 0, cout2 = 0, flag = 0;
    for (int i = 0; i < src.rows; i++)
    {
        if (cout1 < 10)
        {
            flag = 0;
        }
        cout1 = 0;
        for (int j = 10; j < src.cols - 10; j++)
        {
            if (src.at<char>(i, j - 2) == 0 && src.at<uchar>(i, j) == 0 && src.at<uchar>(i, j - 1) == 0 && src.at<uchar>(i, j + 1) == 255 && src.at<uchar>(i, j + 2) == 255)
            {
                cout1++;
            }
            else if (src.at<uchar>(i, j - 2) == 255 && src.at<uchar>(i, j) == 255 && src.at<uchar>(i, j - 1) == 255 && src.at<uchar>(i, j + 1) == 0 && src.at<uchar>(i, j + 2) == 0)
            {
                cout1++;
            }
            if (cout1 >= 10)
            {
                cout2++;
                flag++;
                if (flag >= 3)
                {
                    cout << "斑马线" << endl;
                    flag_cross = 1;
                }
                break;
            }
        }
    }
    cout << "flag_cross" << flag_cross << endl;
    return flag_cross;
}

4.黄线识别

        本方案使用了两种方式对于黄线进行检测分别为HSV颜色分割和Canny边缘检测,同时本方案配备了对黄线色域阈值调整提取程序,可以对不同光照条件下的黄线实现识别

HSV版本

int yellow_hsv(Mat img, bool visual_flag)
{
    Mat cropped_image, canvas, image;
    Scalar lowerb = Scalar(3, 0, 0);
    Scalar upperb = Scalar(40, 100, 255);
    int yellow_num = 0;
    int height = img.rows;
    int width = img.cols;
    int half_height = int(height / 2);
    int per_height = int(height / 20);
    int area_1 = int(0.002775 * height * width);
    int area_2 = int(0.025 * height * width);
    image = img.clone();
    cropped_image = image(Rect(0, half_height - per_height, width, per_height * 3));
    if (visual_flag == true)
    {
        canvas = cropped_image.clone();
    }
    cvtColor(cropped_image, cropped_image, COLOR_BGR2HSV);
    morphologyEx(cropped_image, cropped_image, MORPH_OPEN, getStructuringElement(MORPH_RECT, Size(3, 3)));
    inRange(cropped_image, lowerb, upperb, cropped_image);
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(cropped_image, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    for (size_t i = 0; i < contours.size(); i++)
    {
        double area = contourArea(contours[i]);
        if (area > area_1 && area < area_2)
        {
            Rect rect = boundingRect(contours[i]);
            double aspect_ratio = rect.width / rect.height;
            if (aspect_ratio > 10)
            {
                if (visual_flag == true)
                {
                    rectangle(canvas, rect, Scalar(255, 0, 0), 2, LINE_AA);
                    printf("x: %d, y: %d, width: %d, height: %d, aspect_ratio: %f\n", rect.x, rect.y, rect.width,
                           rect.height, aspect_ratio);
                }
                yellow_num += 1;
            }
        }
    }
    if (visual_flag == true)
    {
        imshow("Image_hsv", canvas);
        waitKey(0);
    }
    return yellow_num;
}

Canny版本

int yellow_edge(Mat img, bool visual_flag)
{
    Mat cropped_image, canvas, image;
    int yellow_num = 0;
    int height = img.rows;
    int width = img.cols;
    int half_height = int(height / 2);
    int per_height = int(height / 20);
    image = img.clone();
    cropped_image = image(Rect(0, half_height - per_height, width, per_height * 3));
    if (visual_flag == true)
    {
        canvas = cropped_image.clone();
    }
    cvtColor(cropped_image, cropped_image, COLOR_BGR2GRAY);
    Canny(cropped_image, cropped_image, 50, 150, 3);
    vector<Vec4i> lines;
    HoughLinesP(cropped_image, lines, 1, CV_PI / 180, 150, 125, 10);
    if (lines.size() == 0)
    {
        printf("No yellow edge detected!\n");
        return 0;
    }
    else
    {
        for (size_t i = 0; i < lines.size(); i++)
        {
            Vec4i l = lines[i];
            double angle = atan2(l[3] - l[1], l[2] - l[0]) * 180.0 / CV_PI;
            double length = sqrt(pow(l[3] - l[1], 2) + pow(l[2] - l[0], 2));
            double aspect_ratio = length / abs(l[3] - l[1]);
            if (abs(angle) < 5 && aspect_ratio > 5)
            {
                if (visual_flag == true)
                {
                    line(canvas, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(255, 0, 0), 2, LINE_AA);
                    printf("x: %d, y: %d, x1: %d, y1: %d, angle: %f, length: %f, aspect_ratio: %f\n", l[0], l[1], l[2],
                           l[3], angle, length, aspect_ratio);
                }
                yellow_num += 1;
            }
        }
    }
    if (visual_flag == true)
    {
        imshow("Image_edge", canvas);
        waitKey(0);
    }
    return yellow_num;
}

阈值调整程序

        队伍中的大佬最近在考试,不理我,这个等寒假再更新!!!

5.蓝色挡板识别

        蓝色挡板分为两部分:1.检测到蓝色挡板,2.蓝色挡板移开

1.检测到蓝色挡板

void blue_card_find(void)
{
    vector<vector<Point>> contours;
    vector<Vec4i> hierarcy;
    findContours(mask, contours, hierarcy, RETR_EXTERNAL, CHAIN_APPROX_NONE);
    if (contours.size() > 0)
    {
        sort(contours.begin(), contours.end(), Contour_Area);
        vector<vector<Point>> newContours;
        for (const vector<Point> &contour : contours)
        {
            Point2f center;
            float radius;
            minEnclosingCircle(contour, center, radius);
            if (center.y > 90 && center.y < 160)
            {
                newContours.push_back(contour);
            }
        }

        contours = newContours;

        if (contours.size() > 0)
        {
            if (contourArea(contours[0]) > 500)
            {
                cout << "find biggest blue" << endl;
                Point2f center;
                float radius;
                minEnclosingCircle(contours[0], center, radius);
                circle(frame, center, static_cast<int>(radius), Scalar(0, 255, 0), 2);
                find_first = 1;
            }
            else
            {
                cout << "not found blue" << endl;
            }
        }
    }
    else
    {
        cout << "not found blue" << endl;
    }
}

2.蓝色挡板移开

void blue_card_remove(void)
{
    cout << "entry move blue process" << endl;
    vector<vector<Point>> contours;
    vector<Vec4i> hierarcy;
    findContours(mask, contours, hierarcy, RETR_EXTERNAL, CHAIN_APPROX_NONE);
    if (contours.size() > 0)
    {
        sort(contours.begin(), contours.end(), Contour_Area);
        vector<vector<Point>> newContours;
        for (const vector<Point> &contour : contours)
        {
            Point2f center;
            float radius;
            minEnclosingCircle(contour, center, radius);
            if (center.y > 90 && center.y < 160)
            {
                newContours.push_back(contour);
            }
        }

        contours = newContours;

        if (contours.size() == 0)
        {
            begin_sign = 0;
            cout << "move" << endl;
            sleep(2);
        }
    }
    else
    {
        begin_sign = 0;
        cout << "蓝色挡板移开" << endl;
        sleep(2);
    }
}

6.锥桶避障方案

        锥桶避障实际上就是对锥桶的颜色进行识别,再利用锥桶中心位置对图像进行补线,从而实现动态路径规划,这一部分代码分为三部分对于1.锥桶颜色进行识别,2.根据锥桶位置对锥桶进行判断是否补线,3.根据锥桶位置对补线方案进行替换,4.对图像补线后进行再次巡线

补线程序(锥桶主程序)

void blue_vertebral_model(void)
{
    contours_all = blue_vertebral_barrel_find_all(mask);
    if (contours_all.size() != 0)
    {
        Point2f center;
        float radius;
        minEnclosingCircle(contours_all[0], center, radius);
        heighest = center.y;
        if (try_patching_line == 2)
        {
            bin_image2 = drawWhiteLine(bin_image2, Point(int(center.x), int(center.y)), Point(int((right_line[0].x + right_line[1].x + right_line[2].x) / 3), 155),
                                       8);
            cout << "center.x:" << center.x << endl;
            cout << "center.y:" << center.y << endl;
            cout << "1" << endl;
        }
        else if (try_patching_line == 1)
        {
            if (count_change != 2)
            {
                bin_image2 = drawWhiteLine(bin_image2, Point(int(center.x), int(center.y)), Point(int((left_line[0].x + left_line[1].x + left_line[2].x) / 3), 155),
                                           8);
            }
            else
            {
                bin_image2 = drawWhiteLine(bin_image2, Point(int(center.x - 20), int(center.y)), Point(int((left_line[0].x + left_line[1].x + left_line[2].x) / 3), 155),
                                           8);
            }
            cout << "center.x:" << center.x << endl;
            cout << "center.y:" << center.y << endl;
            cout << "2" << endl;
        }
    }
}

锥桶的判断程序和补线方式转换程序

vector<vector<Point>> blue_vertebral_barrel_find_all(Mat mask)
{
    vector<vector<Point>> contours;
    vector<Vec4i> hierarcy;
    Point2f center;
    float radius;
    findContours(mask, contours, hierarcy, RETR_EXTERNAL, CHAIN_APPROX_NONE);
    if (contours.size() > 0)
    {
        vector<vector<Point>> newContours;
        for (const vector<Point> &contour : contours)
        {
            Point2f center;
            float radius;
            minEnclosingCircle(contour, center, radius);
            if (center.y > 108 && center.y < 153)
            {
                newContours.push_back(contour);
            }
        }
        contours = newContours;
    }
    if (contours.size() > 0)
    {
        vector<vector<Point>> newContours2;
        for (const vector<Point> &contour : contours)
        {
            if (contourArea(contour) > 10)
            {
                newContours2.push_back(contour);
            }
        }
        contours = newContours2;
    }
    if (contours.size() > 0)
    {
        vector<vector<Point>> newContours5;
        for (const vector<Point> &contour : contours)
        {
            Point2f center;
            float radius;
            minEnclosingCircle(contour, center, radius);
            center.x = (int)center.x;
            center.y = (int)center.y;
            if (center.x > left_line[center.y - 108].x && center.x < right_line[center.y - 108].x)
            {
                cout << "过滤后的点:center.x" << center.x << endl;
                cout << "center.y " << center.y << endl;
                cout << "left_line " << left_line[center.y - 108].x << endl;
                cout << "right_line " << right_line[center.y - 108].x << endl;
                cv::circle(frame, Point(center.x, center.y), 10, cv::Scalar(0, 0, 0), -1);
                newContours5.push_back(contour);
            }
        }
        contours = newContours5;
    }
    if (contours.size() > 0)
    {
        sort(contours.begin(), contours.end(), Contour_Area);
        now_blue_max = (int)contourArea(contours[0]);
    }
    else
    {
        now_blue_max = 0;
    }
    vector<vector<Point>> newContours4;
    newContours4 = contours;
    if (contours.size() > 0)
    {
        vector<vector<Point>> newContours3;
        for (const vector<Point> &contour : contours)
        {
            if (contourArea(contour) < 140)
            {
                newContours3.push_back(contour);
            }
        }
        contours = newContours3;
    }
    // cout << "now_blue_max" << now_blue_max <<endl;
    // cout << "contours.size()" << contours.size() <<endl;
    if (contours.size() == 0 && newContours4.size() != 0)
    {
        if (last_blue == 0)
        {
            if (try_patching_line == 1)
            {
                try_patching_line = 2;
            }
            else if (try_patching_line == 2)
            {
                try_patching_line = 1;
            }
            cout << "--------------------------------补线方式转换------------------------------" << endl;
            number1 = 0;
            count_change++;
        }
    }
    if (now_blue_max > 140)
    {
        last_blue = 1;
    }
    else
    {
        last_blue = 0;
    }
    return contours;
}

7.控制代码

        本方案采用的控制代码与室内智能车差不多,但是建议在锥桶避障时采用多套PID,同时可以考虑使用赛道中线方差实现变加速控制(团队做了后面没用因为确实没啥用!),这里给一些示例代码可以供参考!

1.控制程序

double average(vector<int> vec)
{
    if (vec.size() < 1)
        return -1;

    double sum = 0;
    for (int i = 0; i < vec.size(); i++)
    {
        sum += vec[i];
    }

    return (double)sum / vec.size();
}
double sigma(vector<int> vec)
{
    if (vec.size() < 1)
        return 0;

    double aver = average(vec);
    double sigma = 0;
    for (int i = 0; i < vec.size(); i++)
    {
        sigma += (vec[i] - aver) * (vec[i] - aver);
    }
    sigma /= (double)vec.size();
    return sigma;
}
void motor_servo_contral(int flag_yellow_cond, int cross)
{
    if (flag_yellow_cond != 0 && flag_yellow_finish == 0 && flag_cross_finish == 1)
    {
        flag_yellow_finish = 1;
        gpioPWM(12, 730);
        gpioPWM(13, 10000);
        usleep(250000);
        gpioPWM(13, 9800);
        gpioPWM(13, 8800);
        usleep(250000);
        _exit(0);
    }
    else if (cross == 1 && flag_cross_finish == 0)
    {
        flag_cross_finish = 1;
        gpioPWM(13, 9800);
        gpioPWM(13, 8900);
        usleep(550000);
        gpioPWM(12, 730);
        gpioPWM(13, 10000);
        sleep(3);
    }
    else if (contours_all.size() != 0 && count_change < 3 && number1 >= 7 && flag_cross_finish == 1)
    {
        if (try_patching_line == 2 && count_change < 3 && count_change >= 1)
        {
            float servo_pwm_chayan = servo_pd_blue(160);
            servo_pwm_now = servo_pwm_chayan;
        }
        else if (try_patching_line == 1 && count_change >= 1 && count_change < 3)
        {
            float servo_pwm_chayan = servo_pd_blue(160);
            servo_pwm_now = servo_pwm_chayan;
        }
        else
        {
            servo_pwm_now = servo_pd_blue(160);
        }
        cout << "bin_imagepwm" << servo_pwm_now << endl;
        if (times4 == 0)
        {
            times4 = 1;
            // gpioPWM(13, 9800);
            // gpioPWM(13, 8700);
            gpioPWM(13, 10000);
            // usleep(50000);
        }
        // gpioPWM(13, 10000);//11000
        // gpioPWM(13, 10500);
        gpioPWM(12, servo_pwm_now);
    }
    else
    {
        if (count_change < 1 || count_change > 2)
        {
            cout << 1 << endl;
            if (count_change > 2 && flag_cross_finish == 1)
            {
                servo_pwm_now = servo_pd(160);
                cout << "pwm" << servo_pwm_now << endl;
                gpioPWM(13, 13000);
                gpioPWM(12, servo_pwm_now);
            }
            else if (count_change < 1 && flag_cross_finish == 0)
            {
                servo_pwm_now = servo_pd(160);
                cout << "pwm" << servo_pwm_now << endl;
                gpioPWM(13, 12000);
                gpioPWM(12, servo_pwm_now);
            }
            else
            {
                cout << "after" << endl;
                servo_pwm_now = servo_pd_after(160);
                cout << "pwm" << servo_pwm_now << endl;
                gpioPWM(13, 11700);
                gpioPWM(12, servo_pwm_now);
            }
        }
        else
        {
            cout << 2 << endl;
            servo_pwm_now = servo_pd_left(160);
            cout << "pwm" << servo_pwm_now << endl;
            gpioPWM(13, 11400);
            gpioPWM(12, servo_pwm_now);
        }
    }
    // ----------------------------------变加速控制-------------------------------
    // uint8_t controlLow = 0;   // 速度控制下限
    // uint8_t controlMid = 5;   
    // uint8_t controlHigh = 10; // 速度控制上限
    // float servo_pwm_now = servo_pd(320);
    // if (mid.size() > 20)
    // {
    //     vector<POINT> centerV;
    //     int filt = mid.size() / 5;
    //     for (int i = filt; i < mid.size() - filt; i++)
    //     {
    //         centerV.push_back(mid[i]);
    //     }
    //     sigmaCenter = sigma(centerV);
    // }
    // else
    //     sigmaCenter = 1000;
    // if (abs(sigmaCenter) < 100.0)
    // {
    //     counterShift++;
    //     if (counterShift > controlHigh)
    //         counterShift = controlHigh;
    // }
    // else
    // {
    //     counterShift--;
    //     if (counterShift < controlLow)
    //         counterShift = controlLow;
    // }

    // if (counterShift > controlMid)
    // {
    //     motorSpeed = speedhigh;
    //     cout << "高" << endl;
    // }
    // else
    // {
    //     motorSpeed = speedlow;
    //     cout << "低" << endl;
    // }
    // gpioPWM(13, motorSpeed);
    // gpioPWM(12, servo_pwm_now);
    // ------------------------------------纯竞速---------------------------------
    // if (flag_yellow_cond != 0 && flag_yellow_finish == 0 && flag_cross_finish == 1)
    // {
    //     flag_yellow_finish = 1;
    //     gpioPWM(12, 730);
    //     gpioPWM(13, 10000);
    //     usleep(250000);
    //     gpioPWM(13, 9800);
    //     gpioPWM(13, 8800);
    //     usleep(250000);
    //     cout << "停止" << endl;
    //     _exit(0);
    // }
    // else if (cross == 1 && flag_cross_finish == 0)
    // {
    //     flag_cross_finish = 1;
    //     cout << "11" << endl;
    //     gpioPWM(13, 9800);
    //     gpioPWM(13, 8900);
    //     usleep(550000); //
    //     gpioPWM(12, 730);
    //     gpioPWM(13, 10000);
    //     sleep(3);
    // }
    // else if (contours_all.size() != 0 && count_change < 3 && number1 >= 7)
    // {
    //     if (try_patching_line == 2 && count_change < 3 && count_change >= 1)
    //     {
    //         float servo_pwm_chayan = servo_pd_blue(160);
    //         servo_pwm_now = servo_pwm_chayan;
    //     }
    //     else if (try_patching_line == 1 && count_change >= 1 && count_change < 3)
    //     {
    //         float servo_pwm_chayan = servo_pd_blue(160);
    //         servo_pwm_now = servo_pwm_chayan;
    //     }
    //     else
    //     {
    //         servo_pwm_now = servo_pd_blue(160);
    //     }
    //     if (times4 == 0)
    //     {
    //         times4 = 1;
    //         gpioPWM(13, 9800);
    //         gpioPWM(13, 8700);
    //         usleep(100000);
    //     }
    //     gpioPWM(13, 10000);//11000
    //     gpioPWM(12, servo_pwm_now);
    // }
    // else
    // {
    //     if (flag_cross_finish == 0)
    //     {
    //         servo_pwm_now = servo_pd(160);
    //         gpioPWM(13, 12000);
    //         gpioPWM(12, servo_pwm_now);

    //     }
    //     else
    //     {
    //         servo_pwm_now = servo_pd(160);
    //         gpioPWM(13, 12500);
    //         gpioPWM(12, servo_pwm_now);

    //     }
    // }
}

2.PID控制程序

        本方案直接使用pwm波数值进行舵机控制,如果您使用了逆透视的方案来做巡线,那么我更加建议使用角度来做pwm波控制。

float servo_pd(int target)
{
    int size = int(mid.size());
    int pidx = int((mid[23].x + mid[20].x + mid[25].x) / 3);
    error_first = target - pidx;
    servo_pwm_diff = kp * error_first + kd * (error_first - last_error);
    // cout << "servo_pwm_diff:" << servo_pwm_diff << endl;
    last_error = error_first;
    servo_pwm = 720 + servo_pwm_diff;
    if (servo_pwm > 900)
    {
        servo_pwm = 900;
    }
    else if (servo_pwm < 600)
    {
        servo_pwm = 600;
    }
    return servo_pwm;
}

8.附属模块代码程序

1.拍照程序(python版)

import cv2
import os
import numpy as np



def undistort(frame):
    k1, k2, p1, p2, k3 = -0.340032906324299, 0.101344757327394,0.0,0.0, 0.0

    k = np.array([
        [162.063205442089, 0, 154.707845362265],
        [0, 162.326264903804,129.914361509615],
        [0, 0, 1]
    ])
    # 畸变系数
    d = np.array([
        k1, k2, p1, p2, k3
    ])
    height, weight = frame.shape[:2]
    mapx, mapy = cv2.initUndistortRectifyMap(k, d, None, k, (weight, height), 5)

    return cv2.remap(frame, mapx, mapy, cv2.INTER_LINEAR)

cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)  #设置宽度
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)  #设置长度
if not cap.isOpened():
    print("无法打开摄像头")
else:
    while True:
        ret, frame = cap.read()

        if not ret:
            print("无法捕捉")
            break
        frame = undistort(frame)
        cv2.imshow('Press "q" to Capture and Quit', frame)

        key = cv2.waitKey(1)

        # 如果用户按下 "q",拍照并保存,然后退出程序?
        if key & 0xFF == ord('q'):
            photo_path = os.path.join("test_photo", '40.jpg')
            cv2.imwrite(photo_path, frame)
            print(f"已保存照片为 {photo_path}")
            break

    cap.release()

cv2.destroyAllWindows()

2.蓝色挡板测试程序(python版)

import cv2
import numpy as np

# 使用比较函数对轮廓进行排序
def contour_area(contour):
    return cv2.contourArea(contour)

def process_frame(frame):
    change_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    lower_bound = np.array([100, 43, 46], dtype=np.uint8)
    upper_bound = np.array([124, 255, 255], dtype=np.uint8)
    mask = cv2.inRange(change_frame, lower_bound, upper_bound)
    kernel = np.ones((5, 5), np.uint8)
    mask = cv2.dilate(mask, kernel, iterations=1)
    mask = cv2.erode(mask, kernel, iterations=1)
    return mask



cap = cv2.VideoCapture(0)
find_first = 0
begin_sign = 0
while True:
    ret, frame = cap.read()
    processed_frame = process_frame(frame)
    contours, hierarchy = cv2.findContours(processed_frame, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    if begin_sign == 0:
        if find_first == 0:
            if len(contours) > 0:
                contours = sorted(contours, key=cv2.contourArea, reverse=True)
                new_contours = []
                for contour in contours:
                    (x, y), radius = cv2.minEnclosingCircle(contour)
                    center = (int(x), int(y))
                    # 去除掉上面和下面的噪点
                    if center[1] > 180 and center[1] < 320:
                        new_contours.append(contour)

                # 将新的列表赋值给 contours
                contours = new_contours
                if(len(contours) > 0):

                # 检查列表中第一个轮廓的面积是否大于 1300
                    if cv2.contourArea(contours[0]) > 1300:
                        print("找到了最大的蓝色挡板")
                        (x, y), radius = cv2.minEnclosingCircle(contours[0])
                        center = (int(x), int(y))
                        radius = int(radius)
                        cv2.circle(frame, center, radius, (0, 255, 0), 2)
                        find_first = 1
                    else:
                        print("没找到蓝色挡板")
        else:
            print("进入移开挡板的程序")
            if len(contours) > 0:
                contours = sorted(contours, key=cv2.contourArea, reverse=True)
                new_contours = []
                for contour in contours:
                    (x, y), radius = cv2.minEnclosingCircle(contour)
                    center = (int(x), int(y))
                    # 去除掉上面和下面的噪点
                    if center[1] > 180 and center[1] < 320:
                        new_contours.append(contour)

                contours = new_contours
                if(len(contours) > 0):
                    if cv2.contourArea(contours[0]) < 300:
                        begin_sign = 1
                        print("挡板移开")
                else:
                    begin_sign = 1
                    print("挡板移开")
    cv2.imshow('Original Frame', frame)
    cv2.imshow('Processed Frame', processed_frame)

    # 检测按键,如果按下Esc键则退出循环
    if cv2.waitKey(1) == 27:
        break

cap.release()
cv2.destroyAllWindows()

3.锥桶轮廓面积测试程序(python版)

import cv2
import numpy as np

def find_blue_contours(image_path):
    # 读取图像
    image = cv2.imread(image_path)

    # 将图像转换为HSV颜色空间
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

    # 定义蓝色的HSV范围
    lower_blue = np.array([100, 43, 46])
    upper_blue = np.array([124, 255, 255])

    # 创建一个蓝色掩码
    mask = cv2.inRange(hsv, lower_blue, upper_blue)

    # 执行形态学操作,可以根据实际情况调整参数
    kernel = np.ones((5, 5), np.uint8)
    mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
    mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)

    # 寻找轮廓
    contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    # 仅保留y轴在210-320范围内的轮廓
    filtered_contours = [contour for contour in contours if 95 <= cv2.boundingRect(contour)[1] <= 165]

    # 在图像上画出轮廓,并写出大小
    for i, contour in enumerate(filtered_contours):
        area = cv2.contourArea(contour)
        cv2.drawContours(image, [contour], 0, (0, 255, 0), 2)
        cv2.putText(image, f"Contour {i + 1}: {area:.2f}", (10, 30 * (i + 1)), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)

    # 显示图像
    cv2.imshow("Blue Contours", image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

if __name__ == "__main__":
    image_path = "/home/pi/test_photo/blue_zhuitong8.jpg"  # 替换为你的图像文件路径
    find_blue_contours(image_path)

4.相机矫正程序(python版)

# 摄像头畸变矫正函数,输入待矫正的图形变量
import cv2
import os
import numpy as np

def undistort(frame):
    k1, k2, p1, p2, k3 = -0.287246515012261, 0.066176222325459, 0.005615032474715,0.003425003902561, 0.0

    # 相机坐标系到像素坐标系的转换矩阵
    k = np.array([
        [3.111337497474041e+02, -2.333471935388314, 2.915941445374422e+02],
        [0, 3.109853062871910e+02, 2.473500696130221e+02],
        [0, 0, 1]
    ])
    # 畸变系数
    d = np.array([
        k1, k2, p1, p2, k3
    ])
    height, weight = frame.shape[:2]
    mapx, mapy = cv2.initUndistortRectifyMap(k, d, None, k, (weight, height), 5)

    # 返回矫正好的图形变量
    return cv2.remap(frame, mapx, mapy, cv2.INTER_LINEAR)
# 打开摄像头
cap = cv2.VideoCapture(0)  # 0表示默认摄像头,如果有多个摄像头,可以尝试不同的数字来选择不同的摄像头

if not cap.isOpened():
    print("无法打开摄像头.")
else:
    while True:
        # 捕捉一帧
        ret, frame = cap.read()

        if not ret:
            print("无法捕捉帧.")
            break
        frame = undistort(frame)
        # 显示当前帧
        cv2.imshow('Press "q" to Capture and Quit', frame)

        # 等待用户按下键盘
        key = cv2.waitKey(1)

        # # 如果用户按下 "q" 键,拍照并保存,然后退出程序
        # if key & 0xFF == ord('q'):
        #     # 构造完整的文件路径
        #     photo_path = os.path.join("test_photo", '30.jpg')
        #     cv2.imwrite(photo_path, frame)
        #     print(f"已保存照片为 {photo_path}")
        #     break

    # 释放摄像头
    cap.release()

# 关闭所有窗口
cv2.destroyAllWindows()

5.Canny边缘检测阈值调整程序(c++版本)

#include <opencv2/opencv.hpp>

using namespace cv;

// 回调函数
void onTrackbarChange(int, void *)
{
    // 什么也不做
}

int main()
{
    // 读取图片
    Mat img_original = imread("/home/pi/test_photo/1.jpg");

    // 创建窗口
    namedWindow("Canny");

    // 创建两个滑动条,分别控制threshold1和threshold2
    int threshold1 = 50;
    int threshold2 = 100;

    createTrackbar("threshold1", "Canny", &threshold1, 400, onTrackbarChange);
    createTrackbar("threshold2", "Canny", &threshold2, 400, onTrackbarChange);

    while (true)
    {
        // Canny边缘检测
        Mat img_edges;
        Canny(img_original, img_edges, threshold1, threshold2);

        // 显示图片
        imshow("original", img_original);
        imshow("Canny", img_edges);

        // 检测键盘输入,如果按下 'q' 键,退出循环
        if (waitKey(1) == 'q')
        {
            break;
        }
    }

    destroyAllWindows();

    return 0;
}

6.HoughLinesP阈值调整程序(c++版本)

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

Mat g_mask1Image, g_midImage, g_mask1GrayImage;
cv::Mat sobelX8U;
cv::Mat blurredImage;
cv::Mat sobelX;
cv::Mat binaryImage;
cv::Mat croppedObject;
int g_HoughLinesThreshold = 150;
int g_minLineLength = 100;

void on_HoughLines(int, void *);
Mat customEqualizeHist(const Mat &inputImage, float alpha)
{
    Mat enhancedImage;
    equalizeHist(inputImage, enhancedImage);

    // 减弱对比度增强的效果
    return alpha * enhancedImage + (1 - alpha) * inputImage;
}
int main()
{
    g_mask1Image = imread("/home/pi/test_photo/1.jpg", cv::IMREAD_GRAYSCALE);
    if (!g_mask1Image.data)
    {
        return -1;
    }

    imshow("g_mask1Image", g_mask1Image);
    // float alpha = 0.5;  // 调整这个值以控制对比度增强的强度
    // Mat enhancedImage = customEqualizeHist(g_mask1Image, alpha);
    Mat enhancedImage = g_mask1Image;
    // Sobel边缘检测
    Mat sobelx, sobely;
    Sobel(enhancedImage, sobelx, CV_64F, 1, 0, 3);
    Sobel(enhancedImage, sobely, CV_64F, 0, 1, 3);

    Mat gradientMagnitude = abs(sobelx) + abs(sobely);
    convertScaleAbs(gradientMagnitude, gradientMagnitude);

    // 调整阈值
    Mat binaryImage12 = Mat::zeros(enhancedImage.size(), CV_8U);
    // threshold(gradientMagnitude, binaryImage12, 50, 255, THRESH_BINARY);
    cv::threshold(gradientMagnitude, binaryImage12, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU);
    imshow("binaryImage", binaryImage12);
    imshow("gradientMagnitude", gradientMagnitude);
    cv::Mat kernel1 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
    cv::dilate(binaryImage12, binaryImage, kernel1, cv::Point(-1, -1), 1);
    // imshow("1",binaryImage);
    int x_roi = 1;
    int y_roi = 109;
    int width_roi = 318;
    int height_roi = 45;
    cv::Rect roi(x_roi, y_roi, width_roi, height_roi);

    croppedObject = binaryImage(roi);
    namedWindow("HoughLines", WINDOW_AUTOSIZE);
    createTrackbar(" g_HoughLinesThreshold", "HoughLines", &g_HoughLinesThreshold, 150, on_HoughLines);
    createTrackbar("g_minLineLength", "HoughLines", &g_minLineLength, 100, on_HoughLines);

    on_HoughLines(0, 0);

    while (char(waitKey(1)) != 'q')
    {
    }

    waitKey(0);
    return 0;
}

void on_HoughLines(int, void *)
{
    cvtColor(croppedObject, g_mask1GrayImage, COLOR_GRAY2BGR);

    vector<Vec4i> lines
        HoughLinesP(croppedObject, lines, 1, CV_PI / 180, g_HoughLinesThreshold, g_minLineLength, 10);

    for (size_t i = 0; i < lines.size(); i++)
    {
        Vec4i l = lines[i];
        line(g_mask1GrayImage, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(186, 88, 255), 1, LINE_AA);
    }

    imshow("HoughLines", g_mask1GrayImage);
}

尾言

        感谢湘潭大学电工电子实验室为我们提供的参赛机会,感谢向礼丹老师和电工电子实验室的每个指导老师,感谢十七届参加室外赛的朱嘉明和崔全学长,衷心祝愿各位未来参加智能车比赛的车友能够赛出佳绩!在智能车竞赛中再创辉煌!文章来源地址https://www.toymoban.com/news/detail-829846.html

到了这里,关于第十八届全国大学生智能汽车竞赛室外赛5g室外专项赛(湘大队)开源的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 第18届全国大学生智能汽车竞赛四轮车开源讲解【4】--控制

    1.1舵机 舵机是控制车模方向的重要组成部分,一般放在车头,实物见下图。 C车模允许使用的有S3010,U400这两款舵机 如果比赛限制车模类型为C车模,那么只能使用S3010(已停产)或者U400舵机进行比赛,控制舵机主要是控制PWM波的脉宽。控制规律如下图。 舵机占PWM脉宽与转动角度

    2024年02月08日
    浏览(47)
  • 第18届全国大学生智能汽车竞赛四轮车开源讲解【3】--边线提取

    当摄像头成功获取一帧图像,并且预处理(二值化)之后,我们最重要的事情就是获取赛道信息。其中最基本的就是赛道编边界信息,左边,右边,中线等基础数据。 事先声明,没有那种算法是完美的,只要算法能够得到足够多想要的信息,那么他就是好算法。 不同算法之

    2024年02月08日
    浏览(84)
  • 第18届全国大学生智能汽车竞赛四轮车开源讲解【5】--直道、弯道、十字

    智能车花费时间最多的就是元素识别这一环节,经过我们前几章摄像头矫正,边线提取,中线计算,速度/方向控制。这几个环节都做好的话,车子是可以在简单的赛道中间进行基本的寻迹。沿着直道,弯道走。 但是想要完成比赛要求,需要对元素进行处理,包括但不限于:

    2024年02月06日
    浏览(38)
  • 第十五届全国大学生信息安全竞赛部分WriteUp

    做了10个,都是烂大街的题目,分数很低。CTF榜单186,以为稳进分区赛了。理论题算上变一千五百多名,华东南二百多名,进不去了,WriteUp也不想上传了。 不是密码选手,但密码非预期搞出来几个 签到电台 关注公众号给的提示“弼时安全到达了”,查找这几个字的中文电码

    2024年02月06日
    浏览(58)
  • 第十五届全国大学生信息安全竞赛创新实践能力赛

    ​ 这两个应该是属于非预期,查找文件内容,两个flag都出了: find / |xargs grep -ri flag{ 2/dev/null flag{34f5fdaf-c373-47fd-afab-01ed2914c11a} 解题步骤同上 使用hydra爆破获得root密码toor。 登陆查找(find / |xargs grep -ri flag{ 2/dev/null)获得flag flag{7b352ef0-1bb1-41af-a7d7-b74f62ff23f0} 爆破sha256老脚本套

    2024年02月07日
    浏览(44)
  • 第十五届全国大学生信息安全竞赛知识问答(CISCN)

    一、单项选择题 1、国家秘密的保密期限,除另有规定外,绝密级不超过()年 A、10年 B、50年 C、30年 D、20年 2、安卓逆向中,反编译JAVA通常使用以下哪个软件?( ) A、JPEXS B、dnSpy C、JEB D、IDA 3、我国在信息系统安全保护方面制定的最早一部,也是最基本的一部法规是()

    2024年02月04日
    浏览(55)
  • 2022第十五届全国大学生信息安全竞赛(ciscn)西南赛区部分WP

    EzSignin 访问题目flag是假的,F12源代码,看到base64解码 解码得到flag CODE 访问题目,有个aid.php直接访问不行,使用PHP为协议进行读取,input2需要等于Welcone!,这里要使用data协议编码一下,input3可以随便输入 得到aid.php的代码 很明显的反序列化,直接构造payload: exp: base64解码得

    2024年02月06日
    浏览(50)
  • 第十四届全国大学生电工数学建模竞赛A题-高比例风电电力系统储能运行及配置分析

     写在前面 博主:多次获得华为杯,电工杯,小美赛等数学建模一等奖、二等奖,拥有较为丰富的比赛经验,会分享一些建模的思路、算法以及比赛经验。 博主主页: Born for的博客_CSDN博客-预测,数学建模,深度学习领域博主 希望大家多多关注,大家共同进步! 目录 题目背景

    2024年02月05日
    浏览(62)
  • 2021全国大学生电子设计竞赛论文(智能送药小车(F题))(电赛论文模板)

    电赛是一个很奇妙的过程,可能有些人觉得电赛的门槛太高,那便意味着,当你决定要参加电赛的那一刻起,这一段路、这些日子就注定不会太轻松; 我现在回头看真的很感谢电赛,从前期备赛面对自己未曾涉猎的技术不知如何下手的迷茫与怀疑,再到后来四天三夜紧张到不

    2024年02月05日
    浏览(50)
  • 2020年高教社杯全国大学生数学建模竞赛---校园供水系统智能管理(Python代码实现)

    目录 💥1 概述 📚2 问题 🎉3 运行结果 👨‍💻4 Python代码 校园供水系统是校园公用设施的重要组成部分,学校为了保障校园供水系统的正常运行需要投入大量的人力、物力和财力。随着科学技术的发展,校园内已经普遍使用了智能水表,从而可以获得大量的实时供水系统运

    2024年02月11日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包