Skip to content

Squash/deformable infantry#79

Open
Yukikaze2233 wants to merge 17 commits into
mainfrom
squash/deformable-infantry
Open

Squash/deformable infantry#79
Yukikaze2233 wants to merge 17 commits into
mainfrom
squash/deformable-infantry

Conversation

@Yukikaze2233
Copy link
Copy Markdown
Member

@Yukikaze2233 Yukikaze2233 commented May 31, 2026

概述

该 PR 对 RMCS 可变形步兵机器人仓库进行了大规模改动,涵盖构建脚本增强、交叉编译行为调整、驱动与硬件组件重组、控制器与低层设备接口重构、配置/启动调整以及若干 UI 与工具库改进。

构建与工具链

  • 在 .script/build-rmcs-cross 中新增 --link-default 选项:
    • 新增辅助函数 check_default_linkable() 与 link_default_base(),用于在成功交叉构建后将 build-cross-/install-cross-/log-cross-* 链接到默认路径(build/install/log),并在目标已存在且非符号链接时报错。
    • 为 arm64 构建分支添加 auto_skip_packages(包含 hikcamera、rmcs_auto_aim_v2),并在非空时将 --packages-skip 追加到 colcon 参数以跳过这些包。
  • 更新 .script/clean-rmcs,扩展为清理 cross 构建相关目录(build-cross-/install-cross-/log-cross-*)。
  • 为 build-rmcs-cross 添加 zsh 补全项 --link-default。
  • docs/zh-cn/cross_build.md 增补交叉构建说明,并记录 --link-default 行为与示例。

配置与启动

  • 新增/修改多份 RMCS 启动与参数 YAML:
    • 新增 deformable-infantry-omni-b.yaml(全向底盘 B 变体配置,较多控制参数与话题映射)。
    • 新增或重构 deformable-infantry-omni.yaml 与 deformable-infantry-steering.yaml(调整组件映射、移除/重构大量悬架/关节相关参数、调整限幅/PID/摩擦轮/热量等参数)。
  • rmcs_bringup/launch/rmcs.launch.py:引入 IncludeLaunchDescription/PythonLaunchDescriptionSource,当 is_automatic 为真时包含 rmcs_auto_aim_v2 的启动。
  • rmcs_bringup/package.xml:添加条件化 exec_depend,使 rmcs_auto_aim_v2 在 $RMCS_TARGET_ARCH != 'arm64' 时作为运行依赖。

硬件驱动与设备接口重组

  • 新增硬件/组件实现:
    • rmcs_core::hardware::DeformableInfantryOmniB(全向底盘 B)——大文件实现底板/顶板/IMU/VT13/遥控/服务与 CAN/UART 处理并导出为插件。
    • rmcs_core::hardware::DeformableInfantrySteering ——替换原有 DeformableInfantryV2 的导出目标,并实现 robot_status 服务等。
  • 重构与整合现有驱动:
    • DeformableInfantry(omni)被重构以集成 Vt13 与 RemoteControl,并调整底板/topboard 行为与几何/半径回退策略。
    • OmniInfantry、omni 相关实现改为基于 librmcs::agent::RmcsBoardLite 并引入 Vt13/RemoteControl。
  • 移除/大幅改动的硬件实现:
    • 完整移除或替换了 Sentry、SteeringHero、SteeringInfantry 等实现(对应源文件实现被删除)。
  • 设备级接口变更与新增:
    • Dr16 接口重构:从依赖 Component 的输出注册改为内部原子状态维护,新增 store_status(std::span)、update_status()、valid() 与若干 noexcept/[[nodiscard]] getters(包括 rotary_knob_switch())。
    • 新增 Vt13 类:UART 帧解析(远控/裁判帧)、CRC 校验、状态访问、模式枚举(Normal/Cine/Sport)等接口。
    • 新增 RemoteControl 组件:基于 Dr16/Vt13 提供注册到 Component 的输出并按模式更新输出值(构造与 update 方法)。
    • Supercap 添加输入 /chassis/supercap/control_enable,类成员与输入注册已新增。
    • 多处硬件板卡/TopBoard/BottomBoard 的构造签名与行为调整以适配上述重构。

控制器与算法层重构

  • 底盘控制器(DeformableChassis):
    • 从“厚控制器”重构为更精简的“编排器”实现,移除大量原有悬架/腿部类型与状态机。
    • 新增 AttitudePidAxis、IMU 标定窗口、主动悬架开合逻辑、关节目标状态初始化、基于速度/加速度限制的物理角度轨迹积分与限幅等。
    • 删除旧的 JointTrajectoryPlanner、SuspensionPhase、相关数据结构与方法。
  • 关节控制器(DeformableJointController):
    • 简化为单一配置的关节伺服:移除 suspension_mode、feedforward、动态输出限幅与 ESO 可选输出等,保留 TD/ESO/NLESF 的核心计算与最小输出接口(measurement_angle、setpoint_angle、control)。
  • 能量与电力控制:
    • ChassisPowerController 新增 supercap_control_enabled_ 输出接口并调整功率上限计算逻辑(以 supercap_control_enabled_ 与 supercap_enabled_ 决定上限),并调整缓冲能量相关常量。

射击与运动控制调整

  • BulletFeederController:将自动瞄准开火许可话题改为 /auto_aim/should_shoot,并据此调整开火许可逻辑与单发重置条件。
  • FrictionWheelController:新增对鼠标滚轮的读取,支持可调摩擦轮工作速度(friction_velocity_min/max 校验),新增 /working_velocity 输出并在运行时根据鼠标滚轮与键盘调整工作速度。

UI 与前端改进

  • 新增 AnimatedToggle 组件:带缓动动画的布尔切换控件(可配置时长、reset/update/value 接口)。
  • CrossHairCircle:新增 set_color/set_x/set_y 与 x()/y() 访问器。
  • DeformableInfantry UI(referee/app/ui):
    • 扩展输入(键盘 ctrl、摩擦轮工作速度/控制速度等),新增动画/时间戳输入以支持 UI 过渡,调整准星/指示器与超级电容能量显示逻辑。
  • StatusRing:新增 calculate_energy_angle 及能量分段映射与配色阈值调整。
  • DeformableChassisTopView:调整后腿渲染半径常量(rear_leg_radius_near_ 112→102,rear_leg_radius_far_ 142→132)。

