AI 协作日志

学生 Web 实验作品平台

学号:202412211402010 · 来源:ai-log.md

# 实验四 AI协作记录

> 学号:202412211402010
> 姓名:叶剑彤
> AI工具与使用版本(如适用):豆包(Doubao)
> 请用真实过程填充,保留关键提示词原文;不要只贴整段AI代码或聊天截图。

## 协作1:变换次序核验(Task B)

### 我的目标与先验预测

Task B 要求用同一组参数构造两种矩阵顺序 `M1 = T·Ry·S` 和 `M2 = Ry·T·S`。我根据实验三学到的"列向量约定、右侧先执行"推测:`T·R·S` 是"先旋转后平移",物体中心应落在平移量 (tx,0,0);`R·T·S` 是"先平移后旋转",物体中心会被旋转带着走。对后者最终落在哪,我最初并不确定。

### 关键提示词

> 在WebGL2的列向量约定下,局部原点为(0,0,0),平移(2,0,0),绕Y轴旋转90°。请分别分析T·R与R·T的世界中心位置和计算步骤,并建议我要打印哪些数值进行核验;不要直接重写完整程序。

### AI建议摘要

矩阵链 `chain(A,B,C)=A×B×C` 中**右侧先作用于顶点**:
- `T·R·S`:先缩放→再旋转→最后平移,局部原点最终 = 平移量 (2,0,0),物体在原点转好后再被搬走;
- `R·T·S`:先缩放→再平移→最后旋转,物体先被搬到 (2,0,0),再绕世界原点旋转90°,此时平移量本身被旋转带走,局部原点变为 (0,0,-2)。

### 我的判断与手算

手算 `Ry(90°)` 作用于点 (2,0,0):绕Y轴旋转90°,X轴正方向转向Z轴负方向,(2,0,0) → (0,0,-2)。与 AI 的结论一致,我采纳。

### 我实际测试的参数与坐标

点击 task-b.html 的"一键设置90°测试"按钮:tx=2、θ=90°、s=1,两种矩阵同时显示,状态栏实时输出两组世界中心。

### 运行现象/数值结果

| 矩阵 | 预测中心 | 状态栏实测中心 |
|---|---|---|
| T·R·S(红色立方体) | (2.00, 0.00, 0.00) | (2.00, 0.00, 0.00) |
| R·T·S(青色立方体) | (0.00, 0.00, -2.00) | (0.00, 0.00, -2.00) |

### 修正结论

预测与运行数值完全一致,确认矩阵乘法**不可交换**:相同 T、R、S 参数,仅交换 T 与 R 的次序就改变了局部原点的世界位置。本质区别是"平移在旋转左边→平移不被旋转影响;平移在旋转右边→平移被旋转带着走"。也验证了:θ=0° 时两者结果相同只是旋转未生效的特殊情况,不能据此认为矩阵可交换。

## 协作2:程序调试(Task C)

### 错误现象与自己的初步分析

完成 Task C 的 C1(自转矩阵)、C2(绕点旋转矩阵)后,在 C3 处填写"角度随 dt 更新"逻辑时,把更新代码连同 `c.loop(...)` 一起粘贴进了老师原有的 `c.loop` 函数体内。重新运行 task-c.html 后**画面出现异常(图形显示异常/消失)**。我的初步怀疑是 C1/C2 的矩阵顺序写错,但逐一检查后矩阵链本身没有语法或逻辑问题。

### 提示词

> 我贴出 task-c.js 的完整代码,运行后图形显示异常("图片不见了")。请帮我检查 C 任务代码哪里出了问题。

### AI提出的可能原因

问题不在矩阵,而在**动画循环结构**:C3 处多嵌套了一层 `c.loop`,形成"循环里开循环"。外层 `c.loop` 每一帧都会执行一次内层 `c.loop(...)`,即每帧都新建一个 requestAnimationFrame 循环,多个 rAF 循环不断叠加,最终导致性能崩溃、画面异常。这与实验三的教训一致——应避免重复开启多个 rAF 循环。

### 我逐项完成的测试

1. 检查 C1/C2 矩阵链——语法正确,旋转、平移、缩放次序符合 `T(P)·R·T(-P)·T(q)·S`;
2. 检查 C3——确认存在两层 `c.loop` 嵌套;
3. 将内层更新逻辑(`angle=(angle+speed*dt)%360` 与滑块同步)合并进**唯一一层** `c.loop(function(dt){...}, draw)`,删除多余的嵌套。

### 真实原因与修复

真实原因:C3 的更新代码被包进了第二个 `c.loop`,导致每帧创建新动画循环、循环数量失控。修复为单层循环:

