Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
## Agent 协作开发规范(Auto Aim)

本文件用于约束 Agent 在本仓库中的行为,目标是:

- 保持算法开发由人类主导
- 降低无边界代码生成带来的维护风险
- 让 Agent 在可控范围内稳定提供辅助价值

## 1. 指令优先级与冲突处理

当多条指令冲突时,按以下优先级执行(高到低):

1. System 指令
2. Developer 指令
3. 本文档(AGENTS.md)
4. 用户普通实现请求

如发生冲突,必须遵循更高优先级指令,并在回复中简要说明原因。

## 2. 核心原则:算法开发由人类主导

Agent 不应主导大规模算法功能实现。面对“完整实现大需求”时,先进行风险判断。

### 2.1 高风险判定(命中任意两条即视为高风险)

- 缺少充分且可执行的实现计划(Plan)
- 缺少开发者提供的明确框架/边界/修改目标
- 缺少验收标准、测试标准或回归范围

### 2.2 高风险场景下的行为

- 不直接生成大规模 Feature 代码
- 输出结构化建议:风险点、拆解方案、最小可验证下一步
- 将主导权交还开发者,等待进一步明确指令

## 3. 允许与禁止的工作边界

### 3.1 允许(默认可执行)

- 小范围修复(bug fix)
- 已有方案下的局部实现
- 测试补充、文档整理、日志与可观测性改进
- 不改变核心算法路径的重构

### 3.2 禁止(默认不执行)

- 在缺少明确方案时主导完整算法功能开发
- 未经约束地跨模块大改
- 以“兜底默认值”掩盖配置或调用错误

## 4. 慎重提取辅助函数

提取公共辅助函数前必须评估:

- 复用范围是否足够大且稳定
- 是否能以内联或局部函数解决
- 命名是否会增加全局命名空间噪声

若仅为单点使用或短期逻辑,优先保持局部实现。

## 5. 关于非法条件检查与失败策略

默认策略:尽早失败(fail fast),避免 silent fallback。

- 构造/初始化阶段应一次性完成可验证检查
- 配置或参数非法时应明确报错,不用默认值掩盖
- 对已由类型系统约束的函数参数,不重复做同类防御性检查
- 仅在越界、索引失效、外部系统不稳定等场景增加必要检查

目标是在错误发生时暴露真实原因,降低后期调试成本。

## 6. 标准响应模板(高风险请求)

当判定为高风险请求时,按以下结构回复:

1. 风险判断:说明命中的高风险条件
2. 不直接实现原因:说明可维护性/可验证性风险
3. 推荐最小下一步:给出 1 个可执行且可验证的下一步

示例:

- 风险判断:当前需求缺少明确实现边界和验收标准
- 不直接实现原因:直接大范围生成会提高回归风险,难以定位问题
- 推荐最小下一步:先确定模块边界与接口草案,我基于该草案实现第一阶段并附带测试
26 changes: 19 additions & 7 deletions config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ identifier:
- "shenzhen-0526.onnx"
- "shenzhen-0708.onnx"
model_location: "tongji-yolov5.xml"
infer_device: "GPU"
infer_device: "AUTO"
use_roi_segment: false
roi_rows: 640
roi_cols: 640
Expand All @@ -59,8 +59,20 @@ tracker:
tracking_confirm_frames: 2

pose_estimator:
camera_matrix: [1.722231837421459e+03, 0, 7.013056440882832e+02, 0, 1.724876404292754e+03,5.645821718351237e+02 , 0, 0, 1]
distort_coeff: [-0.064232403853946, -0.087667493884102, 0,0, 0.792381808294582]
camera_matrix:
[
1.722231837421459e+03,
0,
7.013056440882832e+02,
0,
1.724876404292754e+03,
5.645821718351237e+02,
0,
0,
1,
]
distort_coeff:
[-0.064232403853946, -0.087667493884102, 0, 0, 0.792381808294582]

transforms:
- parent: "imu_link"
Expand All @@ -82,10 +94,10 @@ fire_control:
yaw_offset: 0.0 # degree
pitch_offset: 0.0 # degree

coming_angle: 70.0 # degree
leaving_angle: 20.0 # degree
outpost_coming_angle: 70.0 # degree
outpost_leaving_angle: 30.0 # degree
coming_angle: 70.0 # degree
leaving_angle: 20.0 # degree
outpost_coming_angle: 70.0 # degree
outpost_leaving_angle: 30.0 # degree
angular_velocity_threshold: 120 # degree/s

first_tolerance: 3 # 近距离射击容差,degree
Expand Down
69 changes: 33 additions & 36 deletions src/component.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <chrono>
#include <cmath>
#include <limits>

#include <rmcs_description/tf_description.hpp>
#include <rmcs_executor/component.hpp>

