众志成城 抗击肺炎

如何预防新型冠状病毒肺炎

1

保持手卫生。咳嗽、饭前便后、接触或者处理动物粪便后,要用流水洗手,或者使用含有酒精成分的免洗洗手液。

2

保持室内空气流畅。避免到封闭、空气不流通的公众场所和人流集中的地方,必要时请佩戴口罩。

3

咳嗽和打喷嚏时用纸巾或者屈肘遮掩口鼻,防治飞沫传播。不要打完喷嚏后揉眼睛或接触粘膜部位。

4

良好的安全饮食习惯。处理生食和熟食的切菜板及刀具分开,做饭时彻底煮熟肉类和蛋类。

之前在做一个单目测距的小项目,大概需要就是用单目相机,对一个特定的目标进行识别并测算相机与该目标的距离。所以便去网上找了一堆教程,这里给大家总结一下,希望给小白们一个参考。首先是基本需求:

  • opencv自然要会的,这咱就不多说了,会一点就行

  • 需要一个摄像头,我用的是一个畸变很大的鱼眼免驱动摄像头,大家用电脑上的那个自带摄像头也可以的,就是不方便。

  • 需要MATLAB进行相机标定

  • 需要一个编程环境,VS2017,至于VS怎么配置opencv,可以参看一些相关的CSDN博文。

网上的方法大概有两种,这里主要介绍一个我身边的大哥们都称做PnP问题的一个方法,但会另外简单介绍两个比较简单粗暴的,原理可行但其实效果不佳的方法。

相机畸变矫正

在用相机进行单目测距时,需要用到一个叫相机内参的东西,而这需要靠相机标定来得到。这些大概要从相机模型说起,相机模型是每个学opencv的同学早晚的要接触到的吧!

我们高中都做过小孔成像的实验,小孔相机模型就是最简单通用的一种相机模型,这个模型我们就用下面一个图带过好了:

其中f为我们熟知的相机参数——焦距,而光轴与成像平面的交点称为主点,X表示箭头长度,Z是相机到箭头的距离。在上图这个简单且理想的小孔成像"相机"中,我们可以轻松的写出黄色箭头在现实世界坐标系与成像平面坐标系之间的转换关系:

但是在实际相机中,成像平面就是相机感光芯片,针孔就是透镜,然而主点却并不再在成像平面的中心了(也就是透镜光轴与感光芯片中心并不在一条线上了),因为在实际制作中我们是无法做到将相机里面的成像装置以微米级别的精度进行安装的,因此我们需要引入两个新的参数Cx和Cy,来对我们硬件的偏移进行矫正:

上式中我们引入了两个不同的焦距fx和fy,这是因为单个像素在低价成像装置上是矩形而不是正方形。其中,fx是透镜的物理焦距长度与成像装置的每个单元尺寸Sx的乘积。

通过上式我们可以知道相机内参的四个参数了,分别是fx,fy,Cx,Cy。但在计算中,我们常通过一些数学技巧来进行一定的变换,从而得到下式:

其中:

通过上面的式子,我们可以将空间中的点和图片中的点一一对应起来。式中的矩阵M就是我们常听说的相机内参矩阵了。

相机外参

而有相机内参,就有相机外参了,相机外参来源于相机自身的畸变,畸变可以分为径向畸变(有透镜的形状造成)和切向畸变(由整个相机自身的安装过程造成)。

镜像畸变是由凸透镜本身形状引起的,好的透镜,经过一些精密处理,畸变并不明显,但在普通网络相机上畸变显得特别突出。我们可以把畸变看作r=0附近的泰勒奇数展开的前几项来便是。一般为前两项 k1 , k2,对于鱼眼透镜 ,会用前三项 k3 。成像装置上某点的径向位置可以根据以下等式进行调整,这时我们便有了3个或2个的未知变量:

这里(x,y)是成像装置上畸变点的原始位置,(Xcorrected,Ycorrected)是矫正后的新位置。

切向畸变是由于制造上的缺陷使透镜不与成像平面平行而产生的。切向畸变可以用两个参数p1 和 p2 来表示:

至此,我们得到了共五个参数:K1 K2 K3 P1 P2 ,这五个参数是我们消除畸变所必须的,称为畸变向量,也叫相机外参。

相机标定

在上文,相机内参加上相机外参一共有至少8个参数,而我们要想消除相机的畸变,就要靠相机标定来求解这8个未知参数。

