Ceres入门---Ceres优化三维轨迹

    xiaoxiao2022-07-02  137

    文章目录

    1.前言2.场景介绍3.最小二乘理论推导3.1方程建立3.2计算雅可比方程 4.代码5.结果与结论

    1.前言

    本人一直致力于定位研究,接触各种定位算法,最终需要输出比较完美的定位轨迹。一直以来滤波算法被运用于轨迹融合,但是研究表明,基于最小二乘的非线性优化方案,有更好的表现。本文基于谷歌开发的Ceres Solver非线性最小二乘优化包,进行轨迹优化。

    2.场景介绍

    为了演示的方便,我们抛弃了高度,做了一个二维轨迹仿真。在一条10米的路上,一个机器人从(-5,0,0)出发,沿着(1,0,0)以0.1m/s的速度走向(5,0,0),我们能得到的数据是每秒的速度和位置,但是都是有误差的。

    3.最小二乘理论推导

    3.1方程建立

    这时就需要建立最小二乘误差方程。如下: min ⁡ P i , P j ∑ i N − 1 ∣ ∣ V i ∗ Δ t − ( P i − P j ) ∣ ∣ {\min_{P_i,P_j} \sum_i^{N-1}||V_i*\Delta t-(P_i-P_{j})||} Pi,PjminiN1ViΔt(PiPj) 其中 Δ t {\Delta t} Δt P i {P_i} Pi P j {P_j} Pj的时间。 P i {P_i} Pi i {i} i时刻位置, P j {P_j} Pj j {j} j时刻的位置。

    3.2计算雅可比方程

    上式也是残差方程: r e s i d u a l = V i ∗ Δ t − ( P i − P j ) {residual=V_i*\Delta t-(P_i-P_{j})} residual=ViΔt(PiPj) 残差的维度为三维。 由于我们将位置当作优化参数,所以参数有两个,既有两参数块,一个为 P i {P_i} Pi,一个为 P j {P_j} Pj。 所以有 j a c o b i a n s [ 0 ] = ∂ r e s i d u a l ∂ P i = [ − 1 0 0 0 − 1 0 0 0 − 1 ] {jacobians[0]=\frac{\partial residual}{\partial P_i}=\begin{bmatrix} -1 & 0& 0 \\ 0 & -1 &0 \\ 0 &0 & -1 \end{bmatrix}} jacobians[0]=Piresidual=100010001 维度为: 3 ∗ 3 {3*3} 33 j a c o b i a n s [ 1 ] = ∂ r e s i d u a l ∂ P j = [ 1 0 0 0 1 0 0 0 1 ] {jacobians[1]=\frac{\partial residual}{\partial P_j}=\begin{bmatrix} 1 & 0& 0 \\ 0 & 1 &0 \\ 0 &0 & 1 \end{bmatrix}} jacobians[1]=Pjresidual=100010001 维度为: 3 ∗ 3 {3*3} 33

    4.代码

    #include <iostream> #include <opencv2/core/core.hpp> #include <ceres/ceres.h> #include <chrono> #include <eigen3/Eigen/Dense> #include "ros/ros.h" #include <geometry_msgs/Vector3Stamped.h> #include<nav_msgs/Path.h> using namespace std; #define C_PI (double)3.141592653589793 nav_msgs::Path path; nav_msgs::Path path1; ros::Publisher path_pub; ros::Publisher path_pub_n; int N=101; // 代价函数的计算模型 class DeltaDis:public ceres::SizedCostFunction<3,3,3> { public: DeltaDis()=delete; DeltaDis(Eigen::Vector3d deltadis) { Deltadis=deltadis; } virtual ~DeltaDis(){} virtual bool Evaluate(double const *const *parameters, double *residuals, double **jacobians) const { Eigen::Vector3d Pi(parameters[0][0], parameters[0][1], parameters[0][2]); Eigen::Vector3d Pj(parameters[1][0], parameters[1][1], parameters[1][2]); Eigen::Map<Eigen::Matrix<double, 3, 1>> residual(residuals); residual=Deltadis-(Pi-Pj); // cout<<"residual:"<<residual; if(jacobians) { if (jacobians[0]) { Eigen::Map<Eigen::Matrix<double, 3, 3, Eigen::RowMajor>> jacobian_pose_i(jacobians[0]); jacobian_pose_i.setZero(); jacobian_pose_i(0,0)=-1.0; jacobian_pose_i(2,2)=-1; jacobian_pose_i(1,1)=-1; } if (jacobians[1]) { Eigen::Map<Eigen::Matrix<double, 3, 3, Eigen::RowMajor>> jacobian_pose_j(jacobians[1]); jacobian_pose_j.setZero(); jacobian_pose_j(0,0)=1; jacobian_pose_j(2,2)=1; jacobian_pose_j(1,1)=1; } } } private: Eigen::Vector3d Deltadis; }; // Returns true if the solve was successful. bool SolveOptimizationProblem(ceres::Problem* problem) { CHECK(problem != NULL); ceres::Solver::Options options; options.max_num_iterations = 100; options.linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY; ceres::Solver::Summary summary; ceres::Solve(options, problem, &summary); std::cout << summary.FullReport() << '\n'; return summary.IsSolutionUsable(); } enum SIZE_PARAMETERIZATION { SIZE_POSE = 3, }; int main ( int argc, char** argv ) { ros::init(argc, argv, "mp_node"); ros::NodeHandle n; path_pub=n.advertise<nav_msgs::Path>("trajectory1",1,true); path_pub_n=n.advertise<nav_msgs::Path>("trajectory2",1,true); double Pi[12][SIZE_POSE]; double Pj[12][SIZE_POSE]; path.header.frame_id="map"; path1.header.frame_id="map"; Eigen::Vector3d Pose[N]; Eigen::Vector3d T; T.setZero(); double p_sigma=0.1; // 噪声Sigma值 cv::RNG rng; Eigen::Vector3d Vs[N]; // OpenCV随机数产生器 for(int i=0;i<N;i++) { path.header.stamp=ros::Time::now(); // T((i/25)%2)=T((i/25)%2)+pow(-1,i/50)*1; T.setIdentity(); T=T/10; double a=rng.gaussian (p_sigma); Vs[i]=T+Eigen::Vector3d(a/10,a/10,0);; Pose[i]=T*i; Pose[i](0)=Pose[i](0)-5; Pose[i]=Pose[i]+Eigen::Vector3d(a*2,a*5-a*a,0); geometry_msgs::PoseStamped thie_Pose; thie_Pose.pose.position.x=Pose[i](0); thie_Pose.pose.position.y=Pose[i](1); thie_Pose.pose.position.z=Pose[i](2); thie_Pose.pose.orientation.x=1; thie_Pose.pose.orientation.y=1; thie_Pose.pose.orientation.z=1; thie_Pose.pose.orientation.w=1; thie_Pose.header.stamp=ros::Time::now(); thie_Pose.header.frame_id="path"; path.poses.push_back(thie_Pose); path_pub.publish(path); } double pose_s[N][3]; ceres::Problem problem; for(int i=1;i<N;i++) { pose_s[i][0]=Pose[i](0); pose_s[i][1]=Pose[i](1); pose_s[i][2]=Pose[i](2); } for(int i=1;i<N;i++) { Eigen::Vector3d deldis=Vs[i-1]; problem.AddParameterBlock(pose_s[i-1],SIZE_POSE); problem.AddParameterBlock(pose_s[i],SIZE_POSE); DeltaDis* deldisf=new DeltaDis(deldis); problem.AddResidualBlock(deldisf, nullptr, pose_s[i-1],pose_s[i]); } SolveOptimizationProblem(&problem); for(int i=0;i<N;i++) { geometry_msgs::PoseStamped this_Pose; this_Pose.pose.position.x=pose_s[i][0]; this_Pose.pose.position.y=pose_s[i][1]; this_Pose.pose.position.z=pose_s[i][2]; this_Pose.pose.orientation.x=1; this_Pose.pose.orientation.y=1; this_Pose.pose.orientation.z=1; this_Pose.pose.orientation.w=1; this_Pose.header.stamp=ros::Time::now(); this_Pose.header.frame_id="n_path"; path1.poses.push_back(this_Pose); path_pub_n.publish(path1); } int a; cin>>a; return 0; }

    5.结果与结论

    绿色的为仿真轨迹,红色的为优化轨迹。可以看到优化结果相当的好。

    最新回复(0)