搭建一台能够利用摄像头实时捕捉前方画面,通过 OpenCV 库分析图像以识别障碍物,并自动控制电机转向以躲避障碍的智能小车。
要实现这个项目,你需要一个能够运行 C++ 和 OpenCV 的“大脑”,以及配套的机械和电子元件。
核心控制器 (大脑):
摄像头:
小车底盘:
电机驱动模块:
电源:
(可选) 辅助传感器:
硬件连接示意图:
这是项目的核心,我们将使用 OpenCV 来实现视觉避障。
最简单有效的视觉避障策略是区域检测法。
捕获图像: 从摄像头获取一帧实时画面。
预处理:
cv::GaussianBlur
) 以减少噪点。划分感兴趣区域 (ROI - Region of Interest):
障碍物判断:
cv::cvtColor
)。cv::Canny
) 提取出该区域的边缘。cv::countNonZero
)。决策与控制:
你需要安装好 C++ 编译器 (g++)、CMake 构建工具、OpenCV 库以及树莓派的 GPIO 控制库(如 pigpio
或 wiringPi
)。
obstacle-avoidance-car/
├── src/
│ ├── main.cpp //主函数,程序入口
│ ├── MotorControl.h //电机控制类的头文件
│ ├── MotorControl.cpp //电机控制类的实现
│ ├── VisionProcessor.h //视觉处理类的头文件
│ └── VisionProcessor.cpp //视觉处理类的实现
├── CMakeLists.txt //CMake 构建脚本
MotorControl.h
(电机控制类)
这个类封装了对 GPIO 的底层操作,提供简单的上层接口。
#pragma once
class MotorControl {
public:
MotorControl(); // 构造函数,初始化GPIO
~MotorControl(); // 析构函数,释放GPIO资源
void moveForward();
void moveBackward();
void turnLeft();
void turnRight();
void stop();
private:
// 定义连接到L298N的GPIO引脚号
const int ENA = 1; // 左轮使能
const int IN1 = 2;
const int IN2 = 3;
const int ENB = 4; // 右轮使能
const int IN3 = 5;
const int IN4 = 6;
};
VisionProcessor.h
(视觉处理类)
这个类负责所有的 OpenCV 操作。
#pragma once
#include
// 定义一个结构体来表示障碍物状态
struct ObstacleState {
bool isLeftBlocked;
bool isCenterBlocked;
bool isRightBlocked;
};
class VisionProcessor {
public:
VisionProcessor(int width, int height, int threshold);
ObstacleState detect(const cv::Mat& frame);
private:
int frameWidth;
int frameHeight;
int edgeThreshold; // 边缘数量阈值
};
VisionProcessor.cpp
的核心 detect
方法
#include "VisionProcessor.h"
ObstacleState VisionProcessor::detect(const cv::Mat& frame) {
ObstacleState state = {false, false, false};
// 1. 定义ROI区域 (只取下半部分)
cv::Rect roi_rect(0, frameHeight / 2, frameWidth, frameHeight / 2);
cv::Mat roi = frame(roi_rect);
// 2. 定义左、中、右三个子区域
int regionWidth = frameWidth / 3;
cv::Rect left_region_rect(0, 0, regionWidth, roi.rows);
cv::Rect center_region_rect(regionWidth, 0, regionWidth, roi.rows);
cv::Rect right_region_rect(regionWidth * 2, 0, regionWidth, roi.rows);
cv::Mat left_roi = roi(left_region_rect);
cv::Mat center_roi = roi(center_region_rect);
cv::Mat right_roi = roi(right_region_rect);
// 3. 对每个区域进行处理
auto process_region = [&](const cv::Mat& region) {
cv::Mat gray, edges;
cv::cvtColor(region, gray, cv::COLOR_BGR2GRAY);
cv::GaussianBlur(gray, gray, cv::Size(5, 5), 0);
cv::Canny(gray, edges, 50, 150);
return cv::countNonZero(edges);
};
int left_edges = process_region(left_roi);
int center_edges = process_region(center_roi);
int right_edges = process_region(right_roi);
// 4. 判断障碍物
if (left_edges > this->edgeThreshold) state.isLeftBlocked = true;
if (center_edges > this->edgeThreshold) state.isCenterBlocked = true;
if (right_edges > this->edgeThreshold) state.isRightBlocked = true;
// 可选:在原图上画出区域和状态用于调试
// cv::rectangle(frame, ...);
return state;
}
main.cpp
(主循环)
#include
#include "MotorControl.h"
#include "VisionProcessor.h"
int main() {
MotorControl car;
VisionProcessor vision(320, 240, 500); // 图像尺寸320x240, 边缘阈值500
cv::VideoCapture cap(0); // 打开0号摄像头
if (!cap.isOpened()) {
std::cerr << "Error: Cannot open camera!" << std::endl;
return -1;
}
cap.set(cv::CAP_PROP_FRAME_WIDTH, 320);
cap.set(cv::CAP_PROP_FRAME_HEIGHT, 240);
cv::Mat frame;
while (true) {
cap >> frame;
if (frame.empty()) break;
ObstacleState state = vision.detect(frame);
// 根据状态控制小车
if (state.isCenterBlocked) {
std::cout << "Center blocked! Stopping and turning." << std::endl;
car.stop();
// 添加更复杂的转向逻辑
car.turnRight();
} else if (state.isLeftBlocked) {
std::cout << "Left blocked! Turning right." << std::endl;
car.turnRight();
} else if (state.isRightBlocked) {
std::cout << "Right blocked! Turning left." << std::endl;
car.turnLeft();
} else {
std::cout << "Path clear! Moving forward." << std::endl;
car.moveForward();
}
// 按 'q' 键退出
if (cv::waitKey(30) == 'q') {
break;
}
}
car.stop();
return 0;
}
edgeThreshold
(边缘数量阈值) 是最重要的参数。你需要在实际环境中不断调试它,找到一个既能检测到真实障碍物,又不会被地面上无关紧要的纹理干扰的理想值。