detect_wheel_ceres.cpp 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904
  1. //
  2. // Created by zx on 2020/7/1.
  3. //
  4. #include "detect_wheel_ceres.h"
  5. #include <ceres/cubic_interpolation.h>
  6. #include <pcl/common/transforms.h>
  7. constexpr float kMinProbability = 0.01f;
  8. constexpr float kMaxProbability = 1.f - kMinProbability;
  9. constexpr float kMaxCorrespondenceCost = 1.f - kMinProbability;
  10. constexpr int kPadding = INT_MAX / 4;
  11. class GridArrayAdapter {
  12. public:
  13. enum { DATA_DIMENSION = 1 };
  14. explicit GridArrayAdapter(const cv::Mat& grid) : grid_(grid) {}
  15. void GetValue(const int row, const int column, double* const value) const {
  16. if (row < kPadding || column < kPadding || row >= NumRows() - kPadding ||
  17. column >= NumCols() - kPadding) {
  18. *value = kMaxCorrespondenceCost;
  19. } else {
  20. *value = static_cast<double>(grid_.at<double >(row - kPadding, column - kPadding));
  21. }
  22. }
  23. int NumRows() const {
  24. return grid_.rows + 2 * kPadding;
  25. }
  26. int NumCols() const {
  27. return grid_.cols + 2 * kPadding;
  28. }
  29. private:
  30. const cv::Mat& grid_;
  31. };
  32. class CostFunctor {
  33. private:
  34. cv::Mat m_map;
  35. double m_scale;
  36. mutable double m_costs_lf, m_costs_rf, m_costs_lr, m_costs_rr;
  37. pcl::PointCloud<pcl::PointXYZ> m_left_front_cloud; //左前点云
  38. pcl::PointCloud<pcl::PointXYZ> m_right_front_cloud; //右前点云
  39. pcl::PointCloud<pcl::PointXYZ> m_left_rear_cloud; //左后点云
  40. pcl::PointCloud<pcl::PointXYZ> m_right_rear_cloud; //右后点云
  41. public:
  42. CostFunctor(pcl::PointCloud<pcl::PointXYZ> left_front, pcl::PointCloud<pcl::PointXYZ> right_front,
  43. pcl::PointCloud<pcl::PointXYZ> left_rear, pcl::PointCloud<pcl::PointXYZ> right_rear,
  44. cv::Mat &map, double scale)
  45. {
  46. m_map = map;
  47. m_scale = scale;
  48. m_left_front_cloud = left_front;
  49. m_right_front_cloud = right_front;
  50. m_left_rear_cloud = left_rear;
  51. m_right_rear_cloud = right_rear;
  52. m_costs_lf = 0.0;
  53. m_costs_rf = 0.0;
  54. m_costs_lr = 0.0;
  55. m_costs_rr = 0.0;
  56. }
  57. template <typename T>
  58. bool operator()(const T* const variable, T* residual) const {
  59. // 每一轮重新初始化残差值
  60. T costs[4]={T(0),T(0),T(0),T(0)};
  61. std::vector<std::vector<T> > deviation_calc_vector;
  62. deviation_calc_vector.resize(4);
  63. T deviation_weight = T(0.05);
  64. T cx = variable[0];
  65. T cy = variable[1];
  66. T theta = variable[2];
  67. T length = variable[3];
  68. T width = variable[4];
  69. T theta_front = variable[5];
  70. T theta_front_weight = T(0.8);
  71. // T wheel_length_img_ratio = variable[6];
  72. T wheel_width_length_ratio = variable[6];
  73. // [1/3, 5/3]
  74. T limited_wheel_length_img_ratio = T(0.85);// = T(0.7) / (T(1) + ceres::exp(T(-wheel_length_img_ratio))) + T(0.85);
  75. // [0.25, 0.35]
  76. T limited_wheel_length_ratio = T(0.25) / (T(1) + ceres::exp(T(-wheel_width_length_ratio))) + T(0.25);
  77. //整车旋转
  78. Eigen::Rotation2D<T> rotation(theta);
  79. Eigen::Matrix<T, 2, 2> rotation_matrix = rotation.toRotationMatrix();
  80. //左前偏移
  81. Eigen::Matrix<T, 2, 1> wheel_center_normal_left_front(length / 2.0, width / 2.0);
  82. //右前偏移
  83. Eigen::Matrix<T, 2, 1> wheel_center_normal_right_front(length / 2.0, -width / 2.0);
  84. //左后偏移
  85. Eigen::Matrix<T, 2, 1> wheel_center_normal_left_rear(-length / 2.0, width / 2.0);
  86. //右后偏移
  87. Eigen::Matrix<T, 2, 1> wheel_center_normal_right_rear(-length / 2.0, -width / 2.0);
  88. //前轮旋转
  89. Eigen::Rotation2D<T> rotation_front(theta_front);
  90. Eigen::Matrix<T, 2, 2> rotation_matrix_front = rotation_front.toRotationMatrix();
  91. const GridArrayAdapter adapter(m_map);
  92. ceres::BiCubicInterpolator<GridArrayAdapter> interpolator(adapter);
  93. double rows = m_map.rows;
  94. double cols = m_map.cols;
  95. //左前轮
  96. int left_front_num = m_left_front_cloud.size();
  97. for (int i = 0; i < m_left_front_cloud.size(); ++i) {
  98. const Eigen::Matrix<T, 2, 1> point((T(m_left_front_cloud[i].x) - cx),
  99. (T(m_left_front_cloud[i].y) - cy));
  100. //减去经过车辆旋转计算的左前中心
  101. const Eigen::Matrix<T, 2, 1> tanslate_point = rotation_matrix * point - wheel_center_normal_left_front;
  102. //旋转
  103. const Eigen::Matrix<T, 2, 1> point_rotation = rotation_matrix_front * tanslate_point;
  104. interpolator.Evaluate(point_rotation(1, 0) * limited_wheel_length_img_ratio / limited_wheel_length_ratio* m_scale + rows / 2.0 + 0.5 + T(kPadding),
  105. point_rotation(0, 0) * limited_wheel_length_img_ratio * m_scale + cols / 2.0 + 0.5 + T(kPadding),
  106. &residual[i]);
  107. //residual[i] += theta_front*theta_front_weight;
  108. costs[0] += residual[i];
  109. deviation_calc_vector[0].push_back(residual[i]);
  110. }
  111. //右前轮
  112. int right_front_num = m_right_front_cloud.size();
  113. for (int i = 0; i < m_right_front_cloud.size(); ++i) {
  114. const Eigen::Matrix<T, 2, 1> point((T(m_right_front_cloud[i].x) - cx),
  115. (T(m_right_front_cloud[i].y) - cy));
  116. //减去经过车辆旋转计算的左前中心
  117. const Eigen::Matrix<T, 2, 1> tanslate_point = rotation_matrix * point - wheel_center_normal_right_front;
  118. //旋转
  119. const Eigen::Matrix<T, 2, 1> point_rotation = rotation_matrix_front * tanslate_point;
  120. interpolator.Evaluate(point_rotation(1, 0) * limited_wheel_length_img_ratio / limited_wheel_length_ratio* m_scale + rows / 2.0 + 0.5 + T(kPadding),
  121. point_rotation(0, 0) * limited_wheel_length_img_ratio * m_scale + cols / 2.0 + 0.5 + T(kPadding),
  122. &residual[left_front_num + i]);
  123. //residual[left_front_num + i] += theta_front*theta_front_weight;
  124. costs[1] += residual[left_front_num+i];
  125. deviation_calc_vector[1].push_back(residual[left_front_num+i]);
  126. }
  127. //左后轮
  128. int left_rear_num = m_left_rear_cloud.size();
  129. for (int i = 0; i < m_left_rear_cloud.size(); ++i) {
  130. const Eigen::Matrix<T, 2, 1> point((T(m_left_rear_cloud[i].x) - cx),
  131. (T(m_left_rear_cloud[i].y) - cy));
  132. //减去经过车辆旋转计算的左前中心
  133. const Eigen::Matrix<T, 2, 1> tanslate_point = rotation_matrix * point - wheel_center_normal_left_rear;
  134. interpolator.Evaluate(tanslate_point(1, 0) * limited_wheel_length_img_ratio / limited_wheel_length_ratio * m_scale + rows / 2.0 + 0.5 + T(kPadding),
  135. tanslate_point(0, 0) * limited_wheel_length_img_ratio * m_scale + cols / 2.0 + 0.5 + T(kPadding),
  136. &residual[left_front_num + right_front_num + i]);
  137. costs[2] += residual[left_front_num + right_front_num + i];
  138. deviation_calc_vector[2].push_back(residual[left_front_num + right_front_num + i]);
  139. }
  140. //右后轮
  141. int right_rear_num = m_right_rear_cloud.size();
  142. for (int i = 0; i < m_right_rear_cloud.size(); ++i) {
  143. const Eigen::Matrix<T, 2, 1> point((T(m_right_rear_cloud[i].x) - cx),
  144. (T(m_right_rear_cloud[i].y) - cy));
  145. //减去经过车辆旋转计算的左前中心
  146. const Eigen::Matrix<T, 2, 1> tanslate_point = rotation_matrix * point - wheel_center_normal_right_rear;
  147. interpolator.Evaluate(tanslate_point(1, 0) * limited_wheel_length_img_ratio / limited_wheel_length_ratio * m_scale + rows / 2.0 + 0.5 + T(kPadding),
  148. tanslate_point(0, 0) * limited_wheel_length_img_ratio * m_scale + cols / 2.0 + 0.5 + T(kPadding),
  149. &residual[left_front_num + right_front_num + left_rear_num + i]);
  150. costs[3] += residual[left_front_num + right_front_num + left_rear_num + i];
  151. deviation_calc_vector[3].push_back(residual[left_front_num + right_front_num + left_rear_num + i]);
  152. }
  153. // 前轮旋转loss
  154. residual[left_front_num + right_front_num + left_rear_num + right_rear_num] = theta_front*theta_front_weight;
  155. // 四轮方差loss
  156. residual[left_front_num + right_front_num + left_rear_num + right_rear_num+1] = T(0);
  157. //printf("--------------\n");
  158. T avg_costs[4] = {T(costs[0]/T(left_front_num)), T(costs[1]/T(right_front_num)), T(costs[2]/T(left_rear_num)), T(costs[3]/T(right_rear_num))};
  159. for (int i = 0; i < deviation_calc_vector.size(); ++i) {
  160. T t_deviation = T(0);
  161. for (int j = 0; j < deviation_calc_vector[i].size(); ++j) {
  162. t_deviation += deviation_weight * ceres::abs(deviation_calc_vector[i][j] - avg_costs[i]);
  163. }
  164. residual[left_front_num + right_front_num + left_rear_num + right_rear_num+1] += t_deviation;
  165. //printf("??? dev: %.6f\n", t_deviation);
  166. //costs[i] = t_deviation;
  167. //printf("dev: %.6f\n", costs[i]);
  168. }
  169. char buf[30];
  170. memset(buf, 0, 30);
  171. sprintf(buf, "%.12f", costs[0]);
  172. double t_costs_lf = std::stod(buf) / left_front_num;
  173. memset(buf, 0, 30);
  174. sprintf(buf, "%.12f", costs[1]);
  175. double t_costs_rf = std::stod(buf) / right_front_num;
  176. memset(buf, 0, 30);
  177. sprintf(buf, "%.12f", costs[2]);
  178. double t_costs_lr = std::stod(buf) / left_rear_num;
  179. memset(buf, 0, 30);
  180. sprintf(buf, "%.12f", costs[3]);
  181. double t_costs_rr = std::stod(buf) / right_rear_num;
  182. if(t_costs_lf >= 0.001 && t_costs_rf >= 0.001 && t_costs_lr >= 0.001 && t_costs_rr >= 0.001)
  183. {
  184. m_costs_lf=t_costs_lf;
  185. m_costs_rf=t_costs_rf;
  186. m_costs_lr=t_costs_lr;
  187. m_costs_rr=t_costs_rr;
  188. }
  189. // m_costs_lf = costs[0];
  190. //printf("%.6f %.6f %.6f %.6f\n",m_costs_lf, m_costs_rf, m_costs_lr, m_costs_rr);
  191. return true;
  192. }
  193. void get_costs(double &lf, double &rf, double &lr, double &rr)
  194. {
  195. lf = m_costs_lf;
  196. rf = m_costs_rf;
  197. lr = m_costs_lr;
  198. rr = m_costs_rr;
  199. }
  200. };
  201. bool detect_wheel_ceres::update_mat(int rows, int cols)
  202. {
  203. /////创建地图
  204. int L=std::min(rows,cols);
  205. cv::Mat map=cv::Mat::ones(L,L,CV_64F);
  206. //map=map*255;
  207. cv::Point center(L/2,L/2);
  208. float K=L*0.08;
  209. // 从中心开始向外画矩形
  210. // 内层高斯,外层二次函数
  211. for(int n=0;n<L;n+=2)
  212. {
  213. cv::Size size(n+2,n+2);
  214. cv::Rect rect(center-cv::Point(n/2,n/2),size);
  215. double x = n / double(L);
  216. double sigma = 0.05;
  217. double miu = 0.0;
  218. double scale = 0.02;
  219. double translation = 0.03;
  220. double gauss_value = (scale/(sqrt(2*M_PI)*sigma))*exp(-pow(x-miu, 2)/(2*sigma*sigma))+translation;
  221. double quadratic_value = std::max(2*x-0.25, 0.045);
  222. // LOG(INFO) << rect.tl().x<<", "<<rect.tl().y<<", "<<rect.br().x<<", "<<rect.br().y;
  223. // 对内外分别取色
  224. cv::rectangle(map,rect,std::max(gauss_value, quadratic_value));
  225. // if(n<K*2){
  226. // cv::rectangle(map,rect,1.0*float(K*2-n)/float(L));
  227. // }
  228. // else{
  229. // cv::rectangle(map,rect,float(n-K*2)/float(L));
  230. // }
  231. }
  232. // std::cout<<"map size: "<<map.size()<<std::endl;
  233. cv::resize(map,map,cv::Size(cols,rows));
  234. cv::GaussianBlur(map,m_map,cv::Size(7,7),3,3);
  235. return true;
  236. }
  237. detect_wheel_ceres::detect_wheel_ceres()
  238. {
  239. int cols=800;
  240. int rows=800;
  241. update_mat(rows, cols);
  242. }
  243. detect_wheel_ceres::~detect_wheel_ceres(){}
  244. bool detect_wheel_ceres::detect(std::vector<pcl::PointCloud<pcl::PointXYZ>> cloud_vec, Detect_result &detect_result, std::string &error_info)
  245. {
  246. error_info="";
  247. //清理点云
  248. m_left_front_cloud.clear();
  249. m_right_front_cloud.clear();
  250. m_left_rear_cloud.clear();
  251. m_right_rear_cloud.clear();
  252. //重新计算点云,按方位分割
  253. //第一步,计算整体中心,主轴方向
  254. pcl::PointCloud<pcl::PointXYZ> cloud_all;
  255. for(int i=0;i<cloud_vec.size();++i)
  256. {
  257. cloud_all+=cloud_vec[i];
  258. }
  259. // std::cout<<"cloud size: "<<cloud_all.size()<<std::endl;
  260. if(cloud_all.size()<20)
  261. return false;
  262. Eigen::Vector4f centroid;
  263. pcl::compute3DCentroid(cloud_all, centroid);
  264. double center_x=centroid[0];
  265. double center_y=centroid[1];
  266. //计算外接旋转矩形
  267. std::vector<cv::Point2f> points_cv;
  268. for(int i=0;i<cloud_all.size();++i)
  269. {
  270. points_cv.push_back(cv::Point2f(cloud_all[i].x,cloud_all[i].y));
  271. }
  272. cv::RotatedRect rotate_rect=cv::minAreaRect(points_cv);
  273. //计算旋转矩形与X轴的夹角
  274. cv::Point2f vec;
  275. cv::Point2f vertice[4];
  276. rotate_rect.points(vertice);
  277. float len1 = pow(vertice[0].x - vertice[1].x, 2.0) + pow(vertice[0].y - vertice[1].y, 2.0);
  278. float len2 = pow(vertice[1].x - vertice[2].x, 2.0) + pow(vertice[1].y - vertice[2].y, 2.0);
  279. // 寻找长边,倾角为长边与x轴夹角
  280. if (len1 > len2)
  281. {
  282. vec.x = vertice[0].x - vertice[1].x;
  283. vec.y = vertice[0].y - vertice[1].y;
  284. }
  285. else
  286. {
  287. vec.x = vertice[1].x - vertice[2].x;
  288. vec.y = vertice[1].y - vertice[2].y;
  289. }
  290. float angle_x = 180.0 / M_PI * acos(vec.x / sqrt(vec.x * vec.x + vec.y * vec.y));
  291. // printf("rect theta: %.3f\n",angle_x);
  292. //第二步, 将没分点云旋转回去,计算点云重心所在象限
  293. for(int i=0;i<cloud_vec.size();++i)
  294. {
  295. pcl::PointCloud<pcl::PointXYZ> cloud=cloud_vec[i];
  296. Eigen::Affine3f traslation = Eigen::Affine3f::Identity();//初始化变换矩阵为单位矩阵
  297. // 平移
  298. traslation.translation() << -center_x, -center_y, 0.0;
  299. pcl::PointCloud<pcl::PointXYZ> translate_cloud;
  300. pcl::transformPointCloud(cloud, translate_cloud, traslation);
  301. // 旋转; Z 轴上旋转angle_x 弧度,Y轴上旋转0弧度
  302. Eigen::Affine3f rotation = Eigen::Affine3f::Identity();
  303. rotation.rotate(Eigen::AngleAxisf(-angle_x*M_PI/180.0, Eigen::Vector3f::UnitZ()));
  304. pcl::PointCloud<pcl::PointXYZ> transformed_cloud;
  305. pcl::transformPointCloud(translate_cloud, transformed_cloud, rotation);
  306. //计算重心
  307. Eigen::Vector4f centroid;
  308. pcl::compute3DCentroid(transformed_cloud, centroid);
  309. double x=centroid[0];
  310. double y=centroid[1];
  311. //计算象限
  312. if(x>0&&y>0)
  313. {
  314. //第一象限
  315. m_left_front_cloud=cloud;
  316. }
  317. if(x>0 && y<0)
  318. {
  319. //第四象限
  320. m_right_front_cloud=cloud;
  321. }
  322. if(x<0 && y>0)
  323. {
  324. //第二象限
  325. m_left_rear_cloud=cloud;
  326. }
  327. if(x<0 && y<0)
  328. {
  329. //第三象限
  330. m_right_rear_cloud=cloud;
  331. }
  332. }
  333. // // 仅优化一次,不调整图像比例与角度初值。
  334. // detect_result.cx=center_x;
  335. // detect_result.cy=center_y;
  336. // detect_result.theta=-angle_x*M_PI/180.0;
  337. // Loss_result one_shot_loss_result;
  338. // return Solve(detect_result, one_shot_loss_result);
  339. // 多次优化,获取最佳优化结果
  340. detect_result.cx=center_x;
  341. detect_result.cy=center_y;
  342. // 已计算次数,初始传入值正常,之后solve函数会修改初始值,需要恢复角度单位为弧度
  343. int calc_count=0;
  344. // double final_theta = 0;
  345. // 误差结构体,保存左前、右前、左后、右后、整体平均误差
  346. Loss_result loss_result;
  347. // 平均误差值,用于获取最小整体平均误差
  348. double avg_loss = 100;
  349. // 定义图像列数,控制图像大小
  350. int map_cols = 800;
  351. // 优化后图像行数,用于保存优化后结果图像
  352. int optimized_map_rows = 200;
  353. // 优化结果
  354. bool solve_result = false;
  355. double total_solve_time = 0;
  356. bool stop_sign = false;
  357. // for (int j = 2; j < 4&&!stop_sign; j++)
  358. // {
  359. // // double input_vars[] = {detect_result.cx, detect_result.cy, detect_result.theta, detect_result.wheel_base, detect_result.width, detect_result.front_theta};
  360. // double map_rows = map_cols * 1.0 / j ;
  361. // update_mat(map_rows, map_cols);
  362. // !!! 将车轮比例添加到优化模型中
  363. Detect_result t_detect_result = detect_result;
  364. // 寻找最小loss值对应的初始旋转角
  365. for (int i = -3; i < 4&&!stop_sign; i+=6)
  366. {
  367. t_detect_result.theta = (-angle_x + i) * M_PI / 180.0;
  368. // printf("double x:%.3f y: %.3f wheel:%.3f width:%.3f theta : %.3f front theta: %.3f\n",input_vars[0],input_vars[1],input_vars[3],input_vars[4],input_vars[2],input_vars[5]);
  369. Loss_result t_loss_result;
  370. t_loss_result.total_avg_loss = 1000;
  371. //输出角度已变化,需恢复成弧度
  372. if(calc_count > 0)
  373. {
  374. // t_detect_result.theta *= (-M_PI) / 180.0;
  375. t_detect_result.front_theta *= (-M_PI) / 180.0;
  376. }
  377. auto t1=std::chrono::system_clock::now();
  378. std::string t_error_info;
  379. bool current_result = Solve(t_detect_result, t_loss_result, t_error_info);
  380. error_info = t_error_info;
  381. auto t2=std::chrono::system_clock::now();
  382. auto duration=t2-t1;
  383. static double second=0.0;
  384. second=std::chrono::duration_cast<std::chrono::milliseconds>(duration).count()/1000.0;
  385. total_solve_time+=second;
  386. // std::cout<<" time "<<second<<std::endl;
  387. // std::cout<<"current_result: "<<current_result<<std::endl;
  388. if(!current_result)
  389. continue;
  390. // LOG(INFO) << "avg loss, current theta: " << t_loss << ", " << input_vars[2];
  391. if (avg_loss > t_loss_result.total_avg_loss)
  392. {
  393. avg_loss = t_loss_result.total_avg_loss;
  394. // final_theta = -input_vars[2] * M_PI / 180.0;
  395. detect_result = t_detect_result;
  396. solve_result = current_result;
  397. loss_result = t_loss_result;
  398. // optimized_map_rows = map_rows;
  399. calc_count++;
  400. // // 新增,优化时间足够长则认为已找到正确结果
  401. // if(second > 0.02)
  402. // {
  403. // stop_sign = true;
  404. // break;
  405. // }
  406. }
  407. }
  408. // }
  409. // std::cout<<"solve time "<<total_solve_time<<std::endl;
  410. // LOG(INFO) << "final avg cost for four wheels: "<<whole_loss[0]<<" "<<whole_loss[1]<<" "<<whole_loss[2]<<" "<<whole_loss[3]<<" ";
  411. // if(solve_result)
  412. // printf("final avg cost for four wheels: %7.6f %7.6f %7.6f %7.6f---%7.6f\n", loss_result.lf_loss, loss_result.rf_loss, loss_result.lb_loss, loss_result.rb_loss, loss_result.total_avg_loss);
  413. // // 生成并保存图片
  414. // update_mat(optimized_map_rows, map_cols);
  415. // Detect_result t_detect_result = detect_result;
  416. // t_detect_result.theta *= (-M_PI) / 180.0;
  417. // t_detect_result.front_theta *= (-M_PI) / 180.0;
  418. // Loss_result t_loss_result;
  419. // // LOG(INFO) <<"going to show img ";
  420. // Solve(detect_result, t_loss_result, true);
  421. // printf(" x:%.3f y: %.3f wheel:%.3f width:%.3f theta : %.3f front theta: %.3f\n",
  422. // detect_result.cx,detect_result.cy,detect_result.wheel_base,detect_result.width,detect_result.theta,detect_result.front_theta);
  423. if(solve_result)
  424. error_info="";
  425. return solve_result;
  426. }
  427. // 根据测量结果,生成四轮各中心十字星点云
  428. void detect_wheel_ceres::transform_src(Detect_result detect_result)
  429. {
  430. //整车旋转
  431. Eigen::Rotation2D<double> rotation(-detect_result.theta);
  432. Eigen::Matrix<double, 2, 2> rotation_matrix = rotation.toRotationMatrix();
  433. //左前偏移
  434. Eigen::Matrix<double, 2, 1> wheel_center_normal_left_front(detect_result.wheel_base / 2.0, detect_result.width / 2.0);
  435. //右前偏移
  436. Eigen::Matrix<double, 2, 1> wheel_center_normal_right_front(detect_result.wheel_base / 2.0, -detect_result.width / 2.0);
  437. //左后偏移
  438. Eigen::Matrix<double, 2, 1> wheel_center_normal_left_rear(-detect_result.wheel_base / 2.0, detect_result.width / 2.0);
  439. //右后偏移
  440. Eigen::Matrix<double, 2, 1> wheel_center_normal_right_rear(-detect_result.wheel_base / 2.0, -detect_result.width / 2.0);
  441. Eigen::Matrix<double, 2, 1> translate_car(detect_result.cx,detect_result.cy);
  442. const Eigen::Matrix<double, 2, 1> lt_center = rotation_matrix * wheel_center_normal_left_front+translate_car;
  443. const Eigen::Matrix<double, 2, 1> rt_center = rotation_matrix * wheel_center_normal_right_front+translate_car;
  444. const Eigen::Matrix<double, 2, 1> lb_center = rotation_matrix * wheel_center_normal_left_rear+translate_car;
  445. const Eigen::Matrix<double, 2, 1> rb_center = rotation_matrix * wheel_center_normal_right_rear+translate_car;
  446. create_mark(lt_center(0,0),lt_center(1,0),-detect_result.theta-detect_result.front_theta,m_left_front_cloud_out);
  447. create_mark(rt_center(0,0),rt_center(1,0),-detect_result.theta-detect_result.front_theta,m_right_front_cloud_out);
  448. create_mark(lb_center(0,0),lb_center(1,0),-detect_result.theta,m_left_rear_cloud_out);
  449. create_mark(rb_center(0,0),rb_center(1,0),-detect_result.theta,m_right_rear_cloud_out);
  450. }
  451. // 创建车轮中心十字星点云
  452. void detect_wheel_ceres::create_mark(double x,double y,double theta,pcl::PointCloud<pcl::PointXYZ>& cloud_out)
  453. {
  454. cloud_out.clear();
  455. pcl::PointCloud<pcl::PointXYZ> cloud;
  456. //std::cout<<"1111111111"<<std::endl;
  457. int width=30;
  458. int height=10;
  459. double step=0.015;
  460. for(int i=0;i<width;++i)
  461. {
  462. pcl::PointXYZ point((i-width/2)*step,0,0);
  463. cloud.push_back(point);
  464. }
  465. for(int i=0;i<height;++i)
  466. {
  467. pcl::PointXYZ point(0,(i-height/2)*step,0);
  468. cloud.push_back(point);
  469. }
  470. //std::cout<<"22222222222"<<std::endl;
  471. //整车旋转
  472. Eigen::Rotation2D<double> rotation(theta);
  473. Eigen::Matrix<double, 2, 2> rotation_matrix = rotation.toRotationMatrix();
  474. for(int i=0;i<cloud.size();++i) {
  475. const Eigen::Matrix<double, 2, 1> point((double(cloud[i].x)),
  476. (double(cloud[i].y)));
  477. //减去经过车辆旋转计算的左前中心
  478. const Eigen::Matrix<double, 2, 1> tanslate_point = rotation_matrix * point;
  479. pcl::PointXYZ point_out(tanslate_point(0,0)+x,tanslate_point(1,0)+y,0);
  480. cloud_out.push_back(point_out);
  481. }
  482. }
  483. bool detect_wheel_ceres::Solve(Detect_result &detect_result, double &avg_loss)
  484. {
  485. if(m_left_front_cloud.size()<=1 || m_right_front_cloud.size()<=1 || m_left_rear_cloud.size()<=1 || m_right_rear_cloud.size()<=1)
  486. {
  487. return false;
  488. }
  489. double SCALE=300.0;
  490. double cx=detect_result.cx;
  491. double cy=detect_result.cy;
  492. double init_theta=detect_result.theta;
  493. double init_wheel_base=2.7;
  494. double init_width=1.55;
  495. double init_theta_front=0*M_PI/180.0;
  496. double variable[] = {cx,cy,init_theta,init_wheel_base,init_width,init_theta_front};
  497. // 第二部分:构建寻优问题
  498. ceres::Problem problem;
  499. //使用自动求导,将之前的代价函数结构体传入,第一个1是输出维度,即残差的维度,第二个1是输入维度,即待寻优参数x的维度。
  500. ceres::CostFunction* cost_function =new
  501. ceres::AutoDiffCostFunction<CostFunctor, ceres::DYNAMIC, 6>(
  502. new CostFunctor(m_left_front_cloud,m_right_front_cloud,m_left_rear_cloud,m_right_rear_cloud,m_map,SCALE),
  503. m_left_front_cloud.size()+m_right_front_cloud.size()+m_left_rear_cloud.size()+m_right_rear_cloud.size());
  504. problem.AddResidualBlock(cost_function, NULL, variable); //向问题中添加误差项,本问题比较简单,添加一个就行。
  505. //第三部分: 配置并运行求解器
  506. ceres::Solver::Options options;
  507. options.use_nonmonotonic_steps=false;
  508. options.linear_solver_type = ceres::DENSE_QR; //配置增量方程的解法
  509. options.max_num_iterations=60;
  510. options.num_threads=1;
  511. options.minimizer_progress_to_stdout = false;//输出到cout
  512. ceres::Solver::Summary summary;//优化信息
  513. ceres::Solve(options, &problem, &summary);//求解!!!
  514. // std::cout << summary.BriefReport() << "\n";//输出优化的简要信息
  515. /*printf("x:%.3f,y:%.3f,theta:%.3f\nlength:%.3f,width:%.3f\ntheta_front:%.3f\n",
  516. x[0],x[1],x[2]*180.0/M_PI,x[3],x[4],x[5]*180.0/M_PI);*/
  517. double loss=summary.final_cost/(m_left_front_cloud.size()+m_right_front_cloud.size()+m_left_rear_cloud.size()+m_right_rear_cloud.size());
  518. detect_result.cx=variable[0];
  519. detect_result.cy=variable[1];
  520. detect_result.theta=(-variable[2])*180.0/M_PI;
  521. detect_result.wheel_base=variable[3];
  522. detect_result.width=variable[4];
  523. detect_result.front_theta=-(variable[5]*180.0/M_PI);
  524. if(detect_result.theta>180.0)
  525. detect_result.theta=detect_result.theta-180.0;
  526. if(detect_result.theta<0)
  527. detect_result.theta+=180.0;
  528. //检验
  529. if(loss>0.01)
  530. return false;
  531. if (detect_result.width < 1.350 || detect_result.width > 2.000 || detect_result.wheel_base > 3.000 || detect_result.wheel_base < 2.200)
  532. {
  533. return false;
  534. }
  535. detect_result.width+=0.15;//车宽+10cm
  536. // printf(" x:%.3f y: %.3f wheel:%.3f width:%.3f theta : %.3f front theta: %.3f\n",x,y,wheel_base,width,theta,front_theta);
  537. // //added by yct
  538. avg_loss = loss; // 将loss传出
  539. Detect_result t_detect_result = detect_result;
  540. t_detect_result.theta *= -M_PI/180.0;
  541. t_detect_result.front_theta *= -M_PI/180.0;
  542. transform_src(t_detect_result);
  543. return true;
  544. }
  545. bool detect_wheel_ceres::Solve(Detect_result &detect_result, Loss_result &loss_result, std::string &error_info, bool save_img)
  546. {
  547. double SCALE=300.0;
  548. double cx=detect_result.cx;
  549. double cy=detect_result.cy;
  550. double init_theta=detect_result.theta;
  551. double init_wheel_base=2.7;
  552. double init_width=1.55;
  553. double init_theta_front=0*M_PI/180.0;
  554. // 车轮长图比, 比例取值1/3-5/3, 当前变量无限制
  555. // double init_wheel_length_img_ratio = 0.1;
  556. // 车轮宽长比, 默认长为1,比例取值范围0.2-0.8, 当前变量范围无限制
  557. double init_wheel_width_length_ratio = -0.5;
  558. double variable[] = {cx, cy, init_theta, init_wheel_base, init_width, init_theta_front, init_wheel_width_length_ratio};
  559. // printf("solve x:%.3f y: %.3f wheel:%.3f width:%.3f theta : %.3f front theta: %.3f\n", variable[0], variable[1], variable[3], variable[4], variable[2], variable[5]);
  560. // 第二部分:构建寻优问题
  561. ceres::Problem problem;
  562. CostFunctor *cost_func = new CostFunctor(m_left_front_cloud,m_right_front_cloud,m_left_rear_cloud,m_right_rear_cloud,m_map,SCALE);
  563. //使用自动求导,将之前的代价函数结构体传入,第一个1是输出维度,即残差的维度,第二个1是输入维度,即待寻优参数x的维度。
  564. ceres::CostFunction* cost_function =new
  565. ceres::AutoDiffCostFunction<CostFunctor, ceres::DYNAMIC, 7>(
  566. cost_func,
  567. m_left_front_cloud.size()+m_right_front_cloud.size()+m_left_rear_cloud.size()+m_right_rear_cloud.size()+2);
  568. problem.AddResidualBlock(cost_function, NULL, variable); //向问题中添加误差项,本问题比较简单,添加一个就行。
  569. //第三部分: 配置并运行求解器
  570. ceres::Solver::Options options;
  571. options.use_nonmonotonic_steps=false;
  572. options.linear_solver_type = ceres::DENSE_QR; //配置增量方程的解法
  573. options.logging_type = ceres::LoggingType::SILENT;
  574. options.max_num_iterations=60;
  575. options.num_threads=3;
  576. options.minimizer_progress_to_stdout = false;//输出到cout
  577. ceres::Solver::Summary summary;//优化信息
  578. ceres::Solve(options, &problem, &summary);//求解!!!
  579. // if(!summary.IsSolutionUsable())
  580. // {
  581. // error_info.append("检测失败\t");
  582. // return false;
  583. // }
  584. // std::cout << summary.BriefReport() << "\n";//输出优化的简要信息
  585. // debug_img(detect_result, loss_result, true);
  586. double loss=summary.final_cost/(m_left_front_cloud.size()+m_right_front_cloud.size()+m_left_rear_cloud.size()+m_right_rear_cloud.size());
  587. detect_result.cx=variable[0];
  588. detect_result.cy=variable[1];
  589. detect_result.theta=(-variable[2])*180.0/M_PI;
  590. detect_result.wheel_base=variable[3];
  591. detect_result.width=variable[4];
  592. detect_result.front_theta=-(variable[5]*180.0/M_PI);
  593. if(detect_result.theta>180.0)
  594. detect_result.theta=detect_result.theta-180.0;
  595. if(detect_result.theta<0)
  596. detect_result.theta+=180.0;
  597. //检验
  598. // printf("loss: %.5f\n", loss);
  599. // printf("middle x:%.3f y: %.3f wheel:%.3f width:%.3f theta : %.3f front theta: %.3f\n", variable[0], variable[1], variable[3], variable[4], variable[2], variable[5]);
  600. if(loss>0.0115)
  601. {
  602. error_info.append("总loss过大 ").append(std::to_string(loss)).append("\t");
  603. // LOG(WARNING) <<"总loss过大 "<<loss;
  604. return false;
  605. }
  606. if (detect_result.width < 1.25 || detect_result.width > 2.000 || detect_result.wheel_base > 3.15 || detect_result.wheel_base < 2.200)
  607. {
  608. error_info.append(std::string("宽度(1.25, 2.00) 轴距(2.20, 3.15): ").append(std::to_string(detect_result.width).append(", ").append(std::to_string(detect_result.wheel_base)).append("\t")));
  609. // LOG(WARNING) <<"宽度(1.35, 2.00) 轴距(2.20, 3.30): "<<detect_result.width<<", "<<detect_result.wheel_base;
  610. return false;
  611. }
  612. detect_result.width += 0.22; //车宽一边+11cm
  613. // printf(" x:%.3f y: %.3f wheel:%.3f width:%.3f theta : %.3f front theta: %.3f\n",
  614. // detect_result.cx,detect_result.cy,detect_result.wheel_base,detect_result.width,detect_result.theta,detect_result.front_theta);
  615. // //added by yct
  616. if(detect_result.theta > 120 || detect_result.theta < 60)
  617. {
  618. error_info.append("总角度错误 ").append(std::to_string(detect_result.theta)).append("\t");
  619. // LOG(WARNING) <<"总角度错误 "<<detect_result.theta;
  620. return false;
  621. }
  622. if(fabs(detect_result.front_theta)>35)
  623. {
  624. error_info.append("前轮角度错误 ").append(std::to_string(detect_result.front_theta)).append("\t");
  625. // LOG(WARNING) <<"前轮角度错误 "<<detect_result.front_theta;
  626. return false;
  627. }
  628. // 将loss传出
  629. if(cost_func!=nullptr)
  630. {
  631. double costs_lf, costs_rf, costs_lr, costs_rr;
  632. cost_func->get_costs(costs_lf, costs_rf, costs_lr, costs_rr);
  633. // std::cout << "111"<< std::endl;
  634. loss_result.lf_loss = costs_lf;
  635. loss_result.rf_loss = costs_rf;
  636. loss_result.lb_loss = costs_lr;
  637. loss_result.rb_loss = costs_rr;
  638. loss_result.total_avg_loss = loss;
  639. // std::cout << "222"<< std::endl;
  640. if (costs_lf == costs_rf && costs_lf == costs_lf && costs_lf == costs_rr)// (costs_lf <= 1e-6 && costs_rf <= 1e-6 && costs_lr <= 1e-6 && costs_rr <= 1e-6)
  641. {
  642. return false;
  643. loss_result.lf_loss = loss;
  644. loss_result.rf_loss = loss;
  645. loss_result.lb_loss = loss;
  646. loss_result.rb_loss = loss;
  647. }
  648. // 判断每个轮子平均loss是否满足条件
  649. double single_wheel_loss_threshold = 0.135;
  650. if(loss_result.lf_loss > single_wheel_loss_threshold || loss_result.rf_loss > single_wheel_loss_threshold || loss_result.lb_loss > single_wheel_loss_threshold || loss_result.rb_loss > single_wheel_loss_threshold)
  651. {
  652. error_info.append("四轮loss过大 ").append(std::to_string(loss_result.lf_loss)).append(", ")
  653. .append(std::to_string(loss_result.rf_loss)).append(", ")
  654. .append(std::to_string(loss_result.lb_loss)).append(", ")
  655. .append(std::to_string(loss_result.rb_loss)).append("\t");
  656. // LOG(WARNING)<<error_info;
  657. // LOG(WARNING) <<"四轮loss过大"<<loss_result.lf_loss<<", "<<loss_result.rf_loss<<", "<<loss_result.lb_loss<<", "<<loss_result.rb_loss;
  658. return false;
  659. }
  660. // std::cout<<"save img: "<<save_img<<std::endl;
  661. // debug_img(detect_result, loss_result, save_img);
  662. }
  663. else
  664. {
  665. return false;
  666. }
  667. Detect_result t_detect_result = detect_result;
  668. t_detect_result.theta *= -M_PI/180.0;
  669. t_detect_result.front_theta *= -M_PI/180.0;
  670. t_detect_result.width -= 0.22;
  671. transform_src(t_detect_result);
  672. return true;
  673. }
  674. // 显示/保存图片供调试用
  675. void detect_wheel_ceres::debug_img(Detect_result detect_result, Loss_result loss_result, bool save_img, std::string out_img_path)
  676. {
  677. double SCALE=300.0;
  678. cv::Mat lf = m_map.clone();
  679. cv::Mat rf = m_map.clone();
  680. cv::Mat lb = m_map.clone();
  681. cv::Mat rb = m_map.clone();
  682. //整车旋转
  683. Eigen::Rotation2D<double> rotation(detect_result.theta);
  684. Eigen::Matrix<double, 2, 2> rotation_matrix = rotation.toRotationMatrix();
  685. //左前偏移
  686. Eigen::Matrix<double, 2, 1> wheel_center_normal_left_front(detect_result.wheel_base / 2.0, (detect_result.width - 0.15) / 2.0);
  687. //右前偏移
  688. Eigen::Matrix<double, 2, 1> wheel_center_normal_right_front(detect_result.wheel_base / 2.0, (-detect_result.width + 0.15) / 2.0);
  689. //左后偏移
  690. Eigen::Matrix<double, 2, 1> wheel_center_normal_left_rear(-detect_result.wheel_base / 2.0, (detect_result.width - 0.15) / 2.0);
  691. //右后偏移
  692. Eigen::Matrix<double, 2, 1> wheel_center_normal_right_rear(-detect_result.wheel_base / 2.0, (-detect_result.width + 0.15) / 2.0);
  693. //前轮旋转
  694. Eigen::Rotation2D<double> rotation_front(detect_result.front_theta);
  695. Eigen::Matrix<double, 2, 2> rotation_matrix_front = rotation_front.toRotationMatrix();
  696. // 左前轮
  697. for (size_t i = 0; i < m_left_front_cloud.size(); i++)
  698. {
  699. Eigen::Matrix<double, 2, 1> point((double(m_left_front_cloud[i].x) - detect_result.cx),
  700. (double(m_left_front_cloud[i].y) - detect_result.cy));
  701. //减去经过车辆旋转计算的左前中心
  702. Eigen::Matrix<double, 2, 1> tanslate_point = rotation_matrix * point - wheel_center_normal_left_front;
  703. //旋转
  704. Eigen::Matrix<double, 2, 1> point_rotation = rotation_matrix_front * tanslate_point;
  705. int r = (int)(point_rotation(1, 0) * SCALE + m_map.rows / 2.0 + 0.5);
  706. int c = (int)(point_rotation(0, 0) * SCALE + m_map.cols / 2.0 + 0.5);
  707. cv::circle(lf, cv::Point(c, r), 1, cv::Scalar(150));
  708. // std::cout<<"map r,c "<<m_map.rows<<", "<<m_map.cols<<std::endl;
  709. // std::cout<<"r,c "<<r<<", "<<c<<std::endl;
  710. // lf.at<uchar>(r, c) = 150;
  711. }
  712. //右前轮
  713. for (int i = 0; i < m_right_front_cloud.size(); ++i)
  714. {
  715. Eigen::Matrix<double, 2, 1> point((double(m_right_front_cloud[i].x) - detect_result.cx),
  716. (double(m_right_front_cloud[i].y) - detect_result.cy));
  717. //减去经过车辆旋转计算的左前中心
  718. Eigen::Matrix<double, 2, 1> tanslate_point = rotation_matrix * point - wheel_center_normal_right_front;
  719. //旋转
  720. Eigen::Matrix<double, 2, 1> point_rotation = rotation_matrix_front * tanslate_point;
  721. int r = (int)(point_rotation(1, 0) * SCALE + m_map.rows / 2.0 + 0.5);
  722. int c = (int)(point_rotation(0, 0) * SCALE + m_map.cols / 2.0 + 0.5);
  723. cv::circle(rf, cv::Point(c, r), 1, cv::Scalar(150));
  724. }
  725. //左后轮
  726. for (int i = 0; i < m_left_rear_cloud.size(); ++i)
  727. {
  728. Eigen::Matrix<double, 2, 1> point((double(m_left_rear_cloud[i].x) - detect_result.cx),
  729. (double(m_left_rear_cloud[i].y) - detect_result.cy));
  730. //减去经过车辆旋转计算的左前中心
  731. Eigen::Matrix<double, 2, 1> tanslate_point = rotation_matrix * point - wheel_center_normal_left_rear;
  732. int r = (int)(tanslate_point(1, 0) * SCALE + m_map.rows / 2.0 + 0.5);
  733. int c = (int)(tanslate_point(0, 0) * SCALE + m_map.cols / 2.0 + 0.5);
  734. cv::circle(lb, cv::Point(c, r), 1, cv::Scalar(150));
  735. }
  736. //右后轮
  737. for (int i = 0; i < m_right_rear_cloud.size(); ++i)
  738. {
  739. Eigen::Matrix<double, 2, 1> point((double(m_right_rear_cloud[i].x) - detect_result.cx),
  740. (double(m_right_rear_cloud[i].y) - detect_result.cy));
  741. //减去经过车辆旋转计算的左前中心
  742. Eigen::Matrix<double, 2, 1> tanslate_point = rotation_matrix * point - wheel_center_normal_right_rear;
  743. int r = (int)(tanslate_point(1, 0) * SCALE + m_map.rows / 2.0 + 0.5);
  744. int c = (int)(tanslate_point(0, 0) * SCALE + m_map.cols / 2.0 + 0.5);
  745. cv::circle(rb, cv::Point(c, r), 1, cv::Scalar(150));
  746. }
  747. // cv::Mat rot90;// = (cv::Mat_<double>(2, 3)<<cos(90), -sin(90), 0, sin(90), cos(90), 0);
  748. // rot90 = cv::getRotationMatrix2D(cv::Point2f(lf.cols/2, lf.rows/2), 90, 1);
  749. // // std::cout<<rot90<<std::endl;
  750. // cv::warpAffine(lf, lf, rot90, cv::Size(lf.cols, lf.rows));
  751. // cv::flip()
  752. // cv::namedWindow("left front", cv::WINDOW_FREERATIO);
  753. // cv::namedWindow("right front", cv::WINDOW_FREERATIO);
  754. // cv::namedWindow("left back", cv::WINDOW_FREERATIO);
  755. // cv::namedWindow("right back", cv::WINDOW_FREERATIO);
  756. cv::flip(lf, lf, -1);
  757. cv::flip(rf, rf, -1);
  758. cv::flip(lb, lb, -1);
  759. cv::flip(rb, rb, -1);
  760. cv::Mat lft = lf.t(), rft = rf.t(), lbt = lb.t(), rbt = rb.t();
  761. // cv::imshow("left front", lf.t());
  762. // cv::imshow("right front", rf.t());
  763. // cv::imshow("left back", lb.t());
  764. // cv::imshow("right back", rb.t());
  765. // 写入各轮平均误差
  766. cv::putText(lft, (boost::format("lf %.6f") % (loss_result.lf_loss)).str().c_str(),
  767. cv::Point(lft.cols / 6, lft.rows / 4),
  768. cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(0, 0, 0));
  769. cv::putText(rft, (boost::format("rf %.6f") % (loss_result.rf_loss)).str().c_str(),
  770. cv::Point(rft.cols / 6, rft.rows / 4),
  771. cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(0, 0, 0));
  772. cv::putText(lbt, (boost::format("lb %.6f") % (loss_result.lb_loss)).str().c_str(),
  773. cv::Point(lbt.cols / 6, lbt.rows / 4),
  774. cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(0, 0, 0));
  775. cv::putText(rbt, (boost::format("rb %.6f") % (loss_result.rb_loss)).str().c_str(),
  776. cv::Point(rbt.cols / 6, rbt.rows / 4),
  777. cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(0, 0, 0));
  778. cv::Mat total_img = cv::Mat::zeros(m_map.cols * 2, m_map.rows * 2, m_map.type());
  779. lft.copyTo(total_img(cv::Rect(0, 0, m_map.rows, m_map.cols)));
  780. rft.copyTo(total_img(cv::Rect(m_map.rows, 0, m_map.rows, m_map.cols)));
  781. lbt.copyTo(total_img(cv::Rect(0, m_map.cols, m_map.rows, m_map.cols)));
  782. rbt.copyTo(total_img(cv::Rect(m_map.rows, m_map.cols, m_map.rows, m_map.cols)));
  783. // cv::namedWindow("total img", CV_WINDOW_FREERATIO);
  784. // cv::imshow("total img", total_img);
  785. // cv::waitKey(20);
  786. if(save_img)
  787. {
  788. cv::Mat cvted_img = cv::Mat::zeros(m_map.cols * 2, m_map.rows * 2, CV_8U);
  789. int total_milli = (std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now())).time_since_epoch().count();
  790. std::string img_filename="";
  791. if(out_img_path.empty()){
  792. img_filename = std::string("/home/youchen/Documents/measure/MainStructure/elecfence_ws/img/img").append(std::to_string(total_milli)).append(".jpg");
  793. }
  794. else{
  795. img_filename = out_img_path;
  796. }
  797. LOG(INFO) << "write to " << img_filename.c_str();
  798. // cv::cvtColor(total_img*255, cvted_img, CV_8U);
  799. cv::convertScaleAbs(total_img * 255, cvted_img);
  800. cv::imwrite(img_filename, cvted_img);
  801. }
  802. }