序言:
感谢十八届天下大学生智能汽车比赛-室外赛的主理方和组委会让我们有了参加这次比赛的机会!当我们在参加5g室外专项赛时,我们发现很多的组又很多的同砚都是第一次参加智能车比赛对于智能车的了解还不是很深,对于代码如何编写需要哪些模块也不是很熟悉,所以我们计划开源本方案,为未来的智能车参赛者提供一丝丝帮助!如果代码方面的问题可以私信我,若可以或许提供帮助,一定竭尽全力!
github仓库:https://github.com/zzyt6/Smartcar_5g.git
项目简介
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);
- }
复制代码 尾言
感谢湘潭大学电工电子实行室为我们提供的参赛机会,感谢向礼丹老师和电工电子实行室的每个指导老师,感谢十七届参加室外赛的朱嘉明和崔全学长,衷心祝愿各位未来参加智能车比赛的车友可以或许赛出佳绩!在智能车比赛中再创辉煌!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |