使用opencv c++占用栅格地图的实现(对数几率回归)(二)
上一篇简单简述了python opencv 借助numpy的数据结构运算实现了 占用栅格地图的构建,其中有很多巧妙的设计,今天使用opencv c++进行复现和
移植,在没有numpy的数据结构支撑下使用opencv来实现,看效果。
其中
在 numpy中会使用到l2的范数,dist = np.linalg.norm(robotpt - x)
这个在c/c++中是不存在,只能使用 sqrt 和 pow实现求距离。
另外opencv的 exp是对 浮点矩阵就 指数,这个需要注意。
numpy的所有二维array都要使用 opencv 的mat 替代和二维数组替换,涉及到 数组转mat 的方式。
sensor_model.cpp 如下
#include <iostream> #include <stdlib.h> #include <algorithm> #include <time.h> #include <unistd.h> #include <sys/time.h> #include <stack> #include <opencv2/opencv.hpp> #include "sensor_model.h" #include "occ_grid.h" using namespace std; using namespace cv; static Point get_centre(vector<Point> v) { double x = 0, y = 0; int s = v.size(); for (int i = 0; i < v.size(); i++) { x += v[i].x * 100/s; y += v[i].y * 100/s; } return Point((int) x/100, (int) y/100); } //计算两个点的角度 static double CalculateAngle(Point Mar1Point, Point Mar2Point) { // double k = (double)(Mar2Point.y - Mar1Point.y) / (Mar2Point.x - Mar1Point.x); //计算斜率 double arcLength1 = atan2(Mar2Point.y - Mar1Point.y,Mar2Point.x - Mar1Point.x); //弧度 double current_angle = arcLength1 * 180 / M_PI; //角度 return current_angle; } //int Map_Size = 100; //int Xr = float(Map_Size/2);// #Set intial robot position //int Yr = float(Map_Size/2); //int Rangle =0; int SonarModelArray(cv::Mat occmap,int Xr,int Yr,int Rangle,int SonarDist,int cellsize,float scale) { int Map_Size = occmap.cols; //这里必须重新构建新地图,如果直接使用occmap 绘制,会导致重复使用出现重影 Mat mapgrid(Map_Size, Map_Size, CV_8UC1, cv::Scalar(0)); int thick = int(cellsize * scale); SonarDist = int(SonarDist * scale); //创建一张图片 100*100的unsigned char 矩阵 都为0 //Mat img1(Map_Size, Map_Size, CV_8UC1, cv::Scalar(0)); //Returns: Sonar_log - Array of log odds probabilities. //This can be added to existing log odds occ map to update map with latest sonar data. //double **Sonar_log[Map_Size][Map_Size]; int row = occmap.rows;//300 int col = occmap.cols;//300 int thicknesss=-1; //printf("SonarDist:%d \n",SonarDist); //在新地图中使用画椭圆 ellipse(mapgrid,Point(int(Xr),int(Yr)),Size(SonarDist, SonarDist), Rangle, -15, 15, Scalar(255), thicknesss); //Mat dst; //resize(src, dst, cv::Size(1000, 1000)); //创建一个mat用于存储新的坐标 //std::vector<cv::Point2i> locations; // output, locations of non-zero pixels Mat wLocMat = Mat::zeros(mapgrid.size(),CV_8UC1); //findNonZero(src, locations); //在新地图中找到非0,不能使用原地图 findNonZero(mapgrid, wLocMat); //for(int m=0;m<row;m++) // memset(Sonars_logs[m],0,col); for (int i = 0; i < wLocMat.total(); i++ ) { // cout << "Zero#" << i << ": " << wLocMat.at<Point>(i).x << ", " << wLocMat.at<Point>(i).y << endl; //使用范数求距离 //使用范数求距离 float dealtx = pow(wLocMat.at<Point>(i).x - Xr,2); float dealty = pow(wLocMat.at<Point>(i).y - Yr,2); float dist = sqrt(dealtx + dealty); //printf("dist:%d \n",dist);//dist zhengque int Ximg = wLocMat.at<Point>(i).x ; //# 是一个值 对应x int Yimg = wLocMat.at<Point>(i).y ;//# 是一个值 对应Y Point startp,robotp; startp.x = Ximg; startp.y = Yimg; robotp.x = Xr; robotp.y = Yr; float theta= CalculateAngle(robotp,startp) - Rangle; /////数量一致 小数点有偏差 if(theta < -180) //#Note:numpy and OpenCV X and Y reversed theta = theta + 360 ; else if( theta > 180) theta = theta - 360 ; double sigma_t = 5;//这里可以直接去搜标准的正态分布 一看就明白 double A = 1 / (sqrt(2*M_PI*sigma_t)); double C = pow((theta/sigma_t),2); double B = exp(-0.5*C); double Ptheta = A*B ; //小数点有偏差 double Pdist = (SonarDist - dist/2)/SonarDist; double P = (Pdist*2)*Ptheta; double Px=0,logPx=0 ; //printf("dist:%f %d %d\n",dist,SonarDist,thick); if (dist > (SonarDist - thick) && dist < (SonarDist + thick))// #occupied region { Px = 0.5 + Ptheta;//这路的Ptheta 可以改成0.5*cell //printf("Px:%f \n",Px); logPx = log(Px/(1-Px)); Sonars_logs[Yimg][Ximg] = logPx; //printf("logPx:%f \n",logPx); // #occ = np.append(occ,[x],0) } else{// #free region Px = 0.5 - P;//这的P 可以改成0.5*cell //printf("Px free:%f \n",Px); logPx = log(Px/(1-Px)); Sonars_logs[Yimg][Ximg] = logPx; //printf("logPx free:%f \n",logPx); //printf("p:%d,%d \n",Yimg,Ximg); } } //unsigned char *ptmp = NULL;//这是关键的指针!! ptmp指针指向的是img2这个图像 return 0; }
最终使用
SonarModelArray(cv::Mat occmap,int Xr,int Yr,int Rangle,int SonarDist,int cellsize,float scale) 调用。
occ_grid.cpp
如下
#include <iostream> #include <stdlib.h> #include <algorithm> #include <time.h> #include <unistd.h> #include <sys/time.h> #include <stack> #include <opencv2/opencv.hpp> #include "sensor_model.h" #include "occ_grid.h" #include <fstream> #include <sstream> using namespace std; using namespace cv; string Trim(string& str) { str.erase(0,str.find_first_not_of(" \t\r\n")); str.erase(str.find_last_not_of(" \t\r\n") + 1); return str; } static double CalculateRadians(int degree) { //弧度 double current_angle = degree *M_PI/ 180 ; //角度 return current_angle; } #define Map_Size 300 double Sonars_logs[Map_Size][Map_Size]={0}; int offline_test_Array() { int mm_per_tick = 5 ;//Encoder resolution int Xr = float(Map_Size/2);// #Set intial robot position int Yr = float(Map_Size/2); // 读文件 ifstream inFile("Data.txt", ios::in); string lineStr; vector<vector<string>> strArray; int cnt =0; int cellsize = 50 ;// #Set cell width and height in mm float scale = float(1)/cellsize;// #Set map scale Mat src(Map_Size, Map_Size, CV_8UC1, cv::Scalar(0)); int row = Map_Size;//src.rows;//300 int col = Map_Size;//src.cols;//300 Mat Map_log_sonar = Mat(row, col, CV_64F,cv::Scalar(0.0)); Mat Map_log_sonar2= Mat(row, col, CV_64F,cv::Scalar(0.0)); Mat Map_log_sonar_res= Mat(row, col, CV_64F,cv::Scalar(0.0)); while (getline(inFile, lineStr)) { // 打印整行字符串 // cout << lineStr << endl; // 存成二维表结构 stringstream ss(lineStr); string str; vector<string> lineArray; // 按照逗号分隔 while (getline(ss, str, ',')){ lineArray.push_back(str); } string angle_str = Trim(lineArray[2]) ;//Robot heading angle int angle = atoi(angle_str.c_str()); int inv_angle = angle+30 ;//angle for rear sonar sensor int left_angle = angle-30 ; string a0 = Trim(lineArray[0]); string a1 = Trim(lineArray[1]); int avencoder = (atoi(a0.c_str()) + atoi(a1.c_str()))/2 ;//Distance travelled by robot is approximated to average of both encoder readings string Fsonar_str = Trim(lineArray[3]) ;//Sonar values are in cm, convert to mm by muliplying by 10 int Fsonar = atoi(Fsonar_str.c_str())*10; string Rsonar_str = Trim(lineArray[4]); int Rsonar = atoi(Rsonar_str.c_str())*10; //string LIR = Trim(lineArray[5])*10 ;//Same for IR sensor values //string RIR = Trim(lineArray[6])*10 ; cout << " angle : " << angle << endl;// cout << " inv_angle : " << inv_angle << endl;// cout << " Fsonar : " << Fsonar << endl;// cout << " Rsonar : " << Rsonar << endl;// printf("convert begin \n"); cv::Mat Aimg(Map_Size, Map_Size, CV_64F,cv::Scalar(0.0)); if (Fsonar < 5000){ SonarModelArray(src,Xr,Yr,angle,Fsonar,cellsize,scale); std::memcpy(Aimg.data, Sonars_logs, Map_Size*Map_Size*sizeof(double)); //cv::add(Aimg,Map_log_sonar,Map_log_sonar); Map_log_sonar = Map_log_sonar + Aimg; } if (Rsonar < 5000){ SonarModelArray(src,Xr,Yr,inv_angle,Rsonar,cellsize,scale); std::memcpy(Aimg.data, Sonars_logs, Map_Size*Map_Size*sizeof(double)); cv::add(Aimg,Map_log_sonar,Map_log_sonar); Map_log_sonar = Map_log_sonar + Aimg; } if (Rsonar < 5000){ SonarModelArray(src,Xr,Yr,left_angle,Rsonar,cellsize,scale); std::memcpy(Aimg.data, Sonars_logs, Map_Size*Map_Size*sizeof(double)); cv::add(Aimg,Map_log_sonar,Map_log_sonar); Map_log_sonar = Map_log_sonar + Aimg; } int Xrnext = Xr + float((cos(CalculateRadians(angle)) * (avencoder*mm_per_tick)*scale)) ;// // Calculate new robot position from encoder readings int Yrnext = Yr + float((sin(CalculateRadians(angle)) * (avencoder*mm_per_tick)*scale)); //#cv2.line(map_image, (int(Xr),int(Yr)), (int(Xrnext),int(Yrnext)), 150, 1) Xr = Xrnext; Yr = Yrnext; exp(Map_log_sonar, Map_log_sonar2);//e的0次方等于1 如果MAP_LOG_SONAR 的数据有0的存在,//经过求e后在map_log_sona2中变成1 然后下面的1/2是显示灰色的原因 Mat img2(Map_Size, Map_Size, CV_8UC1, cv::Scalar(0)); Mat img3(Map_Size, Map_Size, CV_8UC1, cv::Scalar(0)); img2 =1- (1 / ( 1 + Map_log_sonar2)); img3 = 1 -img2; //resize(src, dst, cv::Size(1000, 1000)); resize(img3,img3,Size(1000,1000)); imshow("Map_log",img3); waitKey(0); } } int offline_test() { //int Map_Size = 300; int mm_per_tick = 5 ;//Encoder resolution int Xr = float(Map_Size/2);// #Set intial robot position int Yr = float(Map_Size/2); // 读文件 ifstream inFile("Data.txt", ios::in); string lineStr; vector<vector<string>> strArray; int cnt =0; int cellsize = 50 ;// #Set cell width and height in mm float scale = float(1)/cellsize;// #Set map scale Mat src(Map_Size, Map_Size, CV_8UC1, cv::Scalar(0)); int row = Map_Size;//src.rows;//300 int col = Map_Size;//src.cols;//300 Mat Map_log_sonar = Mat(row, col, CV_64F,cv::Scalar(0.0)); Mat Map_log_sonar2= Mat(row, col, CV_64F,cv::Scalar(0.0)); Mat Map_log_sonar_res= Mat(row, col, CV_64F,cv::Scalar(0.0)); cv::Mat Aimg(Map_Size, Map_Size, CV_64F,cv::Scalar(0.0)); while (getline(inFile, lineStr)) { // 打印整行字符串 // cout << lineStr << endl; // 存成二维表结构 stringstream ss(lineStr); string str; vector<string> lineArray; // 按照逗号分隔 while (getline(ss, str, ',')){ lineArray.push_back(str); } string angle_str = Trim(lineArray[2]) ;//Robot heading angle int angle = atoi(angle_str.c_str()); int inv_angle = angle+180 ;//angle for rear sonar sensor string a0 = Trim(lineArray[0]); string a1 = Trim(lineArray[1]); int avencoder = atoi(a0.c_str())+ atoi(a1.c_str())/2 ;//Distance travelled by robot is approximated to average of both encoder readings string Fsonar_str = Trim(lineArray[3]) ;//Sonar values are in cm, convert to mm by muliplying by 10 int Fsonar = atoi(Fsonar_str.c_str())*10; string Rsonar_str = Trim(lineArray[4]); int Rsonar = atoi(Rsonar_str.c_str())*10; //string LIR = Trim(lineArray[5])*10 ;//Same for IR sensor values //string RIR = Trim(lineArray[6])*10 ; cout << " angle : " << angle << endl;// cout << " inv_angle : " << inv_angle << endl;// cout << " Fsonar : " << Fsonar << endl;// cout << " Rsonar : " << Rsonar << endl;// printf("convert begin \n"); if (Fsonar < 5000){ double **Sonar_logs =SonarModel(src,Xr,Yr,angle,Fsonar,cellsize,scale); double *data = NULL; for(int i=0;i< Map_Size; i++) //行数--高度 { data = Aimg.ptr<double>(i); //指向第i行的数据 for(int j=0;j<Map_Size;j++) //列数 -- 宽度 { data[j] = Sonar_logs[i][j]; } } //std::memcpy(Aimg.data, Sonar_log, Map_Size*Map_Size*sizeof(double)); //cv::add(Aimg,Map_log_sonar,Map_log_sonar); Map_log_sonar = Map_log_sonar + Aimg; for(int i=0;i< Map_Size;i++) free(Sonar_logs[i]); free(Sonar_logs); } /*if (Rsonar < 5000){ double **Sonar_logs =SonarModel(src,Xr,Yr,inv_angle,Rsonar,cellsize,scale); double* data =NULL; for(int i=0;i<Map_Size;i++) //行数--高度 { data = Aimg.ptr<double>(i); //指向第i行的数据 for(int j=0;j<Map_Size;j++) //列数 -- 宽度 { data[j] = Sonar_logs[i][j]; } } //std::memcpy(Aimg.data, Sonar_log, Map_Size*Map_Size*sizeof(double)); cv::add(Aimg,Map_log_sonar,Map_log_sonar); for(int i=0;i< Map_Size;i++) free(Sonar_logs[i]); free(Sonar_logs); }*/ int Xrnext = Xr + float((cos(CalculateRadians(angle)) * (avencoder*mm_per_tick)*scale)) ;// // Calculate new robot position from encoder readings int Yrnext = Yr + float((sin(CalculateRadians(angle)) * (avencoder*mm_per_tick)*scale)); //#cv2.line(map_image, (int(Xr),int(Yr)), (int(Xrnext),int(Yrnext)), 150, 1) Xr = Xrnext; Yr = Yrnext; exp(Map_log_sonar, Map_log_sonar2); Mat img2(Map_Size, Map_Size, CV_8UC1, cv::Scalar(0)); ; img2 = (1 / ( 1 + Map_log_sonar2)); //resize(src, dst, cv::Size(1000, 1000)); resize(img2,img2,Size(1000,1000)); imshow("Map_log",img2); waitKey(0); } }main.cpp
#include <iostream> #include <stdlib.h> #include <algorithm> #include <time.h> #include <unistd.h> #include <sys/time.h> #include <stack> #include <opencv2/opencv.hpp> #include <math.h> #include "sensor_model.h" #include "occ_grid.h"
int main(int argc ,char *argv[])
{ offline_test_Array();
}
依旧使用 rovibot进行测试。
看上去更平滑一些。