公共库与工具变更

  • rmcs_utility::RingBuffer 新增 peek_front_n 方法:允许在不推进读指针的情况下批量“窥视”前 n 个可读元素并对其执行回调。

依赖与元数据

  • rmcs_core/CMakeLists.txt:将 librmcs SDK 版本从 v3.1.0 更新为 v3.2.0rc0,并更新对应 URL_HASH。
  • rmcs_core/plugins.xml:移除 rmcs_core::hardware::DeformableInfantryV2,新增 DeformableInfantrySteering 与 DeformableInfantryOmniB,保留 DeformableInfantryOmni。
  • 子模块指针更新:hikcamera、rmcs_auto_aim_v2 的子项目指针被修改(仅更新提交哈希)。

其他注意事项(影响范围)

  • 多个硬件源文件被新增或删除,涉及大量导出插件类的变化(新增导出:DeformableInfantryOmniB、DeformableInfantrySteering;移除导出:Sentry、SteeringHero、SteeringInfantry、原 DeformableInfantryV2 等),可能影响运行时可用组件与包依赖解析。
  • 多处接口签名与成员变更(例如 Dr16 构造与方法签名、ImuBoard/TopBoard 构造签名、JointTrajectoryPlanner 全部移除、DeformableJointController 注册的接口减少)将影响上层调用与二次集成,需要关注 ABI/接口依赖与配置兼容性。

代码规模与审查建议

  • 本 PR 涉及大量新增文件与大规模重构(若干文件数百行变更或被删除/新增),部分改动(底盘控制器、硬件板卡、新增 Vt13/RemoteControl、DeformableInfantryOmniB)属于高风险区域,建议逐模块仔细审查:
    • 确认插件导出与 package.xml 条件依赖的一致性与运行时加载行为。
    • 验证 Dr16/Vt13/RemoteControl 的解析与线程/原子使用正确性、边界条件与时序(UART 数据分段、CRC 校验、有效期判定)。
    • 在仿真或硬件验证环境中逐步验证 DeformableChassis 与 DeformableJointController 的新控制流程与参数(IMU 标定、轨迹积分、限幅与主动悬架逻辑)。
    • 检查交叉编译脚本的链接行为(--link-default)在 CI/本地构建中的幂等性与潜在路径冲突。
    • 关注删除老旧硬件实现对部署平台(存在依赖这些插件的上游配置或运行时)的影响。

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 31, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ae63d5cb-c28f-49f9-8caf-3a72ebb1c785

📥 Commits

Reviewing files that changed from the base of the PR and between ffa3bbc and c27cfbb.

📒 Files selected for processing (3)
  • rmcs_ws/src/rmcs_bringup/config/deformable-infantry-omni-b.yaml
  • rmcs_ws/src/rmcs_bringup/config/deformable-infantry-omni.yaml
  • rmcs_ws/src/rmcs_bringup/config/deformable-infantry-steering.yaml
🚧 Files skipped from review as they are similar to previous changes (2)
  • rmcs_ws/src/rmcs_bringup/config/deformable-infantry-omni.yaml
  • rmcs_ws/src/rmcs_bringup/config/deformable-infantry-omni-b.yaml

Walkthrough

该 PR 同步更新交叉构建脚本与文档、多个子模块指针、ROS2 启动/配置与插件注册;新增与重构远控设备层(Dr16/Vt13/RemoteControl)、新增 DeformableInfantryOmniB,并对底盘控制器、射击/摩擦轮控制、UI 与工具库进行广泛重构与增强。

Changes

单一审阅切片(整体)

Layer / File(s) Summary
全部变更(汇总)
全部修改文件(见PR diff)
包含交叉构建脚本与清理脚本修改,补全与文档更新,多个 ROS2 YAML/launch/package.xml 更改,子模块指针更新;新增/重构硬件设备层(Dr16/Vt13/RemoteControl)、新增 DeformableInfantryOmniB,重构 DeformableInfantryOmni/Steering/OmniInfantry,大幅重构底盘控制器 DeformableChassisChassisPowerControllerDeformableJointController,射击与摩擦轮控制改动,以及 UI/工具库新增与调整。

Estimated code review effort:
🎯 4 (Complex) | ⏱️ ~75 minutes

"兔子写的庆祝诗:"
我啃着代码胡萝卜,眨眨眼看改动多,
底盘变得更轻盈,远控跑得更稳妥,
新的板卡登场啦,UI 也会舞蹈,
链接产物像彩带,测试请别忘跑一遭,
啾——提交成功,大家鼓掌叫好! 🐰✨

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch squash/deformable-infantry
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch squash/deformable-infantry

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
docs/zh-cn/cross_build.md (1)

30-40: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

变体与目标架构的对应关系标注互换,存在误导。

根据 L12-13 的 sysroot 内置关系与 L28“对向架构”规则:交叉编译 arm64 需要 arm64 sysroot(只存在于 linux/amd64 变体),交叉编译 amd64 需要 amd64 sysroot(只存在于 linux/arm64 变体)。因此 L34 与 L40 的变体标注被互换,照此操作会在缺少对应 sysroot 的容器中执行,导致 L91-94 自检失败。

📝 建议修正
 build-rmcs-cross --target-arch arm64

-适用于 linux/arm64latest-full 变体。
+适用于 linux/amd64latest-full 变体。

build-rmcs-cross --target-arch amd64

-适用于 linux/amd64latest-full 变体。
+适用于 linux/arm64latest-full 变体。

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @docs/zh-cn/cross_build.md around lines 30 - 40, 在 docs 文档中把 build-rmcs-cross
示例的变体说明与目标架构写反了:把 "build-rmcs-cross --target-arch arm64" 对应为 "适用于 linux/arm64 的
latest-full 变体" 和 "build-rmcs-cross --target-arch amd64" 对应为 "适用于 linux/amd64 的
latest-full 变体" 需要互换;改为说明交叉编译 arm64 时应使用 linux/amd64 变体,交叉编译 amd64 时应使用
linux/arm64 变体(修正与 L12-13 sysroot 关系和 L91-94 自检预期一致),更新文中与命令字符串
"build-rmcs-cross --target-arch arm64" 与 "build-rmcs-cross --target-arch amd64"
相邻的变体描述即可。


