#include "gimbal.hpp" #include "serial/serial.h" #include "tools/crc.hpp" #include "tools/logger.hpp" #include "tools/math_tools.hpp" #include "tools/yaml.hpp" namespace io { Gimbal::Gimbal(const std::string & config_path) { auto yaml = tools::load(config_path); auto com_port = tools::read(yaml, "com_port"); try { serial_.setPort(com_port); serial_.setBaudrate(115200); // 设置波特率 serial_.setFlowcontrol(serial::flowcontrol_none); serial_.setParity(serial::parity_none); serial_.setStopbits(serial::stopbits_one); serial_.setBytesize(serial::eightbits); serial::Timeout timeout = serial::Timeout::simpleTimeout(100); // 100ms超时 serial_.setTimeout(timeout); serial_.open(); } catch (const std::exception & e) { tools::logger()->error("[Gimbal] Failed to open serial: {}", e.what()); exit(1); } thread_ = std::thread(&Gimbal::read_thread, this); queue_.pop(); tools::logger()->info("[Gimbal] First q received."); } Gimbal::~Gimbal() { quit_ = true; if (thread_.joinable()) thread_.join(); serial_.close(); } GimbalMode Gimbal::mode() const { std::lock_guard lock(mutex_); return mode_; } GimbalState Gimbal::state() const { std::lock_guard lock(mutex_); return state_; } std::string Gimbal::str(GimbalMode mode) const { switch (mode) { case GimbalMode::IDLE: return "IDLE"; case GimbalMode::AUTO_AIM: return "AUTO_AIM"; case GimbalMode::SMALL_BUFF: return "SMALL_BUFF"; case GimbalMode::BIG_BUFF: return "BIG_BUFF"; default: return "INVALID"; } } Eigen::Quaterniond Gimbal::q(std::chrono::steady_clock::time_point t) { while (true) { auto [q_a, t_a] = queue_.pop(); auto [q_b, t_b] = queue_.front(); auto t_ab = tools::delta_time(t_a, t_b); auto t_ac = tools::delta_time(t_a, t); auto k = t_ac / t_ab; Eigen::Quaterniond q_c = q_a.slerp(k, q_b).normalized(); if (t < t_a) return q_c; if (!(t_a < t && t <= t_b)) continue; return q_c; } } void Gimbal::send(io::VisionToGimbal VisionToGimbal) { tx_data_.mode = VisionToGimbal.mode; tx_data_.yaw = VisionToGimbal.yaw; tx_data_.yaw_vel = VisionToGimbal.yaw_vel; tx_data_.yaw_acc = VisionToGimbal.yaw_acc; tx_data_.pitch = VisionToGimbal.pitch; tx_data_.pitch_vel = VisionToGimbal.pitch_vel; tx_data_.pitch_acc = VisionToGimbal.pitch_acc; tx_data_.crc16 = tools::get_crc16( reinterpret_cast(&tx_data_), sizeof(tx_data_) - sizeof(tx_data_.crc16)); try { serial_.write(reinterpret_cast(&tx_data_), sizeof(tx_data_)); } catch (const std::exception & e) { tools::logger()->warn("[Gimbal] Failed to write serial: {}", e.what()); } } void Gimbal::send( bool control, bool fire, float yaw, float yaw_vel, float yaw_acc, float pitch, float pitch_vel, float pitch_acc) { tx_data_.mode = control ? (fire ? 2 : 1) : 0; tx_data_.yaw = yaw; tx_data_.yaw_vel = yaw_vel; tx_data_.yaw_acc = yaw_acc; tx_data_.pitch = pitch; tx_data_.pitch_vel = pitch_vel; tx_data_.pitch_acc = pitch_acc; tx_data_.crc16 = tools::get_crc16( reinterpret_cast(&tx_data_), sizeof(tx_data_) - sizeof(tx_data_.crc16)); try { serial_.write(reinterpret_cast(&tx_data_), sizeof(tx_data_)); } catch (const std::exception & e) { tools::logger()->warn("[Gimbal] Failed to write serial: {}", e.what()); } } bool Gimbal::read(uint8_t * buffer, size_t size) { try { return serial_.read(buffer, size) == size; } catch (const std::exception & e) { // tools::logger()->warn("[Gimbal] Failed to read serial: {}", e.what()); return false; } } void Gimbal::read_thread() { tools::logger()->info("[Gimbal] read_thread started."); int error_count = 0; while (!quit_) { if (error_count > 5000) { error_count = 0; tools::logger()->warn("[Gimbal] Too many errors, attempting to reconnect..."); reconnect(); continue; } if (!read(reinterpret_cast(&rx_data_), sizeof(rx_data_.head))) { error_count++; continue; } if (rx_data_.head[0] != 'M' || rx_data_.head[1] != 'R') continue; auto t = std::chrono::steady_clock::now(); if (!read( reinterpret_cast(&rx_data_) + sizeof(rx_data_.head), sizeof(rx_data_) - sizeof(rx_data_.head))) { error_count++; continue; } float q0 = rx_data_.q[0], q1 = rx_data_.q[1], q2 = rx_data_.q[2], q3 = rx_data_.q[3]; float yaw = rx_data_.yaw, yaw_vel = rx_data_.yaw_vel; float pitch = rx_data_.pitch, pitch_vel = rx_data_.pitch_vel; float bullet_speed = rx_data_.bullet_speed; uint16_t bullet_count = rx_data_.bullet_count, crc16 = rx_data_.crc16; if (!tools::check_crc16(reinterpret_cast(&rx_data_), sizeof(rx_data_))) { tools::logger()->debug("[Gimbal] CRC16 check failed."); tools::logger()->debug( "[Gimbal] Received crc16: {}, Calculated crc16: {}", crc16, tools::get_crc16( reinterpret_cast(&rx_data_), sizeof(rx_data_) - sizeof(rx_data_.crc16))); continue; } /*打印受到的数据*/ // struct __attribute__((packed)) GimbalToVision // { // uint8_t head[2] = {'M', 'R'}; // uint8_t mode; // 0: 空闲, 1: 自瞄, 2: 小符, 3: 大符 // float q[4]; // wxyz顺序 // float yaw; // float yaw_vel; // float pitch; // float pitch_vel; // float bullet_speed; // uint16_t bullet_count; // 子弹累计发送次数 // uint16_t crc16; // }; // float q0 = rx_data_.q[0], q1 = rx_data_.q[1], q2 = rx_data_.q[2], q3 = rx_data_.q[3]; // float yaw = rx_data_.yaw, yaw_vel = rx_data_.yaw_vel; // float pitch = rx_data_.pitch, pitch_vel = rx_data_.pitch_vel; // float bullet_speed = rx_data_.bullet_speed; // uint16_t bullet_count = rx_data_.bullet_count, crc16 = rx_data_.crc16; tools::logger()->debug( "[Gimbal] mode: {}, q: [{:.3f}, {:.3f}, {:.3f}, {:.3f}], yaw: {:.3f}, yaw_vel: {:.3f}, " "pitch: {:.3f}, pitch_vel: {:.3f}, bullet_speed: {:.3f}, bullet_count: {}, crc16: {}", rx_data_.mode, q0, q1, q2, q3, yaw, yaw_vel, pitch, pitch_vel, bullet_speed, bullet_count, crc16); error_count = 0; Eigen::Quaterniond q(rx_data_.q[0], rx_data_.q[1], rx_data_.q[2], rx_data_.q[3]); queue_.push({q, t}); std::lock_guard lock(mutex_); state_.yaw = rx_data_.yaw; state_.yaw_vel = rx_data_.yaw_vel; state_.pitch = rx_data_.pitch; state_.pitch_vel = rx_data_.pitch_vel; state_.bullet_speed = rx_data_.bullet_speed; state_.bullet_count = rx_data_.bullet_count; switch (rx_data_.mode) { case 0: mode_ = GimbalMode::IDLE; break; case 1: mode_ = GimbalMode::AUTO_AIM; break; case 2: mode_ = GimbalMode::SMALL_BUFF; break; case 3: mode_ = GimbalMode::BIG_BUFF; break; default: mode_ = GimbalMode::IDLE; tools::logger()->warn("[Gimbal] Invalid mode: {}", rx_data_.mode); break; } } tools::logger()->info("[Gimbal] read_thread stopped."); } void Gimbal::reconnect() { int max_retry_count = 10; for (int i = 0; i < max_retry_count && !quit_; ++i) { tools::logger()->warn("[Gimbal] Reconnecting serial, attempt {}/{}...", i + 1, max_retry_count); try { serial_.close(); std::this_thread::sleep_for(std::chrono::seconds(1)); } catch (...) { } try { serial_.open(); // 尝试重新打开 queue_.clear(); tools::logger()->info("[Gimbal] Reconnected serial successfully."); break; } catch (const std::exception & e) { tools::logger()->warn("[Gimbal] Reconnect failed: {}", e.what()); std::this_thread::sleep_for(std::chrono::seconds(1)); } } } } // namespace io