// // Created by zx on 22-12-1. // #include "loaded_mpc.h" #include #include #include size_t N = 15; //优化考虑后面多少步 size_t delay = 2; // 预判发送到执行的延迟,即几个周期的时间 size_t down_count = 3;//下发前多少步 size_t nx = 0; size_t ny = nx + N; size_t nth = ny + N; size_t nv = nth + N; size_t ndlt = nv + N; size_t nobs = ndlt + N; class FG_eval_half_agv { public: // Fitted polynomial coefficients Eigen::VectorXd m_coeffs; //曲线方程 Eigen::VectorXd m_statu; //当前状态 Eigen::VectorXd m_condition; //搜索条件参数 bool directY_; FG_eval_half_agv(Eigen::VectorXd coeffs, Eigen::VectorXd statu, Eigen::VectorXd condition,bool directY) { m_coeffs = coeffs; m_statu = statu; m_condition = condition; directY_=directY; } typedef CPPAD_TESTVECTOR(CppAD::AD) ADvector; void operator()(ADvector &fg, const ADvector &vars) { fg[0] = 0; double dt = m_condition[0]; double ref_v = m_condition[1]; double obs_w = m_condition[2]; double obs_h = m_condition[3]; double target_x = m_condition[4]; double target_y = m_condition[5]; double v = m_statu[0]; double delta = m_statu[1]; // Weights for how "important" each cost is - can be tuned const double y_cost_weight = 1000; const double th_cost_weight = 4000; const double v_cost_weight = 5000; const double vth_cost_weight = 1000; const double a_cost_weight = 1; const double ath_cost_weight = 10; const double obs_distance_weight = 5000.0; // Cost for CTE, psi error and velocity for (int t = 0; t < N; t++) { CppAD::AD xt = vars[nx + t]; CppAD::AD fx = m_coeffs[0] + m_coeffs[1] * xt + m_coeffs[2] * pow(xt, 2) + m_coeffs[3] * pow(xt, 3); fg[0] += y_cost_weight * CppAD::pow(vars[ny + t] - fx, 2); //fg[0]+=v_cost_weight*(CppAD::pow(vars[nx+t]-target_x,2)+CppAD::pow(vars[ny+t]-target_y,2)); //目标速度loss fg[0] += v_cost_weight * CppAD::pow(vars[nv + t] - ref_v, 2); } for (int t = 0; t < N - 1; t++) { //角速度,加速度,角加速度 weight loss fg[0] += vth_cost_weight * CppAD::pow(vars[ndlt + t], 2); if(t > N / 3){//前中后三段角速度权重1:2:3 fg[0] += vth_cost_weight * CppAD::pow(vars[ndlt + t], 2); } if(t > N / 3 * 2){//前中后三段角速度权重1:2:3 fg[0] += vth_cost_weight * CppAD::pow(vars[ndlt + t], 2); } fg[0] += a_cost_weight * CppAD::pow(vars[nv + t + 1] - vars[nv + t], 2); fg[0] += ath_cost_weight * CppAD::pow(vars[ndlt + t + 1] - vars[ndlt + t], 2); } ///////////////////// fg[1 + nx] = vars[nx] - vars[nv] * dt; fg[1 + ny] = vars[ny]; //CppAD::AD w0=vars[nv]/wheelbase*CppAD::tan(vars[ndlt]); fg[1 + nth] = vars[nth] - vars[ndlt] * dt; //位姿约束 for (int t = 1; t < N; t++) { // State at time t + 1 CppAD::AD x1 = vars[nx + t]; CppAD::AD y1 = vars[ny + t]; CppAD::AD th1 = vars[nth + t]; // State at time t CppAD::AD x0 = vars[nx + t - 1]; CppAD::AD y0 = vars[ny + t - 1]; CppAD::AD th0 = vars[nth + t - 1]; CppAD::AD v0 = vars[nv + t - 1]; // Setting up the rest of the model constraints fg[1 + nx + t] = x1 - (x0 + v0 * CppAD::cos(th0) * dt); fg[1 + ny + t] = y1 - (y0 + v0 * CppAD::sin(th0) * dt); fg[1 + nth + t] = th1 - (th0 + vars[ndlt + t - 1] * dt); } //加速度和dlt约束 fg[1 + nv] = (vars[nv] - v) / dt; fg[1 + ndlt] = (vars[ndlt] - delta) / dt; for (int t = 1; t < N; ++t) { fg[1 + nv + t] = (vars[nv + t] - vars[nv + t - 1]) / dt; fg[1 + ndlt + t] = (vars[ndlt + t] - vars[ndlt + t - 1]) / dt; } if (m_statu.size() == 2 + 16) { //与障碍物的距离 for (int i = 0; i < 8; ++i) { double ox = m_statu[2 + i * 2]; double oy = m_statu[2 + i * 2 + 1]; for (int t = 0; t < N; ++t) { /*第i点的矩形方程为: {[(x-xi)cos(Θi)-(y-yi)sin(Θi)]/1.35}**8 + * {[(x-xi)sin(Θi)+(y-yi)cos(Θi)]/0.75}**8 = 1.0 * * */ CppAD::AD ra = (ox - vars[nx + t]) * CppAD::cos(vars[nth + t]) - (oy - vars[ny + t]) * CppAD::sin(vars[nth + t]); CppAD::AD rb = (ox - vars[nx + t]) * CppAD::sin(vars[nth + t]) + (oy - vars[ny + t]) * CppAD::cos(vars[nth + t]); if(!directY_) fg[1 + nobs + N * i + t] = CppAD::pow(ra / obs_w, 8) + CppAD::pow(rb / (obs_h*0.76), 8); else fg[1 + nobs + N * i + t] = CppAD::pow(ra / (obs_w*0.76), 8) + CppAD::pow(rb / obs_h, 8); } } } } }; LoadedMPC::LoadedMPC(const Pose2d &obs, double obs_w, double obs_h, double min_velocity, double max_velocity) { min_velocity_ = min_velocity; max_velocity_ = max_velocity; obs_relative_pose_ = obs; obs_w_ = obs_w; obs_h_ = obs_h; } LoadedMPC::~LoadedMPC() {} MpcError LoadedMPC::solve(Trajectory trajectory, Pose2d target, Eigen::VectorXd statu, MPC_parameter mpc_param, std::vector &out, Trajectory &select_traj, Trajectory &optimize_trajectory,bool directY) { auto start = std::chrono::steady_clock::now(); // State vector holds all current values neede for vars below Pose2d pose_agv = Pose2d(statu[0], statu[1], statu[2]); double line_velocity = statu[3]; double wmg = statu[4]; //纠正角速度/线速度,使其满足最小转弯半径 double angular = wmg; double radius = mpc_param.shortest_radius * (1.0 / sqrt(line_velocity * line_velocity + 1e-10)); if ((line_velocity * line_velocity) / (wmg * wmg + 1e-9) < (radius * radius)) { angular = fabs(line_velocity) / radius; if (wmg < 0) angular = -angular; } double max_wmg = fabs(line_velocity) / radius;//0.5 / radius; std::vector filte_poses; if (filte_Path(pose_agv, target, trajectory, filte_poses, 20) == false) { printf("filte path failed ...\n"); return failed; } select_traj = Trajectory(filte_poses); //将选中点移动到小车坐标系 std::vector transform_poses; for (int i = 0; i < filte_poses.size(); i++) { double x = filte_poses[i].x() - pose_agv.x(); double y = filte_poses[i].y() - pose_agv.y(); transform_poses.push_back(Pose2d(x, y, 0).rotate(-pose_agv.theta())); } Eigen::VectorXd coef = fit_path(transform_poses); //优化 typedef CPPAD_TESTVECTOR(double) Dvector; //根据当前点和目标点距离,计算目标速度 double dis=Pose2d::distance(pose_agv, target); double ref_velocity = 1.0/(1+exp(-4.*(dis-1))); //目标点与起点的连线 朝向与启动朝向 > M_PI/2.0 Pose2d targetPoseInAGV = Pose2d::relativePose(target, pose_agv); //std::cout<<"target:"< max_velocity_) { line_velocity = max_velocity_; } if (line_velocity < -max_velocity_) { line_velocity = -max_velocity_; } if (angular > max_dlt) { angular = max_dlt; } if (angular < -max_dlt) { angular = -max_dlt; } Eigen::VectorXd statu_velocity(2); if (find_obs) { statu_velocity = Eigen::VectorXd(2 + 16); std::vector vertexs = Pose2d::generate_rectangle_vertexs(obs_relative_pose_, obs_w_ * 2, obs_h_ * 2); for (int i = 0; i < vertexs.size(); ++i) { //std::cout<<"vetex:"< solution; // solve the problem CppAD::ipopt::solve( options, vars, vars_lowerbound, vars_upperbound, constraints_lowerbound, constraints_upperbound, fg_eval, solution); auto now = std::chrono::steady_clock::now(); auto duration = std::chrono::duration_cast(now - start); double time = double(duration.count()) * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den; // invalid_number_detected if (solution.status != CppAD::ipopt::solve_result::success) { printf(" mpc failed statu : %d input: %.4f %.5f(%.5f) max_v:%f\n", solution.status, line_velocity, wmg, angular, max_velocity_); if (solution.status == CppAD::ipopt::solve_result::local_infeasibility) return no_solution; return failed; } // Cost auto cost = solution.obj_value; out.clear(); double solve_velocity[down_count]; double solve_angular[down_count]; for (int i = 0; i < down_count; ++i) { solve_velocity[i] = solution.x[nv + delay + i]; solve_angular[i] = solution.x[ndlt + delay + i]; //纠正角速度/线速度,使其满足最小转弯半径 double correct_angular = solve_angular[i]; if ((solve_velocity[i] * solve_velocity[i]) / (solve_angular[i] * solve_angular[i] + 1e-9) < (radius * radius)) { correct_angular = fabs(line_velocity) / radius; if (solve_angular[i] < 0) correct_angular = -correct_angular; } ///----- printf(" P[%d],input:%.4f %.5f(%.5f) output:%.4f %.5f(%.5f) ref:%.3f max_v:%f time:%.3f\n", i, line_velocity, wmg, angular, solve_velocity[i], solve_angular[i], correct_angular, ref_velocity, max_velocity_, time); /*if(solve_velocity>=0 && solve_velocity-min_velocity_) solve_velocity=-min_velocity_;*/ out.push_back(solve_velocity[i]); out.push_back(correct_angular); } //计算预测轨迹 optimize_trajectory.clear(); for (int i = 0; i < N; ++i) { Pose2d pose(solution.x[nx + i], solution.x[ny + i], solution.x[nth + i]); optimize_trajectory.push_point(pose_agv + pose.rotate(pose_agv.theta())); } return success; } bool LoadedMPC::filte_Path(const Pose2d &point, Pose2d target, Trajectory trajectory, std::vector &poses, int point_num) { double gradient = 0; if (fabs((target - point).x()) == 0) gradient = 200.0; else gradient = (target - point).y() / (target - point).x(); double theta = gradient2theta(gradient, target.x() - point.x() >= 0); if (trajectory.size() < 2) return false; poses.clear(); for (int i = 0; i < trajectory.size(); i++) { // 平移加反向旋转到小车坐标系 Pose2d offset = trajectory[i] - point; double x = offset.x(); double y = offset.y(); double trans_x = x * cos(-theta) - y * sin(-theta); double trans_y = x * sin(-theta) + y * cos(-theta); if (trans_x >= 0 && poses.size() < point_num) { // 旋转到原坐标系 float nx = trans_x * cos(theta) - trans_y * sin(theta); float ny = trans_x * sin(theta) + trans_y * cos(theta); Pose2d pose(nx + point.x(), ny + point.y(), trajectory[i].theta()); poses.push_back(pose); } } int size = poses.size(); if (size < point_num) { //一个点都没有,则以当前点开始,反向取点 float dl = -0.1; Pose2d last = point; if (size >= 1) { //从最后点到最后一点均匀选取 last = poses[size - 1]; // dl = 0.1; } float dx = cos(last.theta()) * dl; float dy = sin(last.theta()) * dl; for (int i = 1; i < point_num - size + 1; ++i) poses.push_back(Pose2d(last.x() + dx * i, last.y() + dy * i, last.theta())); } return true; } Eigen::VectorXd LoadedMPC::fit_path(const std::vector &trajectory) { int order = 3; assert(order >= 1 && order <= trajectory.size() - 1); Eigen::MatrixXd A(trajectory.size(), order + 1); Eigen::VectorXd yvals(trajectory.size()); for (int i = 0; i < trajectory.size(); i++) { A(i, 0) = 1.0; yvals[i] = trajectory[i].y(); } for (int j = 0; j < trajectory.size(); j++) { for (int i = 0; i < order; i++) { A(j, i + 1) = A(j, i) * trajectory[j].x(); } } auto Q = A.householderQr(); auto result = Q.solve(yvals); return result; }