rm_vision/tasks/auto_aim/planner/planner.cpp
2025-12-15 02:33:20 +08:00

205 lines
6.6 KiB
C++

#include "planner.hpp"
#include <vector>
#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<double>(yaml, "yaw_offset") / 57.3;
pitch_offset_ = tools::read<double>(yaml, "pitch_offset") / 57.3;
fire_thresh_ = tools::read<double>(yaml, "fire_thresh");
decision_speed_ = tools::read<double>(yaml, "decision_speed");
high_speed_delay_time_ = tools::read<double>(yaml, "high_speed_delay_time");
low_speed_delay_time_ = tools::read<double>(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> 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<double>(yaml, "max_yaw_acc");
auto Q_yaw = tools::read<std::vector<double>>(yaml, "Q_yaw");
auto R_yaw = tools::read<std::vector<double>>(yaml, "R_yaw");
Eigen::MatrixXd A{{1, DT}, {0, 1}};
Eigen::MatrixXd B{{0}, {DT}};
Eigen::VectorXd f{{0, 0}};
Eigen::Matrix<double, 2, 1> Q(Q_yaw.data());
Eigen::Matrix<double, 1, 1> 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<double>(yaml, "max_pitch_acc");
auto Q_pitch = tools::read<std::vector<double>>(yaml, "Q_pitch");
auto R_pitch = tools::read<std::vector<double>>(yaml, "R_pitch");
Eigen::MatrixXd A{{1, DT}, {0, 1}};
Eigen::MatrixXd B{{0}, {DT}};
Eigen::VectorXd f{{0, 0}};
Eigen::Matrix<double, 2, 1> Q(Q_pitch.data());
Eigen::Matrix<double, 1, 1> 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<double, 2, 1> 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