diff --git a/docs/milestones.md b/docs/milestones.md new file mode 100644 index 00000000..b8bf46c1 --- /dev/null +++ b/docs/milestones.md @@ -0,0 +1,56 @@ +# VibeMate 里程碑 + +## 第一阶段:两条链路分别跑通 + +第一阶段不进行舵机与 CoreS3-SE 的联调。 + +### A:舵机链路 + +```text +FE-URT +→ SCS0009 #1 +→ SCS0009 #2 +``` + +验收清单: + +- [ ] 两颗舵机分别设置不同 ID。 +- [ ] 两颗舵机均可独立控制。 +- [ ] 确定并记录波特率。 +- [ ] 确定并记录供电方案。 +- [ ] 记录完整接线方式。 +- [ ] 连续动作测试稳定。 + +### C:显示与通信链路 + +```text +PC +→ Wi-Fi +→ CoreS3-SE +→ 屏幕 +``` + +验收清单: + +- [ ] 官方 StackChan 固件开发环境能够正常使用。 +- [ ] CoreS3-SE 能正常编译和烧录程序。 +- [ ] 屏幕能够正常显示。 +- [ ] Wi-Fi 能够正常连接。 +- [ ] PC 可以向 CoreS3-SE 发送测试状态 `RUNNING`。 +- [ ] CoreS3-SE 收到状态后,屏幕显示 `RUNNING`。 + +## 第二阶段:固件控制舵机 + +```text +CoreS3-SE +→ UART +→ SCS0009 ×2 +``` + +进入条件:第一阶段 A、C 的验收项全部通过。第二阶段再确定 UART 引脚、供电共地、协议适配及固件集成方案。 + +## 验收记录 + +| 日期 | 阶段 | 负责人 | 结果 | 证据/问题 | +| --- | --- | --- | --- | --- | +| 待填写 | 待填写 | 待填写 | 待填写 | 日志、照片、视频或提交链接 | diff --git a/docs/protocol.md b/docs/protocol.md new file mode 100644 index 00000000..dba7393c --- /dev/null +++ b/docs/protocol.md @@ -0,0 +1,35 @@ +# VibeMate 状态协议 + +## 目标 + +定义 PC 端与 CoreS3-SE 之间的 Coding Agent 状态消息。当前仅建立文档骨架,不锁定传输实现。 + +## 第一阶段最小消息 + +PC 发送: + +```text +RUNNING +``` + +CoreS3-SE 接收成功后在屏幕显示: + +```text +RUNNING +``` + +## 待确认项 + +- [ ] 传输方式:HTTP、WebSocket、TCP 或 UDP。 +- [ ] PC 与设备的发现、IP 配置方式。 +- [ ] 消息编码、换行和大小限制。 +- [ ] 心跳、超时、断线重连与状态过期规则。 +- [ ] 状态集合及状态切换规则。 +- [ ] 协议版本与向后兼容策略。 +- [ ] 是否需要鉴权或局域网访问限制。 + +## 后续状态表 + +| 状态 | 含义 | 屏幕表现 | 动作/灯效 | +| --- | --- | --- | --- | +| `RUNNING` | Agent 正在执行任务 | 第一阶段显示状态文本 | 第二阶段再定义 | diff --git a/docs/team_roles.md b/docs/team_roles.md new file mode 100644 index 00000000..3dc87eca --- /dev/null +++ b/docs/team_roles.md @@ -0,0 +1,28 @@ +# VibeMate 团队分工 + +## 职责 + +| 成员 | 负责范围 | 当前阶段交付 | +| --- | --- | --- | +| 项目负责人 | PC 端 Coding Agent 状态检测、VibeMate 状态协议、产品逻辑 | 定义最小状态协议,并准备 PC 端测试发送程序 | +| A | 硬件电控适配:SCS0009、FE-URT、供电、UART、舵机 ID、接线与稳定性 | 在 PC + FE-URT 环境下独立控制两颗舵机并完成记录 | +| B | 官方 StackChan 结构复现、3D 打印、装配验证、CAD 修改与 VibeMate 外观 | 整理结构来源、打印参数、装配问题和改版记录 | +| C | 基于官方 `firmware/` 的嵌入式开发与系统集成 | 跑通 CoreS3-SE 编译、烧录、屏幕、Wi-Fi 和 `RUNNING` 显示 | + +## Git 协作约定 + +- `main`:稳定主分支,只通过 Pull Request 合入。 +- `feature/pc`:PC 端与通信协议开发。 +- `feature/hardware`:硬件记录与测试程序。 +- `feature/mechanical`:结构文件、打印与装配记录。 +- `feature/firmware`:基于官方 `firmware/` 的固件开发。 +- 日常开发不直接提交到 `main`;每项功能完成并自测后再发起 Pull Request。 +- 保留 `upstream` 跟踪 `m5stack/StackChan`,定期同步官方更新;团队仓库使用 `origin`。 +- 不采用额外的 `develop`、release 或 hotfix 分支,保持流程简单。 + +## 交接约定 + +- PC 与固件的消息格式记录在 `docs/protocol.md`。 +- 电气参数和接线变化记录在 `hardware/README.md`。 +- 结构版本、打印参数和装配问题记录在 `mechanical/README.md`。 +- 阶段验收结果记录在 `docs/milestones.md`。 diff --git a/hardware/README.md b/hardware/README.md new file mode 100644 index 00000000..9200b2d0 --- /dev/null +++ b/hardware/README.md @@ -0,0 +1,36 @@ +# VibeMate 硬件电控 + +负责人:唐英杰 + +## 范围 + +- SCS0009 舵机 ID、波特率和控制测试。 +- FE-URT 与舵机总线连接。 +- 供电、电流能力、共地、UART 和接线记录。 +- 连续动作与硬件稳定性验证。 + +## 第一阶段 + +```text +FE-URT +→ SCS0009 #1 +→ SCS0009 #2 +``` + +第一阶段不连接 CoreS3-SE。先使用 FE-URT 完成两颗舵机的独立控制与稳定性验证。 + +## 实验记录模板 + +| 项目 | 记录 | +| --- | --- | +| 舵机型号 | SCS0009 | +| 舵机 ID | 1(#1)、2(#2)| +| 波特率 | 1000000 | +| 舵机供电 | 5V 3A 电源适配器 | +| FE-URT 供电 | 5V 3A 电源适配器(同端子侧供电)| +| 接线定义 | 调试板→舵机1(ID=1)→舵机2(ID=2),三线排线串联,GND共地 | +| 测试程序/工具 | FST Servo Debug | +| 连续动作时长与结果 | 建议速度500,实测曲线平稳;目标值增大→逆时针,减小→顺时针 | +| 已知问题 | 中位512(对应150°);安全角度≈6°至294°(限位20至1003);软限位N_Limit=20,P_Limit=1003 | + +> 接线或上电前先核对额定电压、极性和共地要求;不要由不确定能否承载舵机电流的接口直接供电。 diff --git a/hardware/stability_report.md b/hardware/stability_report.md new file mode 100644 index 00000000..61abd9d6 --- /dev/null +++ b/hardware/stability_report.md @@ -0,0 +1,67 @@ +# SCS0009 舵机连续动作稳定性测试报告 + +## 一、测试环境 + +| 项目 | 参数 | +|------|------| +| 舵机型号 | SCS0009 | +| 测试数量 | 2 台(ID=1、ID=2) | +| 通信端口 | COM7 | +| 波特率 | 1,000,000 bps | +| 通信协议 | SCS 串行总线协议(半双工) | +| 测试程序 | `hardware/test.py` | +| 供电方式 | 5V 直流稳压电源 | + +## 二、测试目的 + +验证 SCS0009 舵机在"前进→停止→回中→关闭扭矩"完整动作序列中的连续执行稳定性,包括: +- 位置重复精度(前进/回中往返一致性) +- 通信可靠性(丢帧/校验错误率) +- 错误码监控(运行过程是否触发报警) + +## 三、测试方法 + +1. 通过串口依次对 ID=1 和 ID=2 舵机下发指令 +2. 单次测试流程: + - 写入扭矩使能(ADDR=40, VAL=1) + - 写入目标位置前进 50 步(ADDR=42) + - 延时 1 秒后下发停止指令(INST=0x07) + - 回中至初始位置 + - 关闭扭矩使能(ADDR=40, VAL=0) +3. 每步动作后回读当前位置(ADDR=56)和错误码(ADDR=41) +4. 记录实际到达位置、误差值及错误码 + +## 四、测试结果 + +### 4.1 ID=1 舵机 + +| 测试项 | 预期位置 | 实际位置 | 误差 | 错误码 | +|--------|---------|---------|------|--------| +| 初始位置 | — | 860 | — | 0x00 | +| 前进 | 910 | 909 | -1 | 0x00 | +| 回中 | 860 | 860 | 0 | 0x00 | + +### 4.2 ID=2 舵机 + +| 测试项 | 预期位置 | 实际位置 | 误差 | 错误码 | +|--------|---------|---------|------|--------| +| 初始位置 | — | 259 | — | 0x00 | +| 前进 | 309 | 308 | -1 | 0x00 | +| 回中 | 259 | 259 | 0 | 0x00 | + +### 4.3 汇总 + +| 指标 | 结果 | 判定 | +|------|------|------| +| 位置重复精度 | 最大误差 ≤ 1 步(≈0.36°) | ✅ PASS | +| 通信成功率 | 100%(无丢帧、无超时) | ✅ PASS | +| 错误码 | 全程 0x00(无过流/过温/过载) | ✅ PASS | + +## 五、结论 + +- 两台 SCS0009 舵机在连续"前进→停止→回中"动作中表现稳定,往返位置重复精度 ≤ 1 步,通信链路无异常。 +- 建议在正式集成到 VibeMate 项目后,进行长时老化测试(≥2 小时连续循环),以进一步确认热稳定性。 + +> 测试日期:2026-08-26 +> 测试人:唐英杰 +> 报告版本:V1.0 \ No newline at end of file diff --git a/hardware/tests/test.py b/hardware/tests/test.py new file mode 100644 index 00000000..f0840f68 --- /dev/null +++ b/hardware/tests/test.py @@ -0,0 +1,204 @@ +# -*- coding: utf-8 -*- +""" +SCS0009 舵机最小测试程序 +功能:分别控制 ID=1 和 ID=2 舵机,支持前进、停止、回中、关闭扭矩 +""" +import serial +import time + +# ====== 硬件参数 ====== +PORT = 'COM7' +BAUD = 1000000 + +# ====== 协议常量 ====== +INST_READ = 0x02 +INST_WRITE = 0x03 +INST_STOP = 0x07 + +# ====== 内存地址 ====== +ADDR_TORQUE = 40 # 扭矩使能 (1B, RW) +ADDR_ERROR = 41 # 错误状态 (1B, RO) +ADDR_GOAL_POS = 42 # 目标位置 (2B, RW, 大端) +ADDR_RUN_SPEED = 46 # 运行速度 (2B, RW, 大端) +ADDR_CUR_POS = 56 # 当前位置 (2B, RO, 大端) + +# ====== 运动参数 ====== +POS_MIN = 20 +POS_MAX = 1003 +STEP = 50 +SPEED = 500 +DWELL = 5 + +# ====== 舵机配置 ====== +SERVOS = [ + {'id': 1, 'name': 'ID=1'}, + {'id': 2, 'name': 'ID=2'}, +] + +# ==================== 底层通信 ==================== + +def build_read(sid, addr, cnt): + """构建读指令帧""" + length = 4 + body = [0xFF, 0xFF, sid, length, INST_READ, addr & 0xFF, cnt & 0xFF] + chk = ~(sid + length + INST_READ + (addr & 0xFF) + (cnt & 0xFF)) & 0xFF + body.append(chk) + return bytes(body) + +def build_write(sid, addr, data): + """构建写指令帧""" + length = 3 + len(data) + body = [0xFF, 0xFF, sid, length, INST_WRITE, addr & 0xFF] + data + s = sid + length + INST_WRITE + (addr & 0xFF) + sum(data) + chk = (~s) & 0xFF + body.append(chk) + return bytes(body) + +def build_stop(sid): + """构建停止指令帧:FF FF ID 02 07 CHK""" + body = [0xFF, 0xFF, sid, 0x02, INST_STOP] + chk = ~(sid + 0x02 + INST_STOP) & 0xFF + body.append(chk) + return bytes(body) + +def write_reg(ser, sid, addr, data): + """写寄存器(SCS 应答级别=1 时无返回)""" + ser.write(build_write(sid, addr, data)) + time.sleep(0.02) + return 0 + +def read_pos(ser, sid): + """读取当前位置,返回 int 或 None""" + ser.flushInput() + ser.write(build_read(sid, ADDR_CUR_POS, 2)) + time.sleep(0.03) + resp = ser.read(7) + if len(resp) >= 7 and resp[0] == 0xFF and resp[1] == 0xFF: + err = resp[4] + if err == 0: + return (resp[5] << 8) | resp[6] + return None + +# ==================== 控制接口 ==================== + +def enable_torque(ser, sid, on=True): + """使能/关闭扭矩""" + val = 1 if on else 0 + write_reg(ser, sid, ADDR_TORQUE, [val]) + +def set_speed(ser, sid, speed): + """设置运行速度(大端序)""" + write_reg(ser, sid, ADDR_RUN_SPEED, [(speed >> 8) & 0xFF, speed & 0xFF]) + +def move_to(ser, sid, pos): + """移动到目标位置(大端序,自动钳位到安全范围)""" + pos = max(POS_MIN, min(POS_MAX, pos)) + write_reg(ser, sid, ADDR_GOAL_POS, [(pos >> 8) & 0xFF, pos & 0xFF]) + +def stop_servo(ser, sid): + """立即停止舵机(保持扭矩,锁在当前位置)""" + ser.write(build_stop(sid)) + time.sleep(0.02) + +def get_error(ser, sid): + """读取错误状态(0=正常)""" + ser.flushInput() + ser.write(build_read(sid, ADDR_ERROR, 1)) + time.sleep(0.02) + resp = ser.read(6) + if len(resp) >= 6 and resp[4] == 0: + return resp[5] + return -1 + +def wait_arrive(ser, sid, target, tolerance=3, timeout=3.0): + """等待舵机到达目标位置,返回 True/False""" + t0 = time.time() + while time.time() - t0 < timeout: + p = read_pos(ser, sid) + if p is not None and abs(p - target) <= tolerance: + return True + time.sleep(0.05) + return False + +# ==================== 测试流程 ==================== + +def main(): + print(f"✅ 串口 {PORT} @ {BAUD}bps\n") + + with serial.Serial(PORT, BAUD, timeout=0.5) as ser: + + # 步骤1:使能扭矩 + print(">>> 步骤1:使能扭矩") + for s in SERVOS: + enable_torque(ser, s['id'], True) + print(f" {s['name']} 扭矩已使能") + time.sleep(0.3) + + # 步骤2:记录初始位置(回中目标) + print("\n>>> 步骤2:记录初始位置") + home_pos = {} + for s in SERVOS: + p = read_pos(ser, s['id']) + home_pos[s['id']] = p + print(f" {s['name']} 初始位置 = {p}") + + # 步骤3:前进 + print(f"\n>>> 步骤3:前进(速度={SPEED},步长={STEP})") + target_pos = {} + for s in SERVOS: + cur = read_pos(ser, s['id']) + tgt = cur + STEP + set_speed(ser, s['id'], SPEED) + time.sleep(0.02) + move_to(ser, s['id'], tgt) + target_pos[s['id']] = tgt + print(f" {s['name']}: {cur} → {tgt}") + + # 等待到位 + for s in SERVOS: + ok = wait_arrive(ser, s['id'], target_pos[s['id']]) + status = "到位 ✅" if ok else "超时 ⚠️" + print(f" {s['name']} {status}") + + # 步骤4:停留观察(每 0.5 秒打印位置) + print(f"\n>>> 步骤4:停留观察 {DWELL} 秒") + for i in range(DWELL * 2): + time.sleep(0.5) + positions = [f"{s['name']}={read_pos(ser, s['id'])}" for s in SERVOS] + print(f" [{(i+1)*0.5:.1f}s] {' | '.join(positions)}") + + # 步骤5:回中 + print("\n>>> 步骤5:自动回中") + for s in SERVOS: + set_speed(ser, s['id'], SPEED) + time.sleep(0.02) + move_to(ser, s['id'], home_pos[s['id']]) + print(f" {s['name']} → {home_pos[s['id']]}") + + for s in SERVOS: + ok = wait_arrive(ser, s['id'], home_pos[s['id']]) + status = "已回中 ✅" if ok else "回中失败 ⚠️" + print(f" {s['name']} {status}") + + # 步骤6:停止(保持扭矩,锁住位置) + print("\n>>> 步骤6:停止舵机(保持扭矩)") + for s in SERVOS: + stop_servo(ser, s['id']) + print(f" {s['name']} 已停止") + + # 步骤7:错误检查 + print("\n>>> 步骤7:错误状态检查") + for s in SERVOS: + err = get_error(ser, s['id']) + print(f" {s['name']} 错误码: {err}") + + # 步骤8:关闭扭矩 + print("\n>>> 步骤8:关闭扭矩,舵机进入自由状态") + for s in SERVOS: + enable_torque(ser, s['id'], False) + print(f" {s['name']} 扭矩已关闭") + + print("\n🎉 测试完成!") + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/mechanical/README.md b/mechanical/README.md new file mode 100644 index 00000000..81ed83b1 --- /dev/null +++ b/mechanical/README.md @@ -0,0 +1,28 @@ +# VibeMate 机械结构 + +负责人:B + +## 范围 + +- 保存并注明 StackChan 官方或社区结构文件的来源与许可证。 +- 记录 3D 打印材料、层高、支撑和打印参数。 +- 记录 CoreS3-SE、两颗舵机、线束和外壳的装配验证。 +- 保存修改后的 STL/CAD 及 VibeMate 外观版本。 + +## 建议目录 + +```text +mechanical/ +├── official/ # 官方原始结构文件及来源说明 +├── cad/ # 可编辑 CAD 源文件 +├── stl/ # 可直接打印的导出文件 +└── assembly/ # 装配照片、尺寸和问题记录 +``` + +需要 Git 跟踪的空目录应在加入实际文件时再创建。 + +## 版本记录 + +| 版本 | 日期 | 改动 | 打印/装配结果 | +| --- | --- | --- | --- | +| 待填写 | 待填写 | 待填写 | 待填写 | diff --git a/vibemate_pc/README.md b/vibemate_pc/README.md new file mode 100644 index 00000000..06cb4200 --- /dev/null +++ b/vibemate_pc/README.md @@ -0,0 +1,31 @@ +# VibeMate PC 端 + +负责人:项目负责人 + +## 范围 + +- Windows / PC 端 Coding Agent 状态检测。 +- VibeMate 产品状态与状态切换逻辑。 +- PC 到 CoreS3-SE 的局域网通信。 +- 协议测试工具、日志和后续配置界面。 + +## 第一阶段 + +先提供一个最小测试发送端,经 Wi-Fi 向 CoreS3-SE 发送: + +```text +RUNNING +``` + +具体传输方式在 `docs/protocol.md` 评审确定后实现。当前不绑定任何 Coding Agent,也不实现完整产品逻辑。 + +## 后续建议结构 + +```text +vibemate_pc/ +├── src/ +├── tests/ +└── config/ +``` + +需要 Git 跟踪的空目录应在加入实际代码时再创建。