244 lines
7.7 KiB
C++
244 lines
7.7 KiB
C++
#include "decider.hpp"
|
|
|
|
#include <yaml-cpp/yaml.h>
|
|
|
|
#include <filesystem>
|
|
#include <opencv2/opencv.hpp>
|
|
|
|
#include "tools/logger.hpp"
|
|
#include "tools/math_tools.hpp"
|
|
|
|
namespace omniperception
|
|
{
|
|
Decider::Decider(const std::string & config_path) : detector_(config_path), count_(0)
|
|
{
|
|
auto yaml = YAML::LoadFile(config_path);
|
|
img_width_ = yaml["image_width"].as<double>();
|
|
img_height_ = yaml["image_height"].as<double>();
|
|
fov_h_ = yaml["fov_h"].as<double>();
|
|
fov_v_ = yaml["fov_v"].as<double>();
|
|
new_fov_h_ = yaml["new_fov_h"].as<double>();
|
|
new_fov_v_ = yaml["new_fov_v"].as<double>();
|
|
enemy_color_ =
|
|
(yaml["enemy_color"].as<std::string>() == "red") ? auto_aim::Color::red : auto_aim::Color::blue;
|
|
mode_ = yaml["mode"].as<double>();
|
|
}
|
|
|
|
io::Command Decider::decide(
|
|
auto_aim::YOLO & yolo, const Eigen::Vector3d & gimbal_pos, io::USBCamera & usbcam1,
|
|
io::USBCamera & usbcam2, io::Camera & back_camera)
|
|
{
|
|
Eigen::Vector2d delta_angle;
|
|
io::USBCamera * cams[] = {&usbcam1, &usbcam2};
|
|
|
|
cv::Mat usb_img;
|
|
std::chrono::steady_clock::time_point timestamp;
|
|
if (count_ < 0 || count_ > 2) {
|
|
throw std::runtime_error("count_ out of valid range [0,2]");
|
|
}
|
|
if (count_ == 2) {
|
|
back_camera.read(usb_img, timestamp);
|
|
} else {
|
|
cams[count_]->read(usb_img, timestamp);
|
|
}
|
|
auto armors = yolo.detect(usb_img);
|
|
auto empty = armor_filter(armors);
|
|
|
|
if (!empty) {
|
|
if (count_ == 2) {
|
|
delta_angle = this->delta_angle(armors, "back");
|
|
} else {
|
|
delta_angle = this->delta_angle(armors, cams[count_]->device_name);
|
|
}
|
|
|
|
tools::logger()->debug(
|
|
"[{} camera] delta yaw:{:.2f},target pitch:{:.2f},armor number:{},armor name:{}",
|
|
(count_ == 2 ? "back" : cams[count_]->device_name), delta_angle[0], delta_angle[1],
|
|
armors.size(), auto_aim::ARMOR_NAMES[armors.front().name]);
|
|
|
|
count_ = (count_ + 1) % 3;
|
|
|
|
return io::Command{
|
|
true, false, tools::limit_rad(gimbal_pos[0] + delta_angle[0] / 57.3),
|
|
tools::limit_rad(delta_angle[1] / 57.3)};
|
|
}
|
|
|
|
count_ = (count_ + 1) % 3;
|
|
// 如果没有找到目标,返回默认命令
|
|
return io::Command{false, false, 0, 0};
|
|
}
|
|
|
|
io::Command Decider::decide(
|
|
auto_aim::YOLO & yolo, const Eigen::Vector3d & gimbal_pos, io::Camera & back_cammera)
|
|
{
|
|
cv::Mat img;
|
|
std::chrono::steady_clock::time_point timestamp;
|
|
back_cammera.read(img, timestamp);
|
|
auto armors = yolo.detect(img);
|
|
auto empty = armor_filter(armors);
|
|
|
|
if (!empty) {
|
|
auto delta_angle = this->delta_angle(armors, "back");
|
|
tools::logger()->debug(
|
|
"[back camera] delta yaw:{:.2f},target pitch:{:.2f},armor number:{},armor name:{}",
|
|
delta_angle[0], delta_angle[1], armors.size(), auto_aim::ARMOR_NAMES[armors.front().name]);
|
|
|
|
return io::Command{
|
|
true, false, tools::limit_rad(gimbal_pos[0] + delta_angle[0] / 57.3),
|
|
tools::limit_rad(delta_angle[1] / 57.3)};
|
|
}
|
|
|
|
return io::Command{false, false, 0, 0};
|
|
}
|
|
|
|
io::Command Decider::decide(const std::vector<DetectionResult> & detection_queue)
|
|
{
|
|
if (detection_queue.empty()) {
|
|
return io::Command{false, false, 0, 0};
|
|
}
|
|
|
|
DetectionResult dr = detection_queue.front();
|
|
if (dr.armors.empty()) return io::Command{false, false, 0, 0};
|
|
tools::logger()->info(
|
|
"omniperceptron find {},delta yaw is {:.4f}", auto_aim::ARMOR_NAMES[dr.armors.front().name],
|
|
dr.delta_yaw * 57.3);
|
|
|
|
return io::Command{true, false, dr.delta_yaw, dr.delta_pitch};
|
|
};
|
|
|
|
Eigen::Vector2d Decider::delta_angle(
|
|
const std::list<auto_aim::Armor> & armors, const std::string & camera)
|
|
{
|
|
Eigen::Vector2d delta_angle;
|
|
if (camera == "left") {
|
|
delta_angle[0] = 62 + (new_fov_h_ / 2) - armors.front().center_norm.x * new_fov_h_;
|
|
delta_angle[1] = armors.front().center_norm.y * new_fov_v_ - new_fov_v_ / 2;
|
|
return delta_angle;
|
|
}
|
|
|
|
else if (camera == "right") {
|
|
delta_angle[0] = -62 + (new_fov_h_ / 2) - armors.front().center_norm.x * new_fov_h_;
|
|
delta_angle[1] = armors.front().center_norm.y * new_fov_v_ - new_fov_v_ / 2;
|
|
return delta_angle;
|
|
}
|
|
|
|
else {
|
|
delta_angle[0] = 170 + (54.2 / 2) - armors.front().center_norm.x * 54.2;
|
|
delta_angle[1] = armors.front().center_norm.y * 44.5 - 44.5 / 2;
|
|
return delta_angle;
|
|
}
|
|
}
|
|
|
|
bool Decider::armor_filter(std::list<auto_aim::Armor> & armors)
|
|
{
|
|
if (armors.empty()) return true;
|
|
// 过滤非敌方装甲板
|
|
armors.remove_if([&](const auto_aim::Armor & a) { return a.color != enemy_color_; });
|
|
|
|
// 25赛季没有5号装甲板
|
|
armors.remove_if([&](const auto_aim::Armor & a) { return a.name == auto_aim::ArmorName::five; });
|
|
// 不打工程
|
|
// armors.remove_if([&](const auto_aim::Armor & a) { return a.name == auto_aim::ArmorName::two; });
|
|
// 不打前哨站
|
|
armors.remove_if(
|
|
[&](const auto_aim::Armor & a) { return a.name == auto_aim::ArmorName::outpost; });
|
|
|
|
// 过滤掉刚复活无敌的装甲板
|
|
armors.remove_if([&](const auto_aim::Armor & a) {
|
|
return std::find(invincible_armor_.begin(), invincible_armor_.end(), a.name) !=
|
|
invincible_armor_.end();
|
|
});
|
|
|
|
return armors.empty();
|
|
}
|
|
|
|
void Decider::set_priority(std::list<auto_aim::Armor> & armors)
|
|
{
|
|
if (armors.empty()) return;
|
|
|
|
const PriorityMap & priority_map = (mode_ == MODE_ONE) ? mode1 : mode2;
|
|
|
|
if (!armors.empty()) {
|
|
for (auto & armor : armors) {
|
|
armor.priority = priority_map.at(armor.name);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Decider::sort(std::vector<DetectionResult> & detection_queue)
|
|
{
|
|
if (detection_queue.empty()) return;
|
|
|
|
// 对每个 DetectionResult 调用 armor_filter 和 set_priority
|
|
for (auto & dr : detection_queue) {
|
|
armor_filter(dr.armors);
|
|
set_priority(dr.armors);
|
|
|
|
// 对每个 DetectionResult 中的 armors 进行排序
|
|
dr.armors.sort(
|
|
[](const auto_aim::Armor & a, const auto_aim::Armor & b) { return a.priority < b.priority; });
|
|
}
|
|
|
|
// 根据优先级对 DetectionResult 进行排序
|
|
std::sort(
|
|
detection_queue.begin(), detection_queue.end(),
|
|
[](const DetectionResult & a, const DetectionResult & b) {
|
|
return a.armors.front().priority < b.armors.front().priority;
|
|
});
|
|
}
|
|
|
|
Eigen::Vector4d Decider::get_target_info(
|
|
const std::list<auto_aim::Armor> & armors, const std::list<auto_aim::Target> & targets)
|
|
{
|
|
if (armors.empty() || targets.empty()) return Eigen::Vector4d::Zero();
|
|
|
|
auto target = targets.front();
|
|
|
|
for (const auto & armor : armors) {
|
|
if (armor.name == target.name) {
|
|
return Eigen::Vector4d{
|
|
armor.xyz_in_gimbal[0], armor.xyz_in_gimbal[1], 1,
|
|
static_cast<double>(armor.name) + 1}; //避免歧义+1(详见通信协议)
|
|
}
|
|
}
|
|
|
|
return Eigen::Vector4d::Zero();
|
|
}
|
|
|
|
void Decider::get_invincible_armor(const std::vector<int8_t> & invincible_enemy_ids)
|
|
{
|
|
invincible_armor_.clear();
|
|
|
|
if (invincible_enemy_ids.empty()) return;
|
|
|
|
for (const auto & id : invincible_enemy_ids) {
|
|
tools::logger()->info("invincible armor id: {}", id);
|
|
invincible_armor_.push_back(auto_aim::ArmorName(id - 1));
|
|
}
|
|
}
|
|
|
|
void Decider::get_auto_aim_target(
|
|
std::list<auto_aim::Armor> & armors, const std::vector<int8_t> & auto_aim_target)
|
|
{
|
|
if (auto_aim_target.empty()) return;
|
|
|
|
std::vector<auto_aim::ArmorName> auto_aim_targets;
|
|
|
|
for (const auto & target : auto_aim_target) {
|
|
if (target <= 0 || static_cast<size_t>(target) > auto_aim::ARMOR_NAMES.size()) {
|
|
tools::logger()->warn("Received invalid auto_aim target value: {}", int(target));
|
|
continue;
|
|
}
|
|
auto_aim_targets.push_back(static_cast<auto_aim::ArmorName>(target - 1));
|
|
tools::logger()->info("nav send auto_aim target is {}", auto_aim::ARMOR_NAMES[target - 1]);
|
|
}
|
|
|
|
if (auto_aim_targets.empty()) return;
|
|
|
|
armors.remove_if([&](const auto_aim::Armor & a) {
|
|
return std::find(auto_aim_targets.begin(), auto_aim_targets.end(), a.name) ==
|
|
auto_aim_targets.end();
|
|
});
|
|
}
|
|
|
|
} // namespace omniperception
|