</details>

</blockquote></details>
<details>
<summary>rmcs_ws/src/rmcs_core/src/controller/shooting/friction_wheel_controller.cpp (1)</summary><blockquote>

`113-134`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**last_* 状态更新应移出 `switch_right != Switch::DOWN` 分支**。当前 `last_switch_right_ / last_switch_left_ / last_keyboard_` 仅在 `if (switch_right != Switch::DOWN)` 内更新;当 `switch_right == Switch::DOWN` 时跳过更新,随后一旦离开 `DOWN`,边沿检测会基于过期的 `last_*` 可能触发(如 `keyboard.v` 上升沿、`MIDDLE -> UP` 切换)。对照 `bullet_feeder_controller_17mm.cpp` 和 `deformable_chassis.cpp`,它们都在 `update()` 末尾/循环外无条件刷新 `last_*`。建议把这三行移动到该 `if` 外。  

<details>
<summary>🐛 建议将 last_* 更新移出内层分支</summary>

```diff
             update_friction_velocities();
             update_friction_status();
             if (*friction_jammed_)
                 RCLCPP_INFO(logger_, "Friction Jammed!");
             if (*bullet_fired_)
                 RCLCPP_INFO(logger_, "Bullet Fired!");
-
-            last_switch_right_ = switch_right;
-            last_switch_left_ = switch_left;
-            last_keyboard_ = keyboard;
         }
+
+        last_switch_right_ = switch_right;
+        last_switch_left_ = switch_left;
+        last_keyboard_ = keyboard;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rmcs_ws/src/rmcs_core/src/controller/shooting/friction_wheel_controller.cpp`
around lines 113 - 134, The three "last" state updates (last_switch_right_,
last_switch_left_, last_keyboard_) are only updated inside the if (switch_right
!= Switch::DOWN) block which causes stale edge-detection when switch_right ==
DOWN; move the lines that set last_switch_right_ = switch_right;
last_switch_left_ = switch_left; last_keyboard_ = keyboard; so they execute
unconditionally at the end of the update() path (outside that inner if) in
friction_wheel_controller.cpp to mirror the behavior used in
bullet_feeder_controller_17mm.cpp and deformable_chassis.cpp.
🧹 Nitpick comments (1)
rmcs_ws/src/rmcs_core/src/referee/app/ui/widget/status_ring.hpp (1)

248-272: ⚡ Quick win

update_supercapupdate_supercap_energy 的配色分支完全重复。

两个函数中基于 value/enable 的三段着色逻辑一字不差,可抽取一个私有辅助(如 supercap_color(value, enable))统一维护,避免后续两处阈值不一致。