说完相机模型,又要说一下相机标定了,相机标定是为了求解上面这8个参数的,那求解出这8个参数可以干什么呢?可以进行软件消除畸变,也就是在得知上面8个参数后,利用上面罗列的数学计算式,将每个偏移的像素点归位。

标定需要用到一个叫标定板的东西,有很多种类,但常用的大概就是棋盘图了,棋盘要求精度需要很高,格子是正方形,买一张标定板很贵的,在csdn上下棋盘图也要画好多c币,所以大家可以用word画一张,很简单的,只要做一个5列7行的表格,拉大到全页,再设置每个格子的宽高来将它设为正方形再涂色就可以了。这张图里有符号,但打印出来就没有了,建议大家自己画一张就OK了。

标定过程是用MATLAB进行的,过程就不在这里说了,CSDN上的教程一抓一大把,在完成标定后MATLAB会返回相机的内参和外参。关于原理,《学习oepncv3》这本书已经说的很好了,除了照着书抄我说不出什么新意,但今天,原理不懂也没有关系。

有了相机内参外参后,我们就可以进行相机消畸变了:

#include <opencv2/opencv.hpp>#include <opencv2\highgui\highgui.hpp>#include <iostream>#include <stdio.h>
using namespace std;using namespace cv;
const int imageWidth = 640; //定义图片大小,即摄像头的分辨率  const int imageHeight = 480;Size imageSize = Size(imageWidth, imageHeight);Mat mapx, mapy;// 相机内参Mat cameraMatrix = (Mat_<double>(3, 3) << 273.4985, 0, 321.2298,0, 273.3338, 239.7912,0, 0, 1);// 相机外参Mat distCoeff = (Mat_<double>(1, 4) << -0.3551, 0.1386,0, 0);Mat R = Mat::eye(3, 3, CV_32F);
VideoCapture cap1; //打开摄像头
void img_init(void)  //初始化摄像头{  cap1.set(CAP_PROP_FOURCC, 'GPJM');  cap1.set(CAP_PROP_FRAME_WIDTH, imageWidth);  cap1.set(CAP_PROP_FRAME_HEIGHT, imageHeight);}
int main(){  initUndistortRectifyMap(cameraMatrix, distCoeff, R, cameraMatrix, imageSize, CV_32FC1, mapx, mapy);  Mat frame;  img_init();while (1)  {    cap1>>frame;    imshow("原鱼眼摄像头图像",frame);    remap(frame,frame,mapx,mapy, INTER_LINEAR);    imshow("消畸变后",frame);    waitKey(30);  }return 0;}

上面源码中我们在32行和39行有两个函数,就是opencv提供给我们进行消畸变的函数。

使用cv::initUndistortRecitifyMap()函数计算矫正映射,函数原型如下:

initUndistortRectifyMap(InputArray  cameraMaxtrix,    3*3内参矩阵 InputArray  distCoeffs,       畸变系数1*4向量InputArray  R,           可以使用或者设置为noArray()。是一个旋转矩阵,将在矫正前预先使用,来补偿相机相对于相机所处的全局坐标系的旋转。InputArray  newCameraMatrix,  单目成像时一般不会使用它Size    size,           输出映射的尺寸,对应于用来矫正的图像的尺寸int      m1type,        最终的映射类型,可能只为CV_32FC1  32_16SC2,对应于map1的表示类型OutputArray  map1,        OutputArray  map2);

我们只需在程序开头使用该函数计算一次矫正映射,就可以使用cv::remap()函数将该矫正应用到视频每一帧图像。

PnP方法测距

好了到此我们对相机的那点事儿有了一点点的了解了,那什么是PnP问题呢?在有些情况下我们已经知道了相机的内在参数,因此只需要计算正在观察的对象的位置,这种情况下与一般的相机标定明显不同,但有相通之处。这种操作就叫N点透视(Perspective N-Point)或PnP问题。

