-
Notifications
You must be signed in to change notification settings - Fork 5
refactor: ekf and debug tool #62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,191 @@ | ||
| #include "outpost.hpp" | ||
|
|
||
| #include "utility/math/angle.hpp" | ||
| #include "utility/robot/constant.hpp" | ||
|
|
||
| #include <eigen3/Eigen/Geometry> | ||
|
|
||
| using namespace rmcs; | ||
|
|
||
| struct OutpostModel::Impl { | ||
| using StateVector = Eigen::Matrix<double, 5, 1>; | ||
| using Covariance = Eigen::Matrix<double, 5, 5>; | ||
| using ProcessNoise = Eigen::Matrix<double, 5, 5>; | ||
| using ObservationNoise = Eigen::Matrix<double, 4, 4>; | ||
| using ObservationVector = Eigen::Matrix<double, 4, 1>; | ||
| using ObservationJacobian = Eigen::Matrix<double, 4, 5>; | ||
| using KalmanGain = Eigen::Matrix<double, 5, 4>; | ||
|
|
||
| static auto observation_jacobian() { | ||
| auto jacobian = ObservationJacobian { }; | ||
| // clang-format off | ||
| jacobian << | ||
| 1, 0, 0, 0, 0, | ||
| 0, 1, 0, 0, 0, | ||
| 0, 0, 1, 0, 0, | ||
| 0, 0, 0, 0, 1; | ||
| // clang-format on | ||
| return jacobian; | ||
| } | ||
|
|
||
| struct Context { | ||
| StateVector posteriors_state = StateVector::Zero(); | ||
| Covariance posteriors_covariance = Covariance::Identity(); | ||
| ProcessNoise noise_process = ProcessNoise::Zero(); | ||
| ObservationNoise noise_observation = ObservationNoise::Zero(); | ||
|
|
||
| Context() noexcept { | ||
| noise_process.diagonal() << 1e-4, 1e-4, 1e-4, 1e-2, 1e-2; | ||
| noise_observation.diagonal() << 0.09, 0.09, 0.09, 9e-2; | ||
| } | ||
|
|
||
| static_assert(sizeof(State) == sizeof(posteriors_state)); | ||
| auto state() noexcept -> State& { | ||
| auto* data_pointer = &posteriors_state; | ||
| return *reinterpret_cast<State*>(data_pointer); | ||
| } | ||
| auto state() const noexcept -> const State& { | ||
| const auto* data_pointer = &posteriors_state; | ||
| return *reinterpret_cast<const State*>(data_pointer); | ||
| } | ||
|
Comment on lines
+42
to
+50
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
rg -nP 'reinterpret_cast<\s*(const\s+)?State(\s*[*&])|reinterpret_cast<\s*(const\s+)?StateVector(\s*[*&])' src/module/predictor/model/outpost.cpp
rg -nP 'static_assert\s*\(\s*sizeof\(State\)\s*==\s*sizeof\(posteriors_state\)\s*\)' src/module/predictor/model/outpost.cppRepository: Alliance-Algorithm/rmcs_auto_aim_v2 Length of output: 379 🏁 Script executed: cat -n src/module/predictor/model/outpost.cpp | head -160Repository: Alliance-Algorithm/rmcs_auto_aim_v2 Length of output: 7918 🏁 Script executed: cat -n src/module/predictor/model/outpost.hppRepository: Alliance-Algorithm/rmcs_auto_aim_v2 Length of output: 867 移除 Lines 42-50 与 Line 122 的 直接重解释 建议修复示例- static_assert(sizeof(State) == sizeof(posteriors_state));
- auto state() noexcept -> State& {
- auto* data_pointer = &posteriors_state;
- return *reinterpret_cast<State*>(data_pointer);
- }
- auto state() const noexcept -> const State& {
- const auto* data_pointer = &posteriors_state;
- return *reinterpret_cast<const State*>(data_pointer);
- }
+ static auto to_state(const StateVector& v) noexcept -> State {
+ return State {
+ .x = v[0],
+ .y = v[1],
+ .z = v[2],
+ .rotation_speed = v[3],
+ .rotation_angle = v[4],
+ };
+ }
+ static auto from_state(const State& s) noexcept -> StateVector {
+ auto v = StateVector { };
+ v << s.x, s.y, s.z, s.rotation_speed, s.rotation_angle;
+ return v;
+ }
@@
- auto posterior_state = State { prior_state };
- reinterpret_cast<StateVector&>(posterior_state).noalias() += kalman_gain * innovation;
+ auto posterior_vector = Context::from_state(prior_state);
+ posterior_vector.noalias() += kalman_gain * innovation;
+ auto posterior_state = Context::to_state(posterior_vector);🤖 Prompt for AI Agents |
||
|
|
||
| auto update_center(const Eigen::Vector3d& point) noexcept { | ||
| state().x = point.x(); | ||
| state().y = point.y(); | ||
| state().z = point.z(); | ||
| } | ||
| } context; | ||
|
|
||
| auto initialize_from(const Armor3d& armor) noexcept -> void { | ||
| constexpr auto pitch = kPredictedOutpostArmorPitch; | ||
| constexpr auto radius = kOutpostRadius; | ||
|
|
||
| const auto translation = armor.translation.make<Eigen::Vector3d>(); | ||
| const auto orientation = armor.orientation.make<Eigen::Quaterniond>(); | ||
|
|
||
| const auto backward = Eigen::Vector3d { orientation * Eigen::Vector3d::UnitX() }; | ||
| const auto armor_to_center = Eigen::Vector3d { | ||
| Eigen::AngleAxisd { -pitch, orientation * Eigen::Vector3d::UnitY() } * backward | ||
| }; | ||
| const auto center = Eigen::Vector3d { translation + radius * armor_to_center }; | ||
|
|
||
| context.update_center(center); | ||
| context.state().rotation_speed = 0.0; | ||
| context.state().rotation_angle = | ||
| util::normalize_angle(std::atan2(-armor_to_center.y(), -armor_to_center.x())); | ||
|
|
||
| auto initial_covariance_diagonal = Eigen::Matrix<double, 5, 1> { }; | ||
| initial_covariance_diagonal << 1.0, 1.0, 1.0, 64.0, 0.4; | ||
|
|
||
| context.posteriors_covariance = initial_covariance_diagonal.asDiagonal(); | ||
| } | ||
|
|
||
| static auto predict_state(double dt, const State& last) -> State { | ||
| auto next = State { last }; | ||
|
|
||
| next.rotation_angle = util::normalize_angle(last.rotation_angle + last.rotation_speed * dt); | ||
| return next; | ||
| } | ||
|
|
||
| auto predict_covariance(double dt, const Covariance& covariance) const -> Covariance { | ||
| auto jacobian = Covariance { Covariance::Identity() }; | ||
| jacobian(4, 3) = dt; | ||
| return jacobian * covariance * jacobian.transpose() + context.noise_process; | ||
| } | ||
|
|
||
| static auto measure_innovation(const ObservationVector& observation, const State& prior_state) | ||
| -> ObservationVector { | ||
| auto predicted_observation = ObservationVector { }; | ||
| predicted_observation << prior_state.x, prior_state.y, prior_state.z, | ||
| prior_state.rotation_angle; | ||
|
|
||
| auto innovation = ObservationVector { observation - predicted_observation }; | ||
| innovation.coeffRef(3) = util::normalize_angle(innovation.coeff(3)); | ||
| return innovation; | ||
| } | ||
|
|
||
| auto calculate_kalman_gain(const Covariance& prior_covariance) const -> KalmanGain { | ||
| const auto jacobian = observation_jacobian(); | ||
|
|
||
| const auto innovation_covariance = | ||
| jacobian * prior_covariance * jacobian.transpose() + context.noise_observation; | ||
|
|
||
| return prior_covariance * jacobian.transpose() | ||
| * innovation_covariance.ldlt().solve(ObservationNoise::Identity()); | ||
| } | ||
|
|
||
| auto a_posteriori_update(const State& prior_state, const Covariance& prior_covariance, | ||
| const KalmanGain& kalman_gain, const ObservationVector& innovation, | ||
| const ObservationJacobian& jacobian) const -> std::pair<State, Covariance> { | ||
|
|
||
| auto posterior_state = State { prior_state }; | ||
| reinterpret_cast<StateVector&>(posterior_state).noalias() += kalman_gain * innovation; | ||
| posterior_state.rotation_angle = util::normalize_angle(posterior_state.rotation_angle); | ||
|
|
||
| const auto complement = Covariance::Identity() - kalman_gain * jacobian; | ||
| auto posterior_covariance = Covariance { }; | ||
| posterior_covariance.noalias() = complement * prior_covariance * complement.transpose(); | ||
| posterior_covariance += | ||
| (kalman_gain * context.noise_observation * kalman_gain.transpose()).eval(); | ||
| posterior_covariance = 0.5 * (posterior_covariance + posterior_covariance.transpose()); | ||
|
|
||
| return { posterior_state, posterior_covariance }; | ||
| } | ||
|
|
||
| auto predict(double dt) noexcept -> void { | ||
| const auto prior_state = predict_state(dt, context.state()); | ||
| const auto prior_covariance = predict_covariance(dt, context.posteriors_covariance); | ||
|
|
||
| context.state() = prior_state; | ||
| context.posteriors_covariance = prior_covariance; | ||
| } | ||
|
|
||
| auto correct(const Armor3d& armor) noexcept -> void { | ||
| { // 切板检测 | ||
| } | ||
|
|
||
| constexpr auto pitch = kPredictedOutpostArmorPitch; | ||
|
|
||
| const auto orientation = armor.orientation.make<Eigen::Quaterniond>(); | ||
| const auto translation = armor.translation.make<Eigen::Vector3d>(); | ||
|
|
||
| const auto backward = Eigen::Vector3d { orientation * Eigen::Vector3d::UnitX() }; | ||
| const auto armor_to_center = Eigen::Vector3d { | ||
| Eigen::AngleAxisd { -pitch, orientation * Eigen::Vector3d::UnitY() } * backward | ||
| }; | ||
|
|
||
| const auto center = Eigen::Vector3d { translation + kOutpostRadius * armor_to_center }; | ||
|
|
||
| const auto top_armor_yaw = | ||
| util::normalize_angle(std::atan2(-armor_to_center.y(), -armor_to_center.x())); | ||
|
|
||
| auto observation = ObservationVector { }; | ||
| observation << center.x(), center.y(), center.z(), top_armor_yaw; | ||
|
|
||
| const auto prior_state = context.state(); | ||
| const auto prior_covariance = context.posteriors_covariance; | ||
|
|
||
| const auto innovation = measure_innovation(observation, prior_state); | ||
| const auto kalman_gain = calculate_kalman_gain(prior_covariance); | ||
| const auto jacobian = observation_jacobian(); | ||
|
|
||
| const auto [posterior_state, posterior_covariance] = | ||
| a_posteriori_update(prior_state, prior_covariance, kalman_gain, innovation, jacobian); | ||
|
|
||
| context.state() = posterior_state; | ||
| context.posteriors_covariance = posterior_covariance; | ||
| } | ||
| }; | ||
|
|
||
| OutpostModel::OutpostModel(const Armor3d& armor) noexcept | ||
| : pimpl { std::make_unique<Impl>() } { | ||
| pimpl->initialize_from(armor); | ||
| } | ||
|
|
||
| OutpostModel::~OutpostModel() noexcept = default; | ||
|
|
||
| auto OutpostModel::predict(double dt) noexcept -> void { pimpl->predict(dt); } | ||
|
|
||
| auto OutpostModel::correct(const Armor3d& armor) noexcept -> void { pimpl->correct(armor); } | ||
|
|
||
| auto OutpostModel::state() noexcept -> State { return pimpl->context.state(); } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| #pragma once | ||
|
|
||
| #include "utility/pimpl.hpp" | ||
| #include "utility/robot/armor.hpp" | ||
|
|
||
| namespace rmcs { | ||
|
|
||
| class OutpostModel { | ||
| RMCS_PIMPL_DEFINITION(OutpostModel) | ||
|
|
||
| public: | ||
| struct State { | ||
| // 前哨站旋转中心在世界坐标系下的位置 | ||
| double x; | ||
| double y; | ||
| double z; | ||
|
|
||
| // 以参考装甲板朝向为基准的角速度与 yaw 角 | ||
| double rotation_speed; | ||
| double rotation_angle; | ||
| }; | ||
|
|
||
| explicit OutpostModel(const Armor3d& armor) noexcept; | ||
|
|
||
| auto predict(double dt) noexcept -> void; | ||
| auto correct(const Armor3d& armor) noexcept -> void; | ||
| auto state() noexcept -> State; | ||
| }; | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| #pragma once | ||
|
|
||
| namespace rmcs { | ||
|
|
||
| class RobotModel { }; | ||
|
|
||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
在重投影前过滤相机后方/非法点
Line 281 之后直接投影会把
z <= 0或非有限坐标也转成 2D 点,调试层会出现误导性的aim_point标记。建议在调用util::reproject_point前先做可见性与有限值检查,不满足时返回std::nullopt。建议修复
// odom (ROS) -> camera (OpenCV) const auto point_ros_odom = point_odom.make<Eigen::Vector3d>(); const auto point_ros_camera = Eigen::Vector3d { q.inverse() * (point_ros_odom - t) }; + if (!point_ros_camera.allFinite() || point_ros_camera.z() <= 0.0) { + return std::nullopt; + } const auto point_opencv = ros2opencv_position(point_ros_camera); const auto point_cv = Point3d { point_opencv };🤖 Prompt for AI Agents