♻️ 建议的去重方式
+    static Shape::Color supercap_color(double value, bool enable) {
+        if (value > 20.0)
+            return enable ? Shape::Color::CYAN : Shape::Color::GREEN;
+        if (value > 13.5)
+            return enable ? Shape::Color::YELLOW : Shape::Color::ORANGE;
+        return enable ? Shape::Color::PURPLE : Shape::Color::PINK;
+    }
+
     void update_supercap(double value, bool enable) {
         auto angle = 275 + calculate_angle(value, 8.0, supercap_limit_) + 1;
         supercap_status_.set_angle_end(static_cast<uint16_t>(angle));
-
-        if (value > 20.0) {
-            supercap_status_.set_color(enable ? Shape::Color::CYAN : Shape::Color::GREEN);
-        } else if (value > 13.5) {
-            supercap_status_.set_color(enable ? Shape::Color::YELLOW : Shape::Color::ORANGE);
-        } else {
-            supercap_status_.set_color(enable ? Shape::Color::PURPLE : Shape::Color::PINK);
-        }
+        supercap_status_.set_color(supercap_color(value, enable));
     }

     void update_supercap_energy(double value, bool enable, double cutoff_voltage) {
         auto angle = 275 + calculate_energy_angle(value, cutoff_voltage, supercap_limit_) + 1;
         supercap_status_.set_angle_end(static_cast<uint16_t>(angle));
-
-        if (value > 20.0) {
-            supercap_status_.set_color(enable ? Shape::Color::CYAN : Shape::Color::GREEN);
-        } else if (value > 13.5) {
-            supercap_status_.set_color(enable ? Shape::Color::YELLOW : Shape::Color::ORANGE);
-        } else {
-            supercap_status_.set_color(enable ? Shape::Color::PURPLE : Shape::Color::PINK);
-        }
+        supercap_status_.set_color(supercap_color(value, enable));
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rmcs_ws/src/rmcs_core/src/referee/app/ui/widget/status_ring.hpp` around lines
248 - 272, Both update_supercap(...) and update_supercap_energy(...) duplicate
the same three-branch color logic; extract a private helper like
supercap_color(double value, bool enable) that returns a Shape::Color based on
the existing thresholds (>20.0, >13.5, else) and the enable flag, then replace
the duplicated if/else blocks in both update_supercap and update_supercap_energy
with a single call to supercap_status_.set_color(supercap_color(value, enable));
keep the threshold values and ternary mapping (CYAN/GREEN, YELLOW/ORANGE,
PURPLE/PINK) exactly as-is to preserve behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rmcs_ws/src/rmcs_bringup/config/deformable-infantry-steering.yaml`:
- Line 90: 在 deformable-infantry-steering.yaml 中的 upper_limit 值为
-0.65(radians)但注释写为 "-35 deg" 不一致;请将注释与实际数值对齐(例如改为 "-37.2 deg" 或把数值改为 -0.610865
来匹配 -35°),以保持 upper_limit: -0.65 与注释一致并避免误导机械限位与 PID 调参。

In `@rmcs_ws/src/rmcs_bringup/launch/rmcs.launch.py`:
- Around line 54-63: The IncludeLaunchDescription for rmcs_auto_aim_v2 is being
appended unconditionally to entities; move that IncludeLaunchDescription (the
block that constructs
IncludeLaunchDescription(PythonLaunchDescriptionSource([FindPackageShare('rmcs_auto_aim_v2'),
'/launch.py']))) into the existing if is_automatic: branch so it is only
appended when is_automatic is true, removing the unconditional append to avoid
adding the auto-aim dependency in manual mode.

In `@rmcs_ws/src/rmcs_core/src/hardware/deformable-infantry-omni-b.cpp`:
- Around line 501-506: 当前代码在收到 can_id == 0x300 时无论 data.can_data.size() 是否为 8
都会把 supercap_status_received_ 置为 true,导致 update() 在收到非法帧时开始发布旧值/默认值。修复方法:在处理
0x300 帧的分支中,把对 latest_supercap_status_.store(...)、supercap_.store_status(...) 和
supercap_status_received_.store(...) 的调用都包到 data.can_data.size() == 8 的条件块内(使用
device::CanPacket8{data.can_data}、latest_supercap_status_、supercap_.store_status
和 supercap_status_received_ 这几个标识符),只有在确认为合法 8 字节帧时才设置标志;对非 8 字节的帧则忽略,不改变
supercap_status_received_。

In `@rmcs_ws/src/rmcs_core/src/hardware/device/dr16.hpp`:
- Around line 22-42: 当前实现先写入 last_receive_time_ 再逐个原子写入
data_part1_/data_part2_/data_part3_,会导致 update_status()/valid() 并发读取到跨帧拼接的数据;在
store_status 中先把 uart_data 解析到本地临时变量(例如 local_part1/part2/part3
或临时缓冲区),然后按原子方式一次性写回共享原子变量:先写
data_part1_/data_part2_/data_part3_(memory_order_relaxed),最后再写
last_receive_time_;确保使用的符号为 store_status, last_receive_time_, data_part1_,
data_part2_, data_part3_, update_status(), valid() 以避免读到混合帧快照。
- Around line 33-42: The concurrent snapshot can be torn because
Dr16::store_status() stores data_part1_/data_part2_/data_part3_ with
memory_order::relaxed and then updates last_receive_time_ also with relaxed,
while valid() and update_status() read last_receive_time_ relaxed; fix by
keeping payload stores (data_part1_/2_/3_) as relaxed but make the final
last_receive_time_ store use release semantics (store(...,
std::memory_order::release)) so it acts as a publish; on the reading side in
valid() and update_status() load last_receive_time_ with acquire semantics
(load(..., std::memory_order::acquire)) and only read the payload fields after
seeing a newer timestamp to ensure you observe a consistent snapshot for that
timestamp/sequence number, leaving atomic types unchanged.

In `@rmcs_ws/src/rmcs_core/src/hardware/device/remote_control.hpp`:
- Around line 44-85: The update() branching currently prioritizes dr16_ and
prevents vt13_ mode_switch() (kCine/kSport) from taking effect; change the
selection order to first check vt13_.valid() and vt13_.mode_switch() to apply
VT13-specific outputs, then if VT13 is invalid fall back to dr16_ only when
dr16_.valid(), and finally when neither source is valid explicitly write
zero/UNKNOWN/default values to all outputs (switch_right_output_,
switch_left_output_, joystick_*_output_, mouse_velocity_output_,
mouse_wheel_output_, mouse_output_, keyboard_output_, rotary_knob_output_,
rotary_knob_switch_output_) so stale DR16 state is never propagated. Ensure you
reference vt13_.mode_switch(), vt13_.valid(), dr16_.valid(), and update
assignments accordingly.

In `@rmcs_ws/src/rmcs_core/src/hardware/device/supercap.hpp`:
- Around line 31-32: The newly-registered input
"/chassis/supercap/control_enable" (supercap_control_enabled_) is not used in
generate_command() or generate_disable_command(), so the external enable signal
doesn't affect outgoing CAN commands; update the command.enabled calculation
inside generate_command() and generate_disable_command() to include
supercap_control_enabled_ (e.g., combine it with existing enable logic before
populating command.enabled) so the registered input participates in deciding
command.enabled and flows through to the CAN payload.

In `@rmcs_ws/src/rmcs_core/src/referee/app/ui/deformable_infantry_ui.cpp`:
- Around line 142-147: The chassis direction indicator is hard-coded to
set_angle(0, 30) in update_chassis_direction_indicator(), which prevents it
reflecting the actual chassis heading; update this function to compute the
indicator angle from the current chassis_angle_ (and any needed offset relative
to the gimbal/yaw or chassis_mode_), then call
chassis_direction_indicator_.set_angle(computed_angle, 30) so the UI reflects
real heading; if the fixed-forward behavior was intentional instead, add a clear
comment above update_chassis_direction_indicator() explaining that the indicator
is deliberately pinned to 0° and why.

---

Outside diff comments:
In `@docs/zh-cn/cross_build.md`:
- Around line 30-40: 在 docs 文档中把 build-rmcs-cross 示例的变体说明与目标架构写反了:把
"build-rmcs-cross --target-arch arm64" 对应为 "适用于 linux/arm64 的 latest-full 变体" 和
"build-rmcs-cross --target-arch amd64" 对应为 "适用于 linux/amd64 的 latest-full 变体"
需要互换;改为说明交叉编译 arm64 时应使用 linux/amd64 变体,交叉编译 amd64 时应使用 linux/arm64 变体(修正与
L12-13 sysroot 关系和 L91-94 自检预期一致),更新文中与命令字符串 "build-rmcs-cross --target-arch
arm64" 与 "build-rmcs-cross --target-arch amd64" 相邻的变体描述即可。

In `@rmcs_ws/src/rmcs_core/src/controller/shooting/friction_wheel_controller.cpp`:
- Around line 113-134: The three "last" state updates (last_switch_right_,
last_switch_left_, last_keyboard_) are only updated inside the if (switch_right
!= Switch::DOWN) block which causes stale edge-detection when switch_right ==
DOWN; move the lines that set last_switch_right_ = switch_right;
last_switch_left_ = switch_left; last_keyboard_ = keyboard; so they execute
unconditionally at the end of the update() path (outside that inner if) in
friction_wheel_controller.cpp to mirror the behavior used in
bullet_feeder_controller_17mm.cpp and deformable_chassis.cpp.

---

Nitpick comments:
In `@rmcs_ws/src/rmcs_core/src/referee/app/ui/widget/status_ring.hpp`:
- Around line 248-272: Both update_supercap(...) and update_supercap_energy(...)
duplicate the same three-branch color logic; extract a private helper like
supercap_color(double value, bool enable) that returns a Shape::Color based on
the existing thresholds (>20.0, >13.5, else) and the enable flag, then replace
the duplicated if/else blocks in both update_supercap and update_supercap_energy
with a single call to supercap_status_.set_color(supercap_color(value, enable));
keep the threshold values and ternary mapping (CYAN/GREEN, YELLOW/ORANGE,
PURPLE/PINK) exactly as-is to preserve behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b7185d06-f616-488a-bed6-d9c8b9de4cd2

📥 Commits

Reviewing files that changed from the base of the PR and between 7578778 and 8b46d9c.

📒 Files selected for processing (39)
  • .script/build-rmcs-cross
  • .script/clean-rmcs
  • .script/complete/_build-rmcs-cross
  • docs/zh-cn/cross_build.md
  • rmcs_ws/src/hikcamera
  • rmcs_ws/src/rmcs_auto_aim_v2
  • rmcs_ws/src/rmcs_bringup/config/deformable-infantry-omni-b.yaml
  • rmcs_ws/src/rmcs_bringup/config/deformable-infantry-omni.yaml
  • rmcs_ws/src/rmcs_bringup/config/deformable-infantry-steering.yaml
  • rmcs_ws/src/rmcs_bringup/launch/rmcs.launch.py
  • rmcs_ws/src/rmcs_bringup/package.xml
  • rmcs_ws/src/rmcs_core/CMakeLists.txt
  • rmcs_ws/src/rmcs_core/plugins.xml
  • rmcs_ws/src/rmcs_core/src/controller/chassis/chassis_power_controller.cpp
  • rmcs_ws/src/rmcs_core/src/controller/chassis/deformable_chassis.cpp
  • rmcs_ws/src/rmcs_core/src/controller/chassis/deformable_joint_controller.cpp
  • rmcs_ws/src/rmcs_core/src/controller/chassis/deformable_joint_layer.hpp
  • rmcs_ws/src/rmcs_core/src/controller/chassis/deformable_omni_controller.cpp
  • rmcs_ws/src/rmcs_core/src/controller/chassis/deformable_steering_controller.cpp
  • rmcs_ws/src/rmcs_core/src/controller/shooting/bullet_feeder_controller_17mm.cpp
  • rmcs_ws/src/rmcs_core/src/controller/shooting/friction_wheel_controller.cpp
  • rmcs_ws/src/rmcs_core/src/hardware/deformable-infantry-omni-b.cpp
  • rmcs_ws/src/rmcs_core/src/hardware/deformable-infantry-omni.cpp
  • rmcs_ws/src/rmcs_core/src/hardware/deformable-infantry-steering.cpp
  • rmcs_ws/src/rmcs_core/src/hardware/device/dr16.hpp
  • rmcs_ws/src/rmcs_core/src/hardware/device/remote_control.hpp
  • rmcs_ws/src/rmcs_core/src/hardware/device/supercap.hpp
  • rmcs_ws/src/rmcs_core/src/hardware/device/vt13.hpp
  • rmcs_ws/src/rmcs_core/src/hardware/omni_infantry.cpp
  • rmcs_ws/src/rmcs_core/src/hardware/sentry.cpp
  • rmcs_ws/src/rmcs_core/src/hardware/steering-hero.cpp
  • rmcs_ws/src/rmcs_core/src/hardware/steering-infantry.cpp
  • rmcs_ws/src/rmcs_core/src/referee/app/ui/deformable_infantry_ui.cpp
  • rmcs_ws/src/rmcs_core/src/referee/app/ui/widget/animated_toggle.hpp
  • rmcs_ws/src/rmcs_core/src/referee/app/ui/widget/crosshair_circle.hpp
  • rmcs_ws/src/rmcs_core/src/referee/app/ui/widget/deformable_chassis_top_view.hpp
  • rmcs_ws/src/rmcs_core/src/referee/app/ui/widget/status_ring.hpp
  • rmcs_ws/src/rmcs_core/src/referee/status.cpp
  • rmcs_ws/src/rmcs_utility/include/rmcs_utility/ring_buffer.hpp
💤 Files with no reviewable changes (4)
  • rmcs_ws/src/rmcs_core/src/hardware/sentry.cpp
  • rmcs_ws/src/rmcs_core/src/hardware/steering-hero.cpp
  • rmcs_ws/src/rmcs_core/src/hardware/steering-infantry.cpp
  • rmcs_ws/src/rmcs_core/src/controller/chassis/deformable_joint_layer.hpp

friction: 1.65 # Nm/(rad/s)

upper_limit: -0.61 # -35 deg
upper_limit: -0.65 # -35 deg
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

同步更新角度注释。

-0.65 rad 约等于 -37.2°,不是这里写的 -35 deg。这会误导后续机械限位和 PID 调参。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rmcs_ws/src/rmcs_bringup/config/deformable-infantry-steering.yaml` at line
90, 在 deformable-infantry-steering.yaml 中的 upper_limit 值为 -0.65(radians)但注释写为
"-35 deg" 不一致;请将注释与实际数值对齐(例如改为 "-37.2 deg" 或把数值改为 -0.610865 来匹配 -35°),以保持
upper_limit: -0.65 与注释一致并避免误导机械限位与 PID 调参。

Comment on lines 54 to +63
if is_automatic:
pass

entities.append(
IncludeLaunchDescription(
PythonLaunchDescriptionSource([
FindPackageShare('rmcs_auto_aim_v2'), '/launch.py'
])
)
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

把 auto aim 的包含逻辑放回 if is_automatic 分支里。

现在 IncludeLaunchDescription(...) 是无条件追加的,手动机器人配置也会启动 rmcs_auto_aim_v2。这和前面的 is_automatic 判定相矛盾,还会给非自动模式引入额外依赖。

💡 建议修改
-        if is_automatic:
-            pass
-    
-        entities.append(
-            IncludeLaunchDescription(
-                PythonLaunchDescriptionSource([
-                    FindPackageShare('rmcs_auto_aim_v2'), '/launch.py'
-                ])
-            )
-        )
+        if is_automatic:
+            entities.append(
+                IncludeLaunchDescription(
+                    PythonLaunchDescriptionSource(
+                        [FindPackageShare("rmcs_auto_aim_v2"), "/launch.py"]
+                    )
+                )
+            )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rmcs_ws/src/rmcs_bringup/launch/rmcs.launch.py` around lines 54 - 63, The
IncludeLaunchDescription for rmcs_auto_aim_v2 is being appended unconditionally
to entities; move that IncludeLaunchDescription (the block that constructs
IncludeLaunchDescription(PythonLaunchDescriptionSource([FindPackageShare('rmcs_auto_aim_v2'),
'/launch.py']))) into the existing if is_automatic: branch so it is only
appended when is_automatic is true, removing the unconditional append to avoid
adding the auto-aim dependency in manual mode.

Comment on lines +501 to +506
} else if (data.can_id == 0x300) {
if (data.can_data.size() == 8)
latest_supercap_status_.store(
device::CanPacket8{data.can_data}, std::memory_order_relaxed);
supercap_.store_status(data.can_data);
supercap_status_received_.store(true, std::memory_order_relaxed);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

只在收到合法 supercap 帧后再置 supercap_status_received_

现在即使 data.can_data.size() != 8supercap_status_received_ 也会被置为 true。之后 update() 会开始发布旧值/默认值,并把它当成“已经收到状态”。

建议修改
             } else if (data.can_id == 0x300) {
-                if (data.can_data.size() == 8)
+                if (data.can_data.size() == 8) {
                     latest_supercap_status_.store(
                         device::CanPacket8{data.can_data}, std::memory_order_relaxed);
+                    supercap_status_received_.store(true, std::memory_order_relaxed);
+                }
                 supercap_.store_status(data.can_data);
-                supercap_status_received_.store(true, std::memory_order_relaxed);
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rmcs_ws/src/rmcs_core/src/hardware/deformable-infantry-omni-b.cpp` around
lines 501 - 506, 当前代码在收到 can_id == 0x300 时无论 data.can_data.size() 是否为 8 都会把
supercap_status_received_ 置为 true,导致 update() 在收到非法帧时开始发布旧值/默认值。修复方法:在处理 0x300
帧的分支中,把对 latest_supercap_status_.store(...)、supercap_.store_status(...) 和
supercap_status_received_.store(...) 的调用都包到 data.can_data.size() == 8 的条件块内(使用
device::CanPacket8{data.can_data}、latest_supercap_status_、supercap_.store_status
和 supercap_status_received_ 这几个标识符),只有在确认为合法 8 字节帧时才设置标志;对非 8 字节的帧则忽略,不改变
supercap_status_received_。

Comment on lines +22 to 42
void store_status(std::span<const std::byte> uart_data) {
if (uart_data.size() != kStatusSize)
return;

// Avoid using reinterpret_cast here because it does not account for pointer alignment.
// Dr16DataPart structures are aligned, and using reinterpret_cast on potentially unaligned
// uart_data can cause undefined behavior on architectures that enforce strict alignment
// requirements (e.g., ARM).
// Directly accessing unaligned memory through a casted pointer can lead to crashes,
// inefficiencies, or incorrect data reads. Instead, std::memcpy safely copies the data from
// unaligned memory to properly aligned structures without violating alignment or strict
// aliasing rules.
last_receive_time_.store(Clock::now(), std::memory_order_relaxed);

auto* cursor = uart_data.data();

uint64_t part1{};
std::memcpy(&part1, uart_data, 6);
uart_data += 6;
std::memcpy(&part1, cursor, kPart1Size);
cursor += kPart1Size;
data_part1_.store(part1, std::memory_order::relaxed);

uint64_t part2{};
std::memcpy(&part2, uart_data, 8);
uart_data += 8;
std::memcpy(&part2, cursor, kPart2Size);
cursor += kPart2Size;
data_part2_.store(part2, std::memory_order::relaxed);

uint32_t part3{};
std::memcpy(&part3, uart_data, 4);
uart_data += 4;
std::memcpy(&part3, cursor, kPart3Size);
data_part3_.store(part3, std::memory_order::relaxed);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

避免发布跨帧拼接的遥控状态。

这里把一帧拆成 3 个独立原子写入,而且先提交 last_receive_time_。如果 UART 回调和 update_status() 并发,valid() 可能已经变成 true,但 update_status() 读到的仍然可能是新旧分片混合的快照,摇杆/鼠标/键盘会来自不同帧。

Also applies to: 45-88

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rmcs_ws/src/rmcs_core/src/hardware/device/dr16.hpp` around lines 22 - 42,
当前实现先写入 last_receive_time_ 再逐个原子写入 data_part1_/data_part2_/data_part3_,会导致
update_status()/valid() 并发读取到跨帧拼接的数据;在 store_status 中先把 uart_data 解析到本地临时变量(例如
local_part1/part2/part3 或临时缓冲区),然后按原子方式一次性写回共享原子变量:先写
data_part1_/data_part2_/data_part3_(memory_order_relaxed),最后再写
last_receive_time_;确保使用的符号为 store_status, last_receive_time_, data_part1_,
data_part2_, data_part3_, update_status(), valid() 以避免读到混合帧快照。

Comment on lines 33 to 42
data_part1_.store(part1, std::memory_order::relaxed);

uint64_t part2{};
std::memcpy(&part2, uart_data, 8);
uart_data += 8;
std::memcpy(&part2, cursor, kPart2Size);
cursor += kPart2Size;
data_part2_.store(part2, std::memory_order::relaxed);

uint32_t part3{};
std::memcpy(&part3, uart_data, 4);
uart_data += 4;
std::memcpy(&part3, cursor, kPart3Size);
data_part3_.store(part3, std::memory_order::relaxed);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In standard C++, is std::memory_order::relaxedvalid forstd::atomic::load/store, or should the enumerator be std::memory_order_relaxed?

💡 Result:

In standard C++, you can use either of these forms for the memory_order argument of std::atomic::load / std::atomic::store: 1) std::memory_order::relaxed 2) std::memory_order_relaxed Both are valid because std::memory_order is the enumeration type, with enumerator relaxed, and the library also provides inline constexpr constants like std::memory_order_relaxed that are defined in terms of that enumerator.[1][2] Also, the load/store APIs take an argument of type std::memory_order (by default memory_order::seq_cst), and have a precondition that the order argument is not release/acq_rel for load (and not consume/acquire/acq_rel for store), but relaxed satisfies these constraints.[3][4] So if your question is whether std::memory_order::relaxed is valid vs whether you should use std::memory_order_relaxed: both compile, and the standard library exposes both spellings; the “relaxed” semantics are the same.[1][2][3]

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
file="rmcs_ws/src/rmcs_core/src/hardware/device/dr16.hpp"
if [ ! -f "$file" ]; then
  echo "Missing file: $file"
  exit 1