Expand All @@ -31,7 +32,7 @@ class AutoAimComponent final : public rmcs_executor::Component {
using namespace std::chrono_literals;
framerate.set_interval(2s);

visual::Transform::Config config {
const auto config = visual::Transform::Config {
.rclcpp = rclcpp, // 当前组件持有的 RclcppNode
.topic = "odom_to_camera_transform", // 发布的 topic 名
.parent_frame = "odom_imu_link", // 父坐标系
Expand All @@ -54,7 +55,7 @@ class AutoAimComponent final : public rmcs_executor::Component {
}

private:
static constexpr auto auto_aim_state_timeout { std::chrono::milliseconds { 100 } };
static constexpr auto kAutoAimTimeout = std::chrono::milliseconds { 100 };

InputInterface<rmcs_description::Tf> rmcs_tf;

Expand All @@ -64,7 +65,7 @@ class AutoAimComponent final : public rmcs_executor::Component {
RclcppNode rclcpp;
std::unique_ptr<visual::Transform> visual_odom_to_camera;

Feishu<RuntimeRole::Control> feishu;
Feishu<ControlState, AutoAimState> feishu;
ControlState control_state;
AutoAimState auto_aim_state;
bool auto_aim_state_received_ { false };
Expand All @@ -76,20 +77,37 @@ class AutoAimComponent final : public rmcs_executor::Component {
FramerateCounter framerate;
ActionThrottler action_throttler { std::chrono::seconds(1), 233 };

auto has_fresh_auto_aim_state() const -> bool {
return auto_aim_state_received_
&& Clock::now() - auto_aim_state.timestamp <= auto_aim_state_timeout;
/// FIXME:
/// 很多细碎的辅助函数和逻辑
/// 显然是不需要的,记得重构掉
static auto make_invalid_auto_aim_state() -> AutoAimState {
return AutoAimState::kInvalid();
}

static auto make_invalid_auto_aim_state() -> AutoAimState {
auto state = AutoAimState {};
state.reset();
return state;
static auto compute_target_direction(const AutoAimState& state) -> Eigen::Vector3d {
if (!state.gimbal_takeover || !std::isfinite(state.yaw) || !std::isfinite(state.pitch)) {
return Eigen::Vector3d::Zero();
}

const auto& [yaw, pitch] = std::tie(state.yaw, state.pitch);

return {
std::cos(pitch) * std::cos(yaw),
std::cos(pitch) * std::sin(yaw),
std::sin(pitch),
};
}

auto has_fresh_auto_aim_state() const -> bool {
return auto_aim_state_received_
&& Clock::now() - auto_aim_state.timestamp <= kAutoAimTimeout;
}

auto resolve_auto_aim_state() -> AutoAimState {
if (feishu.updated()) {
auto_aim_state = feishu.fetch();
if (feishu.heartbeat()) {
if (auto latest = feishu.latest()) {
auto_aim_state = *latest;
}
auto_aim_state_received_ = true;
}

Expand All @@ -106,41 +124,20 @@ class AutoAimComponent final : public rmcs_executor::Component {
*target_direction = compute_target_direction(state);
}

static auto compute_target_direction(const AutoAimState& state) -> Eigen::Vector3d {
if (!state.has_control_direction()) {
return Eigen::Vector3d::Zero();
}

const auto& [yaw, pitch] = std::tie(state.yaw, state.pitch);

// clang-format off
return Eigen::Vector3d {
std::cos(pitch) * std::cos(yaw),
std::cos(pitch) * std::sin(yaw),
std::sin(pitch)
};
// clang-format on
}

auto forward_auto_aim_outputs() -> void { publish_auto_aim_outputs(resolve_auto_aim_state()); }

auto handle_tf_not_ready() -> void {
action_throttler.dispatch("tf_not_ready", [&] { rclcpp.warn("rmcs_tf is not ready"); });
control_state.reset();
control_state = ControlState::kInvalid();
publish_auto_aim_outputs(make_invalid_auto_aim_state());
}
Comment on lines 129 to 133

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

handle_tf_not_readycontrol_state = ControlState::kInvalid() 实际上是死赋值。

该分支在 Line 50 直接 return,并不会走到 publish_control_state();下一帧 tf 就绪后,update_control_state() 会把 timestamp/yaw/pitch/odom_to_camera_transform/invincible_devices 等字段全部覆盖。也就是说这次 kInvalid() 赋值不会被发送出去,也不会被下一次发送看到。

如果意图是"tf 未就绪期间也要让对端收到 invalid 信号",应改为在此分支 feishu.with_write(...) 一次;如果意图只是把内部状态清空,那么这一行可以直接删掉。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/component.cpp` around lines 129 - 133, 在 handle_tf_not_ready 中当前对
control_state 进行 control_state = ControlState::kInvalid() 会被后续
update_control_state 覆盖且不会触发 publish_control_state(),所以请根据意图处理:如果希望在 tf
未就绪期间通知对端发送 invalid 状态,则在该分支内调用 feishu.with_write(...)(用现有写入/发布路径直接写出并发送包含
kInvalid 的 control_state 或调用 publish_control_state()
的等价写入操作);如果只是想清空内部状态且不通知对端,则删除 control_state = ControlState::kInvalid()
这一行。保持现有对 publish_auto_aim_outputs(make_invalid_auto_aim_state())
的调用不变或与写入操作配对发送。


auto publish_control_state() -> void {
update_gimbal_direction();
update_control_state();

auto success = feishu.commit(control_state);
if (!success) {
action_throttler.dispatch("commit_control_state_failed",
[&] { rclcpp.info("commit control state failed!"); });
} else {
action_throttler.reset("commit_control_state_failed");
}
feishu.with_write([&](auto& data) { data = control_state; });
action_throttler.reset("commit_control_state_failed");
}

auto update_control_state() -> void {
Expand Down
4 changes: 1 addition & 3 deletions src/kernel/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@ using result_type = std::expected<void, std::string>;
using handle_type = std::coroutine_handle<co::task<result_type>::promise_type>;

namespace details {
using Expansion = rmcs::util::Serializable;

template <class T>
concept has_config_trait = requires { typename T::Config; };

template <class T>
concept serialable_config_trait =
requires { requires std::derived_from<typename T::Config, Expansion>; };
requires { requires std::derived_from<typename T::Config, util::Serializable>; };

template <class T>
concept can_initialize_trait = requires(T& kernel) {
Expand Down
Loading
Loading