方案选型:
1、OpenCV(C++)
2、0.96 OLED
3、串口
一,OpenCV
这里我选用是C++和Python都支持的CV依赖包,而不是只支持Python的CV包,对于长远来说,我更喜欢C++编程运行来计算机视觉程序,而不太喜欢Python (可能这并不是一件好事),至于为什么,个人偏好而已。
于是就开启了几天头疼的下载、安装、编译…
安装流程参考博客:亲测有效 [自己一遍又一遍的安装,最后找到的比较靠谱的两篇博客]
博客1:https://www.linuxidc.com/Linux/2019-08/159878.html
博客2:https://www.lijl888.com/archives/1127
踩坑点:
1、在网上查找下载安装时,尽量找时间离当下最近的,时间太过于久远就没有必要看了,树莓派安装也要跟得上时代发展。
2、树莓派一系列基础操作:换源 (换为清华镜像源,记得去官网查阅换源的方法),使能摄像头,更新…
sudo apt-get update
sudo apt-get upgrade
3、(我的)树莓派系统镜像默认没有Cmake,如果想要对CV的源代码文件进行编译的话,需要自己下载一个:
sudo apt install cmake # 最简单的办法,但是安装的Cmake好像是旧版本的
这里我推荐是使用Cmake的新版本,可以参考博客:https://blog.youkuaiyun.com/weixin_43371047/article/details/103763918
4、make编译的时候如果出现下列错误:
~/opencv_contrib/modules/xfeatures2d/src/boostdesc.cpp:673:20: fatal error: boostdesc_bgm.i: No such file or directory
可以参考博客:https://blog.youkuaiyun.com/qq_38131812/article/details/90292703
boostdesc_bgm.i
boostdesc_bgm_bi.i
boostdesc_bgm_hd.i
boostdesc_lbgm.i
boostdesc_binboost_064.i
boostdesc_binboost_128.i
boostdesc_binboost_256.i
vgg_generated_120.i
vgg_generated_64.i
vgg_generated_80.i
vgg_generated_48.i
在自己的电脑下载好之后直接使用FileZilla远程传输到树莓派对应的文件夹,然后重新make就行。
5、输入命令,测试环境是否安装成功:
# example Test
6,(可选)安装Vscode:
参考视频教程:https://www.bilibili.com/video/av970000779/
其中下载完之后不必配置tasks.json、launch.json、c_cpp_properties.json等json文件,对于我来说,VScode就是一个文本文档,最终的编译运行由g++ 进行。
你只需要建立如下文件结构,然后使用Cmake 和 Terminal来运行就OK,这里不提供CmakeLists.txt,因为你需要根据实际的安装路径来编写Cmake指导编译和链接,如果这一步完不成,那很有可能你无法运行C++的CV代码 :
"quadro/minimu9-ahrs" alias home
|-build
|-include
|-src
7、(可选)安装CodeBlocks:
sudo apt-get install codeblocks
基础使用教程:https://blog.youkuaiyun.com/wang_shuai_ww/article/details/17225819
不过使用CodeBlocks同样需要配置OpenCV的路径。
8,运行测试代码:
# python 代码
点击运行即可。
#include<opencv2/core.hpp>
#include<opencv2/highgui.hpp>
#include<wiringPi.h>
using namespace cv;
int main(int argc,char ** argv)
{
VideoCapture cap(0); //
if(!cap.isOpened())
return -1;
Mat frame;
while(true)
{
cap >> frame;
imshow("raw",frame);
waitKey(1);
}
return 0;
}
在Terminal中进入工程文件的build文件夹,输入:
# build
cmake .. && make && ./opencv_9_pic_blur_1
最后大公告成,可以在树莓派上编写OpenCV代码了。
二、0.96 OLED
现展示OLED接收数据的代码:
//此函数应放入while循环中不断扫描 及时处理串口接收的信息
void UART3_Driver(void)
{
if(USART3_RX_STA & 0x8000) //接收到了数据
{
ScanScreem(USART3_RX_BUF);
//printf("uart3 len=%d",USART3_RX_STA & 0x7fff);
USART3_RX_STA=0;
}
}
void ScanScreem(u8 *chr)
{
u8 x,y;
u16 i=0;
for(y=0;y<8;y++)
{
OLED_Set_Pos(0,y);
for(x=0;x<128;x++)
{
OLED_WR_Byte(chr[i++],OLED_DATA);
}
}
}
三、串口
1、下载WiringPi
sudo apt-get install wiringpi # 下载
gpio -v # 查看wringpi是否下载安装成功
2、简单的串口通信 minicom
3、OpenCV发送图片数据到电脑串口
4、OpenCV发送图片数据到STM32单片机
注:图片的格式为Mat类,需要转化为OLED可识别的像素点数据
vector<vector<uchar>> decode(Mat pic) //pic
{
int i128 = 0;
int i64 = 0;
int resize_height = 64;
int resize_width = 128;
Mat src = pic; // 将图片传入Mat容器中
Mat dst;
//imshow("src", src); // 原始图像
resize(src, dst, Size(resize_width, resize_height), (0, 0), (0, 0), INTER_LINEAR);
//imshow("13", dst); // 处理之后的图像
cvtColor(dst, dst, COLOR_BGR2GRAY);
blur(dst, dst, Size(3, 3));
Canny(dst, dst, 100, 100 * 2, 3);
imshow("dst", dst); // 处理之后的图像
int w = dst.cols * dst.channels(); //可能为3通道,宽度要乘图片的通道数
int h = dst.rows;
vector<vector<uchar>> array(h, vector<uchar>(w)); //初始化二维vector
for (int i = 0; i < h; i++)
{
uchar* inData = dst.ptr<uchar>(i); //ptr为指向图片的行指针,参数i为行数
for (int j = 0; j < w; j++)
{
array[i][j] = inData[j];
}
}
return array;
}
void dataSendToSTM32(vector<vector<uchar>> array)
{
int temp;
char str[10] = "";
vector<vector<uchar>> dataPic(8,vector<uchar>(128));
for(int i = 0; i<8 ; i++){
for(int j = 0; j<128 ; j++){
temp = 0;
for(int bit = 0; bit<8 ;bit++){
if (array[i * 8 + bit][j] == 255)
temp |= 0x01 << bit;
}
dataPic[i][j] = temp;
serialPutchar(fd,temp);//发送一个字符给串口
delayMicroseconds(0);
}
}
}
四,识别色块个数,发送信息给单片机
#include <string.h>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui_c.h>
#include <stdio.h>
#include "wiringSerial.h"
#include "wiringPi.h"
#include <stdlib.h>
#define colorTest
using namespace cv;
using namespace std;
int fd = serialOpen("/dev/ttyAMA0",115200);//初始化串口
RNG g_rng(12345);
Mat srcPic;
int main(int argc, char* argv[])
{
Mat Pic;
VideoCapture capture(0);
float AreaByTest = 0;
vector<vector<uchar>> arrayT;
Mat element = getStructuringElement(MORPH_RECT,Size(15,15));
cv::Scalar scalarL = cv::Scalar(100,43,46);
cv::Scalar scalarH = cv::Scalar(124,255,255);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
int n = 0;
wiringPiSetup();
while(1)
{
#ifndef colorTest
cout<<"Wait~";
cin >> n;
switch(n){
case 1:srcPic = imread("//home//pi//Desktop//Cpp_OpenCV//testPic//b1.jpg"); break;
case 2:srcPic = imread("//home//pi//Desktop//Cpp_OpenCV//testPic//b2.jpg"); break;
case 3:srcPic = imread("//home//pi//Desktop//Cpp_OpenCV//testPic//b3.jpg"); break;
case 4:srcPic = imread("//home//pi//Desktop//Cpp_OpenCV//testPic//b4.jpg"); break;
case 5:srcPic = imread("//home//pi//Desktop//Cpp_OpenCV//testPic//b5.jpg"); break;
case 6:srcPic = imread("//home//pi//Desktop//Cpp_OpenCV//testPic//b6.jpg"); break;
default:break;
}
cout<<"OK!"<<endl;
imshow("Begin",srcPic);
cvtColor(srcPic, srcPic,COLOR_BGR2HSV);
erode(srcPic,srcPic,element);
inRange(srcPic,scalarL,scalarH,srcPic);
findContours(srcPic,contours,hierarchy,RETR_TREE,CHAIN_APPROX_SIMPLE,Point());
vector<Moments> mu(contours.size());
for (unsigned int i = 0; i < contours.size(); i++) {
mu[i] = moments(contours[i], false);
}
vector<Point2f> mc(contours.size());
for (unsigned int i = 0; i < contours.size(); i++) {
mc[i] = Point2f(static_cast<float>(mu[i].m10 / mu[i].m00), static_cast<float>(mu[i].m01 / mu[i].m00));
}
Mat drawing = Mat::zeros(srcPic.size(), CV_8UC3);
for (unsigned int i = 0; i < contours.size(); i++) {
Scalar color = Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255));
drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point());
}
for (unsigned int i = 0; i < contours.size(); i++) {
// printf(" >通过m00计算出轮廊的[%d]的面积:(m00)= %.2f\n", i, mu[i].m00);
AreaByTest += mu[i].m00;
Scalar color = Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255));
drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point());
}
printf("%d\n",(int)(AreaByTest/1050.0));
AreaByTest = 0;
imshow("YJ",srcPic);
char temp = (AreaByTest/1050.0);
serialPutchar(fd,temp);//发送一个字符给串口
waitKey(0);
#else
capture >> Pic;
imshow("YJ",Pic);
arrayT = decode(Pic);
dataSendToSTM32(arrayT);
waitKey(1);
#endif
}
return 0;
}
五,发挥部分(自制微型服务器:失败)
由于树莓派是32位的Debain系统,而宝塔是64位的,为了解决这一问题:
- 下载宝塔的历史版本
- 修改宝塔的install.sh脚本,取消自动检测系统位数设置
wget -O install.sh http://download.bt.cn/install/install-ubuntu.sh && bash install.sh // 报错,BT不支持32位系统
vi install.sh //修改好之后按:wq 退出
// 删除掉 if ["$is64bit" = '32']; then 的代码段
sudo bash install.sh // 执行安装语句
树莓派给我报了很很多错误,但是不知道会不要产生什么重大影响:
安装完后,宝塔会给出一个登录地址,账号和密码:
将登陆地址中括号里面的内容换为树莓派的IP地址:http://192.168.43.121:8888/
然后打开Chorm,输入链接,账号和密码:
然后下载宝塔需要的一些常用组件【LNMP】:
多半是一些和Web端打交道的工具(php)和一些存储数据的软件(MySQL)
同时还在提心吊胆,担心宝塔把树莓派的SD卡空间给榨干
df -h
(此图为安装之前)
安装完后,就可以输入ip地址访问到树莓派了
【注意:此时树莓派属于私有IP,意味着别人访问不了,要想解决这个问题,需要对树莓派进行内网穿透:使用CDN、frp内网穿透等工具则正常访问】
立刻卸载宝塔:
wget http://download.bt.cn/install/bt-uninstall.sh
sh bt-uninstall.sh
然后再次打开VNC远程连接:
查阅各种方法,进不去。
第四次重装。
总结:树莓派的SD卡空间太小(我的为16GB)。