fi
# Show the relevant region around the cited lines and nearby status update logic
nl -ba "$file" | sed -n '1,120p'

Repository: Alliance-Algorithm/RMCS

Length of output: 110


🏁 Script executed:

#!/bin/bash
set -euo pipefail
file="rmcs_ws/src/rmcs_core/src/hardware/device/dr16.hpp"
if [ ! -f "$file" ]; then
  echo "Missing file: $file"
  exit 1
fi
nl -ba "$file" | sed -n '1,220p'

Repository: Alliance-Algorithm/RMCS

Length of output: 110


🏁 Script executed:

#!/bin/bash
set -euo pipefail
file="rmcs_ws/src/rmcs_core/src/hardware/device/dr16.hpp"
nl -ba "$file" | sed -n '1,260p'

Repository: Alliance-Algorithm/RMCS

Length of output: 110


🏁 Script executed:

#!/bin/bash
set -euo pipefail
file="rmcs_ws/src/rmcs_core/src/hardware/device/dr16.hpp"
ls -la "$file"
cat -n "$file" | sed -n '1,220p'

Repository: Alliance-Algorithm/RMCS

Length of output: 9511


纠正:std::memory_order::relaxed 无需改拼写;保留并重点处理并发快照一致性问题。

  • std::memory_order::relaxed 用在 std::atomic::load/store 里是合法且语义等价于 std::memory_order_relaxed,不属于编译阻塞问题(应移除对“改为 _relaxed 才能编译”的修改建议)。
  • 并发一致性:Dr16::store_status()last_receive_time_ 再依次写 data_part1_/2_/3_,且全部使用 memory_order::relaxedvalid() 只看 last_receive_time_(也为 relaxed),因此可能出现“时间戳已更新但 payload 仍是旧的/混合的”视图(尤其当 store_status()valid()/update_status() 在不同线程并发时)。建议把 last_receive_time_ 作为发布信号:先写三段 payload,再用 release 语义发布时间戳;读取侧用 acquire(并可在 update_status() 中以时间戳/序列号保证读取到的是同一轮快照)。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rmcs_ws/src/rmcs_core/src/hardware/device/dr16.hpp` around lines 33 - 42, The
concurrent snapshot can be torn because Dr16::store_status() stores
data_part1_/data_part2_/data_part3_ with memory_order::relaxed and then updates
last_receive_time_ also with relaxed, while valid() and update_status() read
last_receive_time_ relaxed; fix by keeping payload stores (data_part1_/2_/3_) as
relaxed but make the final last_receive_time_ store use release semantics
(store(..., std::memory_order::release)) so it acts as a publish; on the reading
side in valid() and update_status() load last_receive_time_ with acquire
semantics (load(..., std::memory_order::acquire)) and only read the payload
fields after seeing a newer timestamp to ensure you observe a consistent
snapshot for that timestamp/sequence number, leaving atomic types unchanged.

Comment on lines +44 to +85
void update() {
if (dr16_.valid() || !vt13_.valid() || vt13_.mode_switch() == Vt13::ModeSwitch::kNormal) {
*switch_right_output_ = dr16_.switch_right();
*switch_left_output_ = dr16_.switch_left();

*joystick_right_output_ = dr16_.joystick_right();
*joystick_left_output_ = dr16_.joystick_left();

*mouse_velocity_output_ = dr16_.mouse_velocity();
*mouse_wheel_output_ = dr16_.mouse_wheel();

*mouse_output_ = dr16_.mouse();
*keyboard_output_ = dr16_.keyboard();
} else if (vt13_.mode_switch() == Vt13::ModeSwitch::kCine) {
*switch_right_output_ = rmcs_msgs::Switch::DOWN;
*switch_left_output_ = rmcs_msgs::Switch::DOWN;

*joystick_right_output_ = Eigen::Vector2d::Zero();
*joystick_left_output_ = Eigen::Vector2d::Zero();

*mouse_velocity_output_ = Eigen::Vector2d::Zero();
*mouse_wheel_output_ = 0;

*mouse_output_ = rmcs_msgs::Mouse::zero();
*keyboard_output_ = rmcs_msgs::Keyboard::zero();
} else if (vt13_.mode_switch() == Vt13::ModeSwitch::kSport) {
*switch_right_output_ = rmcs_msgs::Switch::MIDDLE;
*switch_left_output_ = rmcs_msgs::Switch::MIDDLE;

*joystick_right_output_ = vt13_.joystick_right();
*joystick_left_output_ = vt13_.joystick_left();

*mouse_velocity_output_ = vt13_.mouse_velocity();
*mouse_wheel_output_ = vt13_.mouse_wheel();

*mouse_output_ = vt13_.mouse();
*keyboard_output_ = vt13_.keyboard();
}

*rotary_knob_output_ = dr16_.rotary_knob();
*rotary_knob_switch_output_ = dr16_.rotary_knob_switch();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

当前分支顺序会让 VT13 的模式切换基本失效。

只要 dr16_.valid() 为 true,第一个分支就会先命中,kCine/kSport 永远不会生效;而当两个源都无效时,!vt13_.valid() 又会继续下发旧的 DR16 状态。建议先按 vt13_.valid() + mode_switch() 选源,再在 VT13 无效时回退到有效的 DR16,最后显式输出零值/UNKNOWN

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rmcs_ws/src/rmcs_core/src/hardware/device/remote_control.hpp` around lines 44
- 85, The update() branching currently prioritizes dr16_ and prevents vt13_
mode_switch() (kCine/kSport) from taking effect; change the selection order to
first check vt13_.valid() and vt13_.mode_switch() to apply VT13-specific
outputs, then if VT13 is invalid fall back to dr16_ only when dr16_.valid(), and
finally when neither source is valid explicitly write zero/UNKNOWN/default
values to all outputs (switch_right_output_, switch_left_output_,
joystick_*_output_, mouse_velocity_output_, mouse_wheel_output_, mouse_output_,
keyboard_output_, rotary_knob_output_, rotary_knob_switch_output_) so stale DR16
state is never propagated. Ensure you reference vt13_.mode_switch(),
vt13_.valid(), dr16_.valid(), and update assignments accordingly.

