AI 协作日志

学生 Web 实验作品平台

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

# 实验四 AI协作记录

> 学号:202412211401023  
> 姓名:(学生填写)  
> AI工具与使用版本:豆包(Doubao)WebGL图形学辅助

---

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

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

**目标:** 验证在列向量约定下,平移T与旋转R相乘顺序不同,会导致局部原点的世界坐标不同。

**我的先验预测:**
- 参数:T=(2,0,0),绕Y轴旋转90°,缩放s=1。
- M1 = T·R·S(先缩放、再旋转、最后平移):我预测模型中心应该在(2, 0, 0),因为局部原点经过缩放和旋转后仍在原点,最后平移到(2,0,0)。
- M2 = R·T·S(先缩放、再平移、最后旋转):我预测模型中心会变化,因为平移向量(2,0,0)会被旋转矩阵作用。但具体变成多少我不确定,直觉上90°绕Y轴旋转可能把X方向转到Z方向。

### 关键提示词

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

### AI建议摘要

AI给出的分析:
1. 列向量约定下,矩阵乘法从右到左作用于顶点,即最右侧矩阵最先作用。
2. 对于M1 = T·R·S:局部原点(0,0,0) → S(1)后仍为(0,0,0) → R(Y,90°)后仍为(0,0,0)(原点旋转后不变)→ T(2,0,0)后为(2,0,0)。
3. 对于M2 = R·T·S:局部原点 → S后(0,0,0) → T(2,0,0)后(2,0,0) → R(Y,90°)后,X轴正方向旋转到Z轴负方向,因此变为(0,0,-2)。
4. 建议在程序中打印model矩阵的平移分量(第4列前3行)以及c.point(model,0,0,0)的结果进行双向核验。

### 我的判断与手算

**我的手算验证:**
绕Y轴90°旋转矩阵(右手坐标系,列向量):
```
Ry(90°) = [ cos90°  0  sin90°  0 ]   [ 0  0  1  0 ]
          [   0     1    0    0 ] = [ 0  1  0  0 ]
          [-sin90°  0  cos90°  0 ]   [-1  0  0  0 ]
          [   0     0    0    1 ]   [ 0  0  0  1 ]
```

- T·R作用于(0,0,0):R先作用,(0,0,0)不变;T再作用,得到(2,0,0)。✓
- R·T作用于(0,0,0):T先作用得到(2,0,0);R再作用:x' = 0·2 + 0·0 + 1·0 = 0,z' = -1·2 + 0·0 + 0·0 = -2,即(0,0,-2)。✓

AI的分析与我的手算一致。

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

在Task B页面点击"一键设置90°测试"按钮,参数自动设为tx=2、angle=90°、scale=1。

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

状态栏显示:
- T·R·S中心:(2.00, 0.00, 0.00)
- R·T·S中心:(0.00, 0.00, -2.00)

与手算预测完全一致。

进一步测试:
- theta=0°时,两种顺序中心都为(2,0,0)——旋转为恒等矩阵时结果相同。
- theta=180°时,T·R·S中心仍为(2,0,0);R·T·S中心为(-2,0,0)——180°旋转把X正方向转到X负方向。

### 修正结论

**结论:** 矩阵乘法不满足交换律。T和R的先后顺序决定了"是物体先转再移,还是先移再整体转"。
- T·R = 物体绕原点旋转后,整体平移到目标位置——物体中心始终落在平移向量指定处。
- R·T = 物体先平移到目标位置,再让整个场景绕原点旋转——平移向量本身也被旋转了。

theta=0°时两种结果相同只是特例(旋转矩阵为单位矩阵),不能据此证明矩阵乘法可交换。

---

## 协作2:程序调试(Task D 层次变换)

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

**错误现象:** Task D中,当我只修改基座偏航角度yaw时,机械臂和探头没有跟着一起旋转,仍然保持原来的朝向。但修改pitch时机械臂俯仰是正常的。

**我的初步分析:**
1. 可能是基座位姿basePose没有正确包含yaw旋转——基座本身转了,但子部件没继承这个旋转。
2. 可能是肩部位姿shoulderPose直接用了世界坐标的T(0,0.55,0),没有乘以basePose。
3. 可能是矩阵链式乘法顺序写反了。