```javascript
c.loop(function (dt) {
  if (playing) {
    const speed = c.num("speed");
    angle = (angle + speed * dt) % 360;   // 角速度×时间增量,%360转满归零
  }
  c.el("angle").value = Math.round(angle / 5) * 5;
}, draw);
```

### AI建议是否采纳及理由

采纳。修复后画面恢复正常:P=(0,0,0)、θ=90° 时自转中心 (2,0,0)、绕点旋转中心约 (0,0,-2);暂停时角度停止累加,继续时无突跳,转满 360° 回到初始位置。

## 协作3:主题场景协商(Task E)

### 我希望实现的对象关系

Task E 需要至少 3 个可区分部件、1 组父子关系、2 种复合变换机制、2 个独立运动参数。教师示例为"医学设备定位装置+旋转样本台":左侧 base→shoulder→tip 父子链,右侧样本自转+标记绕样本中心公转。我考虑过保留该主题,或与 AI 协商改为"行星—卫星"系统。

### 关键提示词

> 我已实现T·R·S、绕指定点旋转和两级父子变换。请提出两个可在40分钟内用简单立方体完成的三维联动场景,并比较部件关系、矩阵链、交互参数、验证难度。请复用现有WebGL2框架,不引入相机拾取、光照或外部库,也不要直接生成整套代码。

### AI提供的两个方案与比较

| 方案 | 部件关系 | 核心矩阵链 | 交互参数 | 验证难度 |
|---|---|---|---|---|
| A 医学设备定位装置(教师示例) | 基座→机械臂→探头(父子链)+样本自转+标记公转 | 基座 `T·Ry(yaw)`;肩 `base·T·Rz(pitch)`;末端 `shoulder·T`;样本 `T(samplePivot)·Ry(spinAngle)·S`;标记 `T(samplePivot)·Ry(orbitAngle)·T(1.16,.28,0)·S` | yaw、pitch、speed | 低:D 的层次代码直接复用,验收点明确 |
| B 行星—卫星系统 | 行星绕恒星公转+卫星绕行星公转(两级继承) | 行星 `T(star)·Ry(θ1)·T(-star)·T(planet)·S`;卫星在行星坐标系再套一层绕点旋转 | 两轨角速度 | 中:需两级公转叠加,继承关系多一层 |

### 我的选择理由

倾向方案 A(教师示例):与 Task D 的层次结构一脉相承,E1~E3 与 D1~D3 矩阵链同构,40 分钟内可实现;右侧自转/公转直接复用 Task C 已验证的 `T(P)·R·T(-P)` 思路,验证风险最低。

### 实际矩阵链与交互参数

按方案 A 设计(模板已给出全部占位与验证点):
- E1 基座:`basePose = chain(T(-2,-1.3,0), R("Y", yaw))`
- E2 肩:`shoulderPose = chain(basePose, T(0,.55,0), R("Z", pitch))`
- E3 末端:`tipPose = chain(shoulderPose, T(0,2.2,0))`
- E4 样本自转:`specimenDraw = chain(T(samplePivot), R("Y", spinAngle), S(.7,.78,.7))`,spinAngle = elapsed×65
- E5 环绕标记:`orbitMarker = chain(T(samplePivot), R("Y", orbitAngle), T(1.16,.28,0), S(.22))`,orbitAngle = elapsed×42
- 交互:yaw(基座偏航)、pitch(机械臂俯仰)、speed(动画速度)、暂停/继续、重置、PNG 截图

### 两组对照测试

(本轮未实施 Task E,无实际运行对照数据——如实记录。)

### 最终结果和反思

因个人时间与精力安排,本轮**未实际实现 Task E**,仅完成方案协商。已在本实验中完整验证:A 的复合矩阵、B 的变换次序非交换性、C 的绕自身/绕指定点旋转、D 的父子层次代码填充。若后续补做 E,按上表矩阵链可直接在 40 分钟内完成,并补做"调基座探头整体跟随/调肩部基座静止"两组对照测试。

---

## 实验四总体反思

- 明确掌握:列向量约定下"右侧先执行";`T·R·S` 与 `R·T·S` 的非交换性;绕点旋转的三段式 `T(P)·R·T(-P)`;父子层次中"子矩阵 = 父位姿 × 子局部变换",非均匀缩放只属于绘制层、不能进入位姿矩阵传给孩子。
- 本次最大的坑:动画循环里嵌套动画循环,导致 rAF 循环数量失控、画面崩溃——与实验三"不要重复开启多个 rAF 循环"的教训完全一致,今后填动画逻辑时先确认只保留一层 `c.loop`。