Comment on lines +31 to +32
command_component.register_input(
"/chassis/supercap/control_enable", supercap_control_enabled_);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

control_enable 目前是死输入。

新注册的 /chassis/supercap/control_enable 没有参与 generate_command()/generate_disable_command(),所以上游新增的使能控制现在不会落到 CAN 命令上。至少要把它并入 command.enabled 的计算,否则这层接口契约是断的。

一个直接的修正方向
-        command.enabled = *chassis_output_status_;
+        command.enabled = *chassis_output_status_ && *supercap_control_enabled_;

Also applies to: 54-78

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rmcs_ws/src/rmcs_core/src/hardware/device/supercap.hpp` around lines 31 - 32,
The newly-registered input "/chassis/supercap/control_enable"
(supercap_control_enabled_) is not used in generate_command() or
generate_disable_command(), so the external enable signal doesn't affect
outgoing CAN commands; update the command.enabled calculation inside
generate_command() and generate_disable_command() to include
supercap_control_enabled_ (e.g., combine it with existing enable logic before
populating command.enabled) so the registered input participates in deciding
command.enabled and flows through to the CAN payload.

Comment on lines 142 to 147
void update_chassis_direction_indicator() {
auto chassis_mode = *chassis_mode_;

auto to_referee_angle = [](double angle) {
return static_cast<int>(
std::round((2 * std::numbers::pi - angle) / std::numbers::pi * 180));
};
chassis_direction_indicator_.set_color(chassis_direction_indicator_color(chassis_mode));
chassis_direction_indicator_.set_angle(to_referee_angle(*chassis_angle_), 30);
chassis_direction_indicator_.set_angle(0, 30);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

RoboMaster referee UI chassis direction indicator typical behavior gimbal relative

💡 Result:

In the RoboMaster competition environment, the referee system's client user interface (UI) typically includes a radar or map view that provides situational awareness to the operator. Regarding the orientation of the robot, the "chassis direction" indicator serves to help the operator understand the robot's current heading relative to its gimbal (or the arena). While specific UI implementations can vary by season and competition category (e.g., Collegiate vs. Youth), the chassis direction indicator in the referee UI is designed to function as follows: 1. Relationship to Gimbal: The indicator typically displays the chassis orientation relative to the gimbal's current yaw angle. Because robots often operate in "Gimbal Follow" mode—where the chassis automatically rotates to follow the gimbal's yaw—the UI provides a visual representation of the offset between the two. 2. Visual Representation: On the operator's interface, this is usually depicted as a directional icon (often a triangle or arrow) representing the chassis, overlaid with a representation of the gimbal's aim (often a line or cone). 3. "Gimbal Relative" Behavior: When the UI is in a gimbal-relative mode, the indicator shows the gimbal's aim direction as fixed (e.g., pointing straight up or forward on the UI), while the chassis indicator rotates to show how many degrees the chassis has deviated from the gimbal's centerline. Conversely, in a chassis-centric view, the chassis is often fixed, and the gimbal orientation rotates around it. It is important to distinguish between the referee system UI (which displays data reported by the robot to the server) and the onboard robot control logic. On the robot side, "Gimbal Lead" or "Chassis Follow" modes determine the physical mechanical rotation [1]. The referee UI merely visualizes the telemetry data sent by the robot's main controller regarding its yaw axis state [2][3]. Operators should refer to the specific "Referee System Interface Protocol" document provided for the current competition season, as the data packet structures for custom UI elements are updated annually to reflect changes in competition rules [4][3].

Citations:


底盘方向指示器固定为 set_angle(0, 30),需确认是否有意

  • 目前实现将角度写死,不再反映底盘朝向;而 RoboMaster 裁判系统客户端的“chassis direction”通常用于展示机器人航向/与云台的相对偏移。
  • 若确为“始终指向正前方”,建议补充注释说明;否则应改为基于 chassis_angle_ 计算并设置角度。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rmcs_ws/src/rmcs_core/src/referee/app/ui/deformable_infantry_ui.cpp` around
lines 142 - 147, The chassis direction indicator is hard-coded to set_angle(0,
30) in update_chassis_direction_indicator(), which prevents it reflecting the
actual chassis heading; update this function to compute the indicator angle from
the current chassis_angle_ (and any needed offset relative to the gimbal/yaw or
chassis_mode_), then call chassis_direction_indicator_.set_angle(computed_angle,
30) so the UI reflects real heading; if the fixed-forward behavior was
intentional instead, add a clear comment above
update_chassis_direction_indicator() explaining that the indicator is
deliberately pinned to 0° and why.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

2 participants