### 提示词

> 我的三维对象能显示,但修改父关节角度后探头位置异常。请提出三个最有可能的矩阵顺序或局部坐标错误,每个错误给出一项能区分原因的测试,不要直接替我重写整份代码。

### AI提出的可能原因

AI提出三个最可能的原因:

**原因1:肩部位姿没有继承基座位姿**
- 错误写法:`shoulderPose = T(0, h, 0) · Rz(pitch)`(直接用世界坐标系)
- 正确写法:`shoulderPose = basePose · T(0, h, 0) · Rz(pitch)`(先乘父级矩阵)
- 区分测试:把yaw固定改为90°,如果肩部世界坐标仍在(0, h, 0)附近,说明没有继承;如果x/z坐标变了,说明继承正确。

**原因2:矩阵链乘法顺序写反**
- 错误写法:`chain(T(0,h,0), Rz(pitch), basePose)`(父级在最右侧)
- 列向量约定下应该是父级在最左侧:父矩阵 × 局部矩阵
- 区分测试:打印shoulderPose矩阵的第4列(平移分量),看是否等于basePose作用于(0,h,0)的结果。

**原因3:把非均匀缩放混入了位姿矩阵**
- 错误:basePose或shoulderPose中包含了S()缩放,导致子对象也被非均匀缩放。
- 区分测试:观察探头形状是否变形,如果被拉细长了就是这个问题。

### 我逐项完成的测试

**测试1:检查shoulderPose是否继承basePose**
- 原代码:`const shoulderPose = c.T(0,.55,0);`
- 问题:确实没有乘以basePose,直接用了世界坐标平移。
- 把yaw设为90°,打印肩部世界坐标:仍在(0, 0.55, 0)附近,没有旋转。确认原因1成立。

**测试2:确认矩阵乘法顺序**
- 修复后:`shoulderPose = chain(basePose, T(0,0.55,0), Rz(pitch))`
- yaw=90°时打印肩部坐标:x和z都变了,说明basePose的旋转已经作用到肩部。
- 确认chain()函数是从左到右依次相乘(A×B×C),列向量约定下父级在左、局部在右是正确的。

**测试3:检查缩放是否泄漏到子层级**
- 原代码中baseDraw和armDraw是单独的绘制矩阵,包含缩放;而basePose和shoulderPose是位姿矩阵,不含缩放。
- 观察探头形状:没有变形,说明缩放隔离正确。原因3不成立。

### 真实原因与修复

**真实原因:** 肩部位姿shoulderPose和末端位姿tipPose都没有继承父级矩阵,直接用了世界坐标的平移。

**修复:**
- shoulderPose = chain(basePose, T(0, 0.55, 0), Rz(pitch))
- tipPose = chain(shoulderPose, T(0, 2.2, 0))

**验证:** 修复后,调节yaw滑块,基座、机械臂、探头整体绕Y轴旋转;调节pitch滑块,只有机械臂和探头俯仰,基座不动。父子继承关系正确。

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

全部采纳。AI提出的三个原因中,原因1和原因2实际上是同一个问题的不同表现(矩阵链不完整),原因3经测试排除。最终的分层设计——位姿矩阵(T+R)与绘制矩阵(+S)分开——正是AI建议的核心思路。

---

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

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

我已经掌握了T·R·S复合变换、绕指定点旋转、两级父子层次变换。希望把这些整合到一个有意义的三维场景中,同时满足实验要求:至少3个部件、1组父子关系、2种复合机制、2个独立参数。

### 关键提示词

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

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

