#include "planner.hpp" #include #include "tools/math_tools.hpp" #include "tools/trajectory.hpp" #include "tools/yaml.hpp" using namespace std::chrono_literals; namespace auto_aim { Planner::Planner(const std::string & config_path) { auto yaml = tools::load(config_path); yaw_offset_ = tools::read(yaml, "yaw_offset") / 57.3; pitch_offset_ = tools::read(yaml, "pitch_offset") / 57.3; fire_thresh_ = tools::read(yaml, "fire_thresh"); decision_speed_ = tools::read(yaml, "decision_speed"); high_speed_delay_time_ = tools::read(yaml, "high_speed_delay_time"); low_speed_delay_time_ = tools::read(yaml, "low_speed_delay_time"); setup_yaw_solver(config_path); setup_pitch_solver(config_path); } Plan Planner::plan(Target target, double bullet_speed) { // 0. Check bullet speed if (bullet_speed < 10 || bullet_speed > 25) { bullet_speed = 22; } // 1. Predict fly_time Eigen::Vector3d xyz; auto min_dist = 1e10; for (auto & xyza : target.armor_xyza_list()) { auto dist = xyza.head<2>().norm(); if (dist < min_dist) { min_dist = dist; xyz = xyza.head<3>(); } } auto bullet_traj = tools::Trajectory(bullet_speed, min_dist, xyz.z()); target.predict(bullet_traj.fly_time); // 2. Get trajectory double yaw0; Trajectory traj; try { yaw0 = aim(target, bullet_speed)(0); traj = get_trajectory(target, yaw0, bullet_speed); } catch (const std::exception & e) { tools::logger()->warn("Unsolvable target {:.2f}", bullet_speed); return {false}; } // 3. Solve yaw Eigen::VectorXd x0(2); x0 << traj(0, 0), traj(1, 0); tiny_set_x0(yaw_solver_, x0); yaw_solver_->work->Xref = traj.block(0, 0, 2, HORIZON); tiny_solve(yaw_solver_); // 4. Solve pitch x0 << traj(2, 0), traj(3, 0); tiny_set_x0(pitch_solver_, x0); pitch_solver_->work->Xref = traj.block(2, 0, 2, HORIZON); tiny_solve(pitch_solver_); Plan plan; plan.control = true; plan.target_yaw = tools::limit_rad(traj(0, HALF_HORIZON) + yaw0); plan.target_pitch = traj(2, HALF_HORIZON); plan.yaw = tools::limit_rad(yaw_solver_->work->x(0, HALF_HORIZON) + yaw0); plan.yaw_vel = yaw_solver_->work->x(1, HALF_HORIZON); plan.yaw_acc = yaw_solver_->work->u(0, HALF_HORIZON); plan.pitch = pitch_solver_->work->x(0, HALF_HORIZON); plan.pitch_vel = pitch_solver_->work->x(1, HALF_HORIZON); plan.pitch_acc = pitch_solver_->work->u(0, HALF_HORIZON); auto shoot_offset_ = 2; plan.fire = std::hypot( traj(0, HALF_HORIZON + shoot_offset_) - yaw_solver_->work->x(0, HALF_HORIZON + shoot_offset_), traj(2, HALF_HORIZON + shoot_offset_) - pitch_solver_->work->x(0, HALF_HORIZON + shoot_offset_)) < fire_thresh_; return plan; } Plan Planner::plan(std::optional target, double bullet_speed) { if (!target.has_value()) return {false}; double delay_time = std::abs(target->ekf_x()[7]) > decision_speed_ ? high_speed_delay_time_ : low_speed_delay_time_; auto future = std::chrono::steady_clock::now() + std::chrono::microseconds(int(delay_time * 1e6)); target->predict(future); return plan(*target, bullet_speed); } void Planner::setup_yaw_solver(const std::string & config_path) { auto yaml = tools::load(config_path); auto max_yaw_acc = tools::read(yaml, "max_yaw_acc"); auto Q_yaw = tools::read>(yaml, "Q_yaw"); auto R_yaw = tools::read>(yaml, "R_yaw"); Eigen::MatrixXd A{{1, DT}, {0, 1}}; Eigen::MatrixXd B{{0}, {DT}}; Eigen::VectorXd f{{0, 0}}; Eigen::Matrix Q(Q_yaw.data()); Eigen::Matrix R(R_yaw.data()); tiny_setup(&yaw_solver_, A, B, f, Q.asDiagonal(), R.asDiagonal(), 1.0, 2, 1, HORIZON, 0); Eigen::MatrixXd x_min = Eigen::MatrixXd::Constant(2, HORIZON, -1e17); Eigen::MatrixXd x_max = Eigen::MatrixXd::Constant(2, HORIZON, 1e17); Eigen::MatrixXd u_min = Eigen::MatrixXd::Constant(1, HORIZON - 1, -max_yaw_acc); Eigen::MatrixXd u_max = Eigen::MatrixXd::Constant(1, HORIZON - 1, max_yaw_acc); tiny_set_bound_constraints(yaw_solver_, x_min, x_max, u_min, u_max); yaw_solver_->settings->max_iter = 10; } void Planner::setup_pitch_solver(const std::string & config_path) { auto yaml = tools::load(config_path); auto max_pitch_acc = tools::read(yaml, "max_pitch_acc"); auto Q_pitch = tools::read>(yaml, "Q_pitch"); auto R_pitch = tools::read>(yaml, "R_pitch"); Eigen::MatrixXd A{{1, DT}, {0, 1}}; Eigen::MatrixXd B{{0}, {DT}}; Eigen::VectorXd f{{0, 0}}; Eigen::Matrix Q(Q_pitch.data()); Eigen::Matrix R(R_pitch.data()); tiny_setup(&pitch_solver_, A, B, f, Q.asDiagonal(), R.asDiagonal(), 1.0, 2, 1, HORIZON, 0); Eigen::MatrixXd x_min = Eigen::MatrixXd::Constant(2, HORIZON, -1e17); Eigen::MatrixXd x_max = Eigen::MatrixXd::Constant(2, HORIZON, 1e17); Eigen::MatrixXd u_min = Eigen::MatrixXd::Constant(1, HORIZON - 1, -max_pitch_acc); Eigen::MatrixXd u_max = Eigen::MatrixXd::Constant(1, HORIZON - 1, max_pitch_acc); tiny_set_bound_constraints(pitch_solver_, x_min, x_max, u_min, u_max); pitch_solver_->settings->max_iter = 10; } Eigen::Matrix Planner::aim(const Target & target, double bullet_speed) { Eigen::Vector3d xyz; double yaw; auto min_dist = 1e10; for (auto & xyza : target.armor_xyza_list()) { auto dist = xyza.head<2>().norm(); if (dist < min_dist) { min_dist = dist; xyz = xyza.head<3>(); yaw = xyza[3]; } } debug_xyza = Eigen::Vector4d(xyz.x(), xyz.y(), xyz.z(), yaw); auto azim = std::atan2(xyz.y(), xyz.x()); auto bullet_traj = tools::Trajectory(bullet_speed, min_dist, xyz.z()); if (bullet_traj.unsolvable) throw std::runtime_error("Unsolvable bullet trajectory!"); return {tools::limit_rad(azim + yaw_offset_), -bullet_traj.pitch - pitch_offset_}; } Trajectory Planner::get_trajectory(Target & target, double yaw0, double bullet_speed) { Trajectory traj; target.predict(-DT * (HALF_HORIZON + 1)); auto yaw_pitch_last = aim(target, bullet_speed); target.predict(DT); // [0] = -HALF_HORIZON * DT -> [HHALF_HORIZON] = 0 auto yaw_pitch = aim(target, bullet_speed); for (int i = 0; i < HORIZON; i++) { target.predict(DT); auto yaw_pitch_next = aim(target, bullet_speed); auto yaw_vel = tools::limit_rad(yaw_pitch_next(0) - yaw_pitch_last(0)) / (2 * DT); auto pitch_vel = (yaw_pitch_next(1) - yaw_pitch_last(1)) / (2 * DT); traj.col(i) << tools::limit_rad(yaw_pitch(0) - yaw0), yaw_vel, yaw_pitch(1), pitch_vel; yaw_pitch_last = yaw_pitch; yaw_pitch = yaw_pitch_next; } return traj; } } // namespace auto_aim