bool cv::solvePnP(  cv::InputArray  objectPoints,     //三维点坐标矩阵,至少四个(世界坐标系)  cv::InputArray  imagePoints,      //该四个点在图像中的像素坐标  cv::InputArray  cameraMatrix,     //相机内参矩阵(9*9)  cv::InputArray  distCoeffs,       //相机外参矩阵(1*4)或(1*5)  cv::OutputArray rvec,             //输出旋转矩阵  cv::OutputArray tvec,             //输出平移矩阵  bool        useExtrinsicGuess = false,    int        flags = cv::SOLVEPNP_ITERATIVE);

首先来解释一下该函数的输出:

  • 旋转矩阵就是一个3*1的向量,该矩阵可以表示相机相对于世界坐标系XYZ轴的3个旋转角度。

  • 平移矩阵也是一个3维向量,可以表示相机相对于物体的XYZ轴的偏移,而这个矩阵就是我们需要求的:我们知道了相机相对于物体的位置,也就得到了距离,从而实现了测距的目的。
    输入的参数:

  • 第一个参数,是物体任意四个点在世界坐标系的三位点坐标,为什么是四个其实很好理解,我们需要求解的是一个旋转矩阵和XYZ轴偏移量,一共四个未知量,需要至少列四个式子才可以求解。

  • 第二个参数,我们在第一个参数中任意找的物体上的四个点在图像中的像素坐标。

  • 现在就很清楚明白了吧?通过旋转向量和平移向量就可以得到相机坐标系相对于世界坐标系的旋转参数与平移情况。

不过我们还要解决一个问题,如何确保这四个点的位置呢?就是,例如物体是一个正方形板子,板子长为2L,我可以选板子中心作为世界坐标系的中心,那么我可以得到板子四个角上的坐标分别为(L,L),(L,-L),(-L,L),(-L,-L)。但如何确定图像上哪四个点是板子的四个角呢?你就需要把板子识别出来。但如果不是个板子是个人呢?你怎么把人分出来?这就需要更复杂的东西了,什么语义分割啊分类器啊啥的,这里就不多说了。

那我不取板子的四个角,利用角点检测任意取四个点也可以,这就解决了世界坐标系与像素坐标系之间的对应问题,但又有一个新问题,如何确保这四个角点是物体身上的而不是背景上的呢?

所以说这么多,我们便引入了二维码,我们可以直接识别二维码来测距,这儿就要用到一个叫ZBar库的东西了,它是一个可以识别二维码或条形码的函数库,具体的自行百度吧。那我们还需要学一个新库?opencv库都还没学明白呢,又要学一个识别二维码的?其实不需要,这个库的两个例程已经可以满足我们的需要了:

例程一:

#include <zbar.h>#include <opencv2\opencv.hpp>#include <iostream>
int main(int argc, char*argv[]){  zbar::ImageScanner scanner;  scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_ENABLE, 1);  cv::VideoCapture capture;  capture.open(0);  //打开摄像头  cv::Mat image;  cv::Mat imageGray;std::vector<cv::Point2f> obj_location;bool flag = true;
if (!capture.isOpened())  {std::cout << "cannot open cam!" << std::endl;  }else  {while (flag)    {      capture >> image;      cv::cvtColor(image, imageGray, CV_RGB2GRAY);int width = imageGray.cols;int height = imageGray.rows;      uchar *raw = (uchar *)imageGray.data;      zbar::Image imageZbar(width, height, "Y800", raw, width * height);      scanner.scan(imageZbar);  //扫描条码      zbar::Image::SymbolIterator symbol = imageZbar.symbol_begin();if (imageZbar.symbol_begin() != imageZbar.symbol_end())  //如果扫描到二维码      {        flag = false;//解析二维码for (int i = 0; i < symbol->get_location_size(); i++)        {          obj_location.push_back(cv::Point(symbol->get_location_x(i), symbol->get_location_y(i)));        }for (int i = 0; i < obj_location.size(); i++)        {          cv::line(image, obj_location[i], obj_location[(i + 1) % obj_location.size()], cv::Scalar(255, 0, 0), 3);//定位条码        }for (; symbol != imageZbar.symbol_end(); ++symbol)        {std::cout << "Code Type: " << std::endl << symbol->get_type_name() << std::endl; //获取条码类型std::cout << "Decode Result: " << std::endl << symbol->get_data() << std::endl;  //解码        }        imageZbar.set_data(NULL, 0);      }      cv::imshow("Result", image);      cv::waitKey(50);    }    cv::waitKey();  }return 0;}

这个函数可以实现打开摄像头,并识别看到的二维码,进而打印二维码的类型和内容:

例程二:

#include <opencv2/opencv.hpp>#include <zbar.h>
using namespace cv;using namespace std;using namespace zbar;
typedef struct{string type;string data;vector <Point> location;} decodedObject;
// Find and decode barcodes and QR codesvoid decode(Mat &im, vector<decodedObject>&decodedObjects){
// Create zbar scanner  ImageScanner scanner;
// Configure scanner  scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1);
// Convert image to grayscale  Mat imGray;  cvtColor(im, imGray,COLOR_BGR2GRAY);
// Wrap image data in a zbar imageImage image(im.cols, im.rows, "Y800", (uchar *)imGray.data, im.cols * im.rows);
// Scan the image for barcodes and QRCodesint n = scanner.scan(image);
// Print resultsfor(Image::SymbolIterator symbol = image.symbol_begin(); symbol != image.symbol_end(); ++symbol)  {    decodedObject obj;obj.type = symbol->get_type_name();    obj.data = symbol->get_data();
// Print type and datacout << "Type : " << obj.type << endl;cout << "Data : " << obj.data << endl << endl;
// Obtain locationfor(int i = 0; i< symbol->get_location_size(); i++)    {      obj.location.push_back(Point(symbol->get_location_x(i),symbol->get_location_y(i)));    }decodedObjects.push_back(obj);  }}
// Display barcode and QR code location  void display(Mat &im, vector<decodedObject>&decodedObjects){// Loop over all decoded objectsfor(int i = 0; i < decodedObjects.size(); i++)  {vector<Point> points = decodedObjects[i].location;vector<Point> hull;
// If the points do not form a quad, find convex hullif(points.size() > 4)      convexHull(points, hull);else      hull = points;
// Number of points in the convex hullint n = hull.size();
for(int j = 0; j < n; j++)    {      line(im, hull[j], hull[ (j+1) % n], Scalar(255,0,0), 3);    }}
// Display results   imshow("Results", im);  waitKey(0);
}
int main(int argc, char* argv[]){
// Read image  Mat im = imread("zbar-test.jpg");
// Variable for decoded objects vector<decodedObject> decodedObjects;
// Find and decode barcodes and QR codes  decode(im, decodedObjects);
// Display location   display(im, decodedObjects);
return EXIT_SUCCESS;}

该例程可以在实现例程一的功能的基础上,还可以识别出二维码的位置。

代码实现

下面,如何实现测距代码编写呢?我们需要在上面例程二这个代码的基础上,加上相机畸变矫正的代码,还要加上一段PnP函数求解的代码:

vector<Point3f> obj = vector<Point3f>{        cv::Point3f(-HALF_LENGTH, -HALF_LENGTH, 0),  //tl        cv::Point3f(HALF_LENGTH, -HALF_LENGTH, 0),  //tr        cv::Point3f(HALF_LENGTH, HALF_LENGTH, 0),  //br        cv::Point3f(-HALF_LENGTH, HALF_LENGTH, 0)  //bl    };   //自定义二维码四个点坐标    cv::Mat rVec = cv::Mat::zeros(3, 1, CV_64FC1);//init rvec     cv::Mat tVec = cv::Mat::zeros(3, 1, CV_64FC1);//init tvec    solvePnP(obj, pnts, cameraMatrix, distCoeff, rVec, tVec, false, SOLVEPNP_ITERATIVE);

把上面三个部分融合在一起,就可以写出我们的单目测距代码啦:

#include "pch.h"#include <iostream>#include <opencv2/opencv.hpp>#include <zbar.h>
using namespace cv;using namespace std;
#define HALF_LENGTH 15   //二维码宽度的二分之一
const int imageWidth = 640; //设置图片大小,即摄像头的分辨率  const int imageHeight = 480;Size imageSize = Size(imageWidth, imageHeight);Mat mapx, mapy;// 相机内参Mat cameraMatrix = (Mat_<double>(3, 3) << 273.4985, 0, 321.2298,0, 273.3338, 239.7912,0, 0, 1);// 相机外参Mat distCoeff = (Mat_<double>(1, 4) << -0.3551, 0.1386, 0, 0);Mat R = Mat::eye(3, 3, CV_32F);
VideoCapture cap1;
typedef struct   //定义一个二维码对象的结构体{  string type;  string data;  vector <Point> location;} decodedObject;
void img_init(void);   void decode(Mat &im, vector<decodedObject>&decodedObjects);void display(Mat &im, vector<decodedObject>&decodedObjects);int main(int argc, char* argv[]){  initUndistortRectifyMap(cameraMatrix, distCoeff, R, cameraMatrix, imageSize, CV_32FC1, mapx, mapy);  img_init();  namedWindow("yuantu", WINDOW_AUTOSIZE);  Mat im;
while (waitKey(1) != 'q') {    cap1 >> im;if (im.empty())  break;    remap(im, im, mapx, mapy, INTER_LINEAR);//畸变矫正    imshow("yuantu", im);
// 已解码对象的变量    vector<decodedObject> decodedObjects;
// 找到并解码条形码和二维码    decode(im, decodedObjects);
// 显示位置    display(im, decodedObjects);//vector<Point> points_xy = decodedObjects[0].location;  //假设图中就一个二维码对象,将二维码四角位置取出    imshow("二维码", im);waitKey(30);  }
return EXIT_SUCCESS;}
void img_init(void){//初始化摄像头  cap1.open(0);  cap1.set(CAP_PROP_FOURCC, 'GPJM');  cap1.set(CAP_PROP_FRAME_WIDTH, imageWidth);  cap1.set(CAP_PROP_FRAME_HEIGHT, imageHeight);}// 找到并解码条形码和二维码//输入为图像//返回为找到的条形码对象void decode(Mat &im, vector<decodedObject>&decodedObjects){
// 创建zbar扫描仪  zbar::ImageScanner scanner;
// 配置扫描仪  scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_ENABLE, 1);
// 转换图像为灰度图灰度  Mat imGray;  cvtColor(im, imGray, COLOR_BGR2GRAY);
// 将图像数据包 装在zbar图像中//可以参考:https://blog.csdn.net/bbdxf/article/details/79356259  zbar::Image image(im.cols, im.rows, "Y800", (uchar *)imGray.data, im.cols * im.rows);
// Scan the image for barcodes and QRCodes//扫描图像中的条形码和qr码  int n = scanner.scan(image);
// Print resultsfor (zbar::Image::SymbolIterator symbol = image.symbol_begin(); symbol != image.symbol_end(); ++symbol)  {    decodedObject obj;obj.type = symbol->get_type_name();    obj.data = symbol->get_data();
// Print type and data//打印//cout << "Type : " << obj.type << endl;//cout << "Data : " << obj.data << endl << endl;
// Obtain location//获取位置for (int i = 0; i < symbol->get_location_size(); i++)    {      obj.location.push_back(Point(symbol->get_location_x(i), symbol->get_location_y(i)));    }decodedObjects.push_back(obj);  }}// 显示位置  void display(Mat &im, vector<decodedObject>&decodedObjects){// Loop over all decoded objects//循环所有解码对象for (int i = 0; i < decodedObjects.size(); i++)  {    vector<Point> points = decodedObjects[i].location;    vector<Point> hull;
// If the points do not form a quad, find convex hull//如果这些点没有形成一个四边形,找到凸包if (points.size() > 4)      convexHull(points, hull);else      hull = points;    vector<Point2f> pnts;// Number of points in the convex hull//凸包中的点数    int n = hull.size();
for (int j = 0; j < n; j++)    {      line(im, hull[j], hull[(j + 1) % n], Scalar(255, 0, 0), 3);      pnts.push_back(Point2f(hull[j].x, hull[j].y));    }    vector<Point3f> obj = vector<Point3f>{        cv::Point3f(-HALF_LENGTH, -HALF_LENGTH, 0),  //tl        cv::Point3f(HALF_LENGTH, -HALF_LENGTH, 0),  //tr        cv::Point3f(HALF_LENGTH, HALF_LENGTH, 0),  //br        cv::Point3f(-HALF_LENGTH, HALF_LENGTH, 0)  //bl    };   //自定义二维码四个点坐标    cv::Mat rVec = cv::Mat::zeros(3, 1, CV_64FC1);//init rvec     cv::Mat tVec = cv::Mat::zeros(3, 1, CV_64FC1);//init tvec    solvePnP(obj, pnts, cameraMatrix, distCoeff, rVec, tVec, false, SOLVEPNP_ITERATIVE);    cout << "tvec:\n " << tVec << endl;  }}

下图是运行结果:

三个数分别是X,Y,Z的距离了,单位cm,精度可以达到0.1cm。

三角测距法

还记得文章开头的那个小孔相机模型吗?

三角测距法就是基于这个理想的,简单的模型,进行的,在知道物体大小,透镜焦距F,并测出图像中的物体长度后,就可以基于下面公式进行计算长度Z了。

像素块测距法

这个方法是玩openmv时知道的,openmv封装的单目测距算法,就是将目标对象先在固定的距离(10cm)拍一张照片,测出照片中该物体的像素面积。得到一个比例系数K,然后将物体挪到任意位置,就可以根据像素面积估算距离了。

不过这两种方法肯定鲁棒性都不咋样。今天就到这里啦。

微信号 : 计算机视觉战队

知乎:EdisonGzq

● 扫码关注我们

实践篇 | 机器人单目相机测距的实验相关推荐

  1. 【RoboMaster】我是这样搞定第一次单目相机测距的

    之前在做一个单目测距的小项目,大概需要就是用单目相机,对一个特定的目标进行识别并测算相机与该目标的距离.所以便去网上找了一堆教程,这里给大家总结一下,希望给小白们一个参考. 首先是基本需求了: ope ...

  2. 我是这样搞定第一次单目相机测距的

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 之前在做一个单目测距的小项目,大概需要就是用单目相机,对一个特定的 ...

  3. 相机矫正_实战 | 我用位姿解算实现单目相机测距

    在项目过程中,总遇到需要单目视觉给出目标测距信息的情况,其实单目相机本不适合测距,即使能给出,精度也有限,只能在有限制的条件下或者对精度要求很不高的情况下进行应用.该文结合SLAM方法,通过3D-2D ...

  4. 实战 | 巧用位姿解算实现单目相机测距

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 在项目过程中,总遇到需要单目视觉给出目标测距信息的情况,其实单目相 ...

  5. 史上最全 | 单目相机测距测速方法大盘点!

    点击下方卡片,关注"自动驾驶之心"公众号 ADAS巨卷干货,即可获取 点击进入→自动驾驶之心技术交流群 后台回复[ECCV2022]获取ECCV2022所有自动驾驶方向论文! 论文 ...

  6. opencv 单目相机pnp测距(Cpp)

    概述 单目相机pnp测距是通过单目相机拍摄的一张2d图片,来测量图片中某物与相机的距离. 需要知道被测物的实际尺寸 测距前需要先相机标定,需要使用哪个相机进行测距就标定哪个.一旦换成了其他相机, 就要 ...

  7. 单目相机三维姿态解算

    单目相机三维姿态解算 Abstract:This passage mainly describes how to solve pose(Yaw,Pitch,Roll)with signal camer ...

  8. VIO:飞行机器人单目VIO算法测评

    泡泡图灵智库,带你精读机器人顶级会议文章 标题:A Benchmark Comparison of Monocular Visual-Inertial Odometry Algorithms for ...

  9. 半并行深度神经网络(SPDNN)混合架构,首次应用于单目相机的深度

    摘要:近年来,深度神经网络应用于广泛的问题.在这项工作中,卷积神经网络(CNN)应用于从单个摄像机图像(单眼深度)确定深度的问题.设计八个不同的网络来执行深度估计,每个网络适合于特征级别.具有不同池大 ...

最新文章

  1. “算法不行,干啥都不行!”面试官:面试基本都会考这点!
  2. struts2 过滤器和拦截器
  3. Kubernetes的HTTPS和证书问题,汇总
  4. 在C#中利用DirectX实现声音播放(winForm)
  5. MyEclipse中Web项目的重命名问题
  6. c语言self用法,C/C++知识点之Self Numbers C语言 UVA640
  7. python2与python3,Python2和Python3的10大区别
  8. 手机如何访问电脑局域网文件共享服务器,手机怎么访问局域网电脑共享文件
  9. 树莓派4B控制步进电机(电机28BYJ4+驱动板ULN2003)
  10. C#邮件过滤系统(论文+可执行程序+源码+外文翻译+程序操作演示录像)
  11. matlab解坐标方程,matlab程序(解泊松方程)
  12. 自学python经验_我学Python的经验,Python学习经验分享
  13. http 常用的默认端口号
  14. 你所不知道的马化腾:腾讯成功的较完整版本
  15. arduino麦轮转弯程序_Arduino 自动避障智能小车制作教程
  16. IT行业金饭碗?数据库工程师凭什么
  17. 推荐10个顶级的技术公众号
  18. Flash AS2.0脚本语言
  19. 美国留学生研究生计算机专业,美国研究生计算机专业排名
  20. win10更新卡住不动_为什么推荐win7升级到win10?两个电脑系统有什么区别

热门文章

  1. nginx rewrite if指令剖析
  2. java中作用是什么_Java在编程中的作用是什么?
  3. 论文阅读:Compacting, Picking and Growing for Unforgetting Continual Learning
  4. 【智能系统学报】投稿记录
  5. Ubuntu下加速pip安装的方法
  6. 【数学】《离散数学中“群”的概念》
  7. 什么是股票分仓软件, 实现原理解析1
  8. 新开网站不收录的原因及解决方法有哪些
  9. 通用的ARGOX条码打印软件
  10. 100个人排队,编号分别为1到100,每轮淘汰编号为奇数的人,然后重新编号,问最后剩下的人的初始编号是多少