**方案A:医学设备定位装置 + 旋转样本台**
- 左侧:基座(偏航)→ 机械臂(俯仰)→ 探头(末端跟随)——两级父子层次。
- 右侧:样本台(自转)+ 环绕标记(绕样本中心公转)——独立旋转。
- 部件数:6个(基座、臂、探头、台面、样本、标记)。
- 矩阵链:base = T·Ry(yaw);shoulder = base·T·Rz(pitch);tip = shoulder·T(0,L,0);sample = T(P)·R(ωt)·S;marker = T(P)·R(ω't)·T(r,0,0)·S。
- 交互参数:yaw、pitch、speed。
- 验证难度:中等——可以直接对照"父动子随、子动父不动"的层次特性。

**方案B:行星—卫星系统**
- 中心恒星(不动)、行星(绕恒星公转 + 自转)、卫星(绕行星公转)。
- 三层层次关系:恒星→行星→卫星。
- 部件数:3~4个。
- 矩阵链:planet = T(center)·R(orbit)·T(r,0,0)·R(spin)·S;moon = planet·T(d,0,0)·R(moonOrbit)·S。
- 交互参数:公转角速度、自转角速度。
- 验证难度:较高——三层嵌套矩阵更容易写错,需要更多数值调试。

### 我的选择理由

**选择方案A(医学设备定位装置 + 旋转样本台)**,理由:
1. 与实验指导书的教师参考示例主题一致,但可以完善所有矩阵链,而不是只改颜色——满足"实质性修改"的要求。
2. 左侧机械臂的父子层次关系直观,容易验证"父动子随、子动父不动"。
3. 右侧样本自转 + 标记公转,正好对应Task C的两种旋转机制(自转 vs 绕点旋转),可以复用已掌握的知识。
4. 6个部件、3种复合机制(层次继承、自转、公转)、3个独立参数,远超最低要求,验证更充分。

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

**部件与矩阵链:**
1. 基座:`basePose = T(-2,-1.3,0) · Ry(yaw)` → `baseDraw = basePose · T(0,0.28,0) · S(1.35,0.55,1.05)`
2. 机械臂:`shoulderPose = basePose · T(0,0.55,0) · Rz(pitch)` → `armDraw = shoulderPose · T(0,1.1,0) · S(0.38,2.2,0.45)`
3. 探头:`tipPose = shoulderPose · T(0,2.2,0)` → `probeDraw = tipPose · T(0,0.32,0) · S(0.73,0.65,0.73)`
4. 样本台:`turntableDraw = T(2,-1.32,0) · S(1.9,0.25,1.9)`
5. 样本(自转):`specimenDraw = T(2,-0.48,0) · Ry(65°/s × t) · S(0.7,0.78,0.7)`
6. 环绕标记(公转):`orbitMarker = T(2,-0.48,0) · Ry(42°/s × t) · T(1.16,0.28,0) · S(0.22)`

**交互参数:**
- 基座偏航yaw(-180°~180°)
- 机械臂俯仰pitch(-60°~60°)
- 样本旋转速度speed(0~3倍速)

### 两组对照测试

**对照1:纯基座旋转测试**
- 操作:pitch固定为0,yaw从0°调到90°。
- 预期:基座、机械臂、探头整体绕Y轴旋转90°,相对装配关系不变;右侧样本台和样本不受影响。
- 实际:左侧臂组整体旋转,探头始终在臂端;右侧独立旋转。符合预期。

**对照2:纯肩部俯仰测试**
- 操作:yaw固定为0,pitch从0°调到-45°。
- 预期:基座保持竖直不动,机械臂和探头绕肩部Z轴俯仰,探头位置随臂端变化;右侧样本继续独立旋转。
- 实际:基座不动,臂段向前下方倾斜,探头跟随臂端运动。符合预期。

### 最终结果和反思

**最终结果:** 场景可运行,暂停/继续、速度调节、重置、PNG截图功能均正常。左侧两级层次继承正确,右侧自转与公转轨迹不同。

**反思:**
1. 位姿矩阵(只含T和R)与绘制矩阵(再加S)必须严格分开,否则非均匀缩放会层层传递导致子对象变形。
2. 每帧从参数重建矩阵(而不是累乘旧矩阵)是避免漂移的关键做法——因为yaw/pitch/speed都是绝对参数,不是增量。
3. 绕点旋转的标准形式T(P)·R·T(-P)·X,本质是"把P移到原点→旋转→移回P",这个模式可以复用到任何绕任意点旋转的场景。
4. 与教师示例相比,我完善了全部6个部件的矩阵链(教师模板中很多TODO留空),并验证了三组控制变量(纯yaw、纯pitch、暂停继续),不只是改了页面颜色。