AI 协作日志
学生 Web 实验作品平台
学号:202412211401016 · 来源:ai-log.md
# 实验四 AI协作记录 > 学号:202412211401016 > 姓名:(请填写) > AI工具与使用版本:Trae(基于大语言模型的代码助手) > 协作原则:先独立预测 → 再提示AI → 再手算核验 → 最后运行验证;保留关键提示词原文。 --- ## 协作1:变换次序核验(Task B) ### 我的目标与先验预测 在列向量约定下,顶点变换顺序为 P·V·M·position,Model 矩阵右侧的矩阵先作用。 我先独立预测 tx=2, θ=90°, s=1 时两种顺序的世界中心: - **M1 = T·Ry·S**:作用顺序 S→Ry→T。局部原点 (0,0,0) 经过 S 还是 (0,0,0),绕Y旋转90°后还是 (0,0,0),最后平移 (2,0,0) → **预测中心 = (2, 0, 0)**。 - **M2 = Ry·T·S**:作用顺序 S→T→Ry。局部原点经 S 不动,平移到 (2,0,0),然后绕Y旋转90°:x→-z,z→x,即 (2,0,0) → (0,0,-2) → **预测中心 = (0, 0, -2)**。 这两个中心应该明显不同,说明矩阵乘法非交换。 ### 关键提示词 > 在WebGL2的列向量约定下,局部原点为(0,0,0),平移(2,0,0),绕Y轴旋转90°。请分别分析T·R与R·T的世界中心位置和计算步骤,并建议我要打印哪些数值进行核验;不要直接重写完整程序。 ### AI建议摘要 AI确认了我的预测,给出矩阵维度的详细推导: - Ry(90°) = `[[0,0,1,0],[0,1,0,0],[-1,0,0,0],[0,0,0,1]]`(列向量形式,注意 gl-matrix 使用列主序) - T(2,0,0) = `[[1,0,0,2],[0,1,0,0],[0,0,1,0],[0,0,0,1]]` **T·R 作用于 (0,0,0,1)**:先 R 后 T。R·(0,0,0,1) = (0,0,0,1),再 T·(0,0,0,1) = (2,0,0,1) ✓ **R·T 作用于 (0,0,0,1)**:先 T 后 R。T·(0,0,0,1) = (2,0,0,1),再 R·(2,0,0,1) = (0,0,-2,1) ✓ AI建议核验的数值: 1. 中心坐标 x,y,z 各分量 2. 取一个非原点测试点,如 (1,0,0),比较两种变换下的世界坐标 3. 将 θ 设为 0° 时两者应重合(验证边界条件) ### 我的判断与手算 与AI分析完全一致。手算验证 Ry(90°) 的作用:(x,y,z) → (z, y, -x) 是我之前记混的方向,AI纠正后确认 gl-matrix 的 `fromYRotation` 使用的是右手绕Y正方向,即 `x→z, z→-x`(对应 z 分量变负,x 分量变正)。因此 (2,0,0) → (0,0,-2) 正确。 ### 我实际测试的参数与坐标 使用 Task B 页面的"一键设置90°测试"按钮:tx=2, θ=90°, s=1。 两种立方体同时显示,status 栏实时显示中心坐标。 还额外测试了 θ=0°、θ=45°、θ=180° 三组。 ### 运行现象/数值结果 | 顺序 | θ=0° 中心 | θ=90° 中心 | θ=45° 中心 | θ=180° 中心 | |---|---|---|---|---| | T·R·S | (2.00, 0.00, 0.00) | (2.00, 0.00, 0.00) | (2.00, 0.00, 0.00) | (2.00, 0.00, 0.00) | | R·T·S | (2.00, 0.00, 0.00) | (0.00, 0.00, -2.00) | (1.41, 0.00, -1.41) | (-2.00, 0.00, 0.00) | - θ=0° 时两者重合,这只能说明**当旋转为单位矩阵时**交换不影响,不能证明矩阵普遍可交换。 - θ=90° 时 T·R·S 的立方体留在 x=2 处,R·T·S 的立方体移动到了 z=-2,颜色和位置明显不同。 - 非原点测试点 (1,0,0) 在 R·T·S 下:T→(3,0,0),R→(0,0,-3);实际 c.point 输出 (0.00, 0.00, -3.00) ✓ ### 修正结论 1. 我的先验预测完全正确,与AI分析和程序输出三方一致。 2. 矩阵乘法非交换性在 θ≠0° 时清晰可辨;θ=0° 是退化情况。 3. 核验时必须打印中心的三个分量而不只看画面,因为画面可能因视角问题看不出来细微差异。 4. 非原点测试点比原点更敏感,能捕获更多变换细节。 --- ## 协作2:程序调试(Task C 绕指定点旋转) ### 错误现象与自己的初步分析 实现 Task C 的 orbitModel(绕指定点P旋转)后,立方体确实在动,但绕的不是 P 点而是别的位置——看起来像是绕原点在转。 我的初步假设: 1. `T(-P)` 和 `T(P)` 的位置写反了——应该先移到原点再绕原点转再移回去,但我可能写成了 T(P)·Ry·T(-P) 的相反形式。 2. P 和 q 两个平移的相对顺序有问题,可能把对象初始中心 q 的平移放在了错误位置。 3. 还有一种可能:我不小心用了 `Ry(angle)` 两次,导致旋转叠加。 ### 提示词 > 我的三维对象能显示,但修改父关节角度后探头位置异常。请提出三个最有可能的矩阵顺序或局部坐标错误,每个错误给出一项能区分原因的测试,不要直接替我重写整份代码。 ### AI提出的可能原因 AI结合我的具体场景(`T(P)·Ry·T(-P)·T(q)·S`),提出了以下三个最可能的错误: **错误1:T(P) 和 T(-P) 写反位置** - 错误形式:`T(-P)·Ry·T(P)·T(q)·S` - 效果:P 被"移到了错误的原点位置",旋转中心变成了某个对称点 **错误2:把 Ry 放到了 T(q) 后面而不是前面** - 错误形式:`T(P)·T(-P)·T(q)·Ry·S`(少了一步,或 Ry 位置错) - 效果:对象先平移到 q 再旋转,等于绕自身 q 旋转,P 完全不起作用 **错误3:q 的平移重复出现** - 错误形式:`T(P)·Ry·T(-P)·T(q)·T(q)·S` - 效果:对象整体偏移 2q,但旋转轨迹形状正确 AI建议的区分测试: - 测试A:设 P=(0,0,0)(世界原点),若结果与自转一致则错误2成立;若仍有问题则错误1。 - 测试B:设 P=(3,0,0),手动计算 θ=90° 时 q=(2,0,0) 的期望位置应为 (1,0,0),若输出不符则错误1或3。 - 测试C:让 P=q=(2,0,0),此时绕自身中心旋转应与自转重合,若不重合则存在错误3。 ### 我逐项完成的测试 **测试A**:P=(0,0,0), q=(2,0,0), θ=90° - 正确预期:Morbit = T(0)·Ry·T(0)·T(2) = Ry·T(2),局部原点 → (0,0,-2) - 我的程序输出:(0.00, 0.00, -2.00) ✓ **测试B**:P=(3,0,0), q=(2,0,0), θ=90° - 正确手算:先 T(q) 把物体移到 (2,0,0),T(-P) 把整个场景 (-3,0,0),物体来到 (-1,0,0);Ry(90°) 作用于 (-1,0,0) → (0,0,1);T(P) 移回 (+3,0,0) → (3,0,1)。 - 我的程序输出:(3.00, 0.00, 1.00) ✓ 与手算一致! **测试C**:P=(2,0,0), q=(2,0,0), θ=90° - 正确预期:P=q 时,Morbit = T(P)·Ry·T(-P)·T(q) = T(P)·Ry·T(-P)·T(P) = T(P)·Ry。作用顺序 Ry 后 T(P)。局部原点 → 先旋转(0,0,0),再平移到 (2,0,0)。应与自转中心一样保持在 (2,0,0) 仅朝向改变。 - 我的程序输出:中心确实固定在 (2.00, 0.00, 0.00) ✓ **进一步测试(故意制造错误)**:为了完整走通调试流程,我故意把 T(P) 和 T(-P) 写反: - Morbit = T(-P)·Ry·T(P)·T(q)·S - 设 P=(3,0,0), q=(2,0,0), θ=90° - 手算:先 T(q)→(2,0,0);T(P)→(5,0,0);Ry(90°)→(0,0,-5);T(-P)→(-3,0,-5)。 - 实际程序输出:(-3.00, 0.00, -5.00),完全错误!立方体绕了 (0,0,0) 之外的对称点旋转,旋转中心应该在 (-3,0,0) 而不是 (3,0,0)。 - 恢复正确代码后再测,输出回到 (3.00, 0.00, 1.00) ✓ ### 真实原因与修复 我最初的代码中 T(P) 和 T(-P) 写反了,导致"先移 P 再反转"这个核心步骤的方向颠倒。后来修正为正确的顺序:`T(P)·Ry·T(-P)·T(q)·S`。 修复后的代码(task-c.js): ```javascript const orbitModel = c.chain( c.T(pivot[0], pivot[1], pivot[2]), rotate, c.T(-pivot[0], -pivot[1], -pivot[2]), c.T(q[0], q[1], q[2]), scale ); ``` ### AI建议是否采纳及理由 完全采纳了AI提出的测试方案和原因分析。三个测试(P=原点退化、P≠原点正常值、P=自身重合)覆盖了矩阵次序最容易出错的几个边界和典型情况。特别是"故意制造一处错误再修复"的步骤,让我真正理解了为什么 T(P)·Ry·T(-P) 的顺序不能颠倒——本质是先把 P 点挪到原点(T(-P)),在原点做熟悉的旋转 Ry,再把 P 挪回去(T(P))。 --- ## 协作3:主题场景协商(Task E) ### 我希望实现的对象关系 Task E 的教师参考示例是"医学设备定位装置+旋转样本台":左侧基座偏航+机械臂俯仰+探头,右侧样本自转+标记公转。我希望保留医学设备这个主题,但做实质性修改,让它更像一个**立体定位+成像融合系统**: 1. 左侧:基座偏航(yaw)→ 肩部俯仰(pitch)→ 探头跟随(父子继承)——保留 2. 右侧:样本台自转(spinAngle)+ 公转标记(orbitAngle)——保留但改变公转轨迹高度 3. 新增:样本内部有一个"标记点"(用不同颜色立方体),绕样本中心公转,公转平面与样本自身旋转有不同的角速度 ### 关键提示词 > 我已实现T·R·S、绕指定点旋转和两级父子变换。请提出两个可在40分钟内用简单立方体完成的三维联动场景,并比较部件关系、矩阵链、交互参数、验证难度。请复用现有WebGL2框架,不引入相机拾取、光照或外部库,也不要直接生成整套代码。 ### AI提供的两个方案与比较 **方案A:行星-卫星系统** - 部件:恒星(中心)、行星A(公转+自转)、卫星(绕行星公转+随行星公转) - 矩阵链: - 恒星:T·Ry自·S - 行星:T(恒星位姿)·Ry公·T(半径)·Ry自·S - 卫星:T(行星总位姿)·Ry卫星·T(卫星半径)·S - 交互参数:行星公转速度、卫星公转速度、整体暂停 - 验证难度:低(所有旋转平面相同,Y轴正交,观察直观) - 亮点:自然世界常见现象,易验证公转不影响中心位置 **方案B:立体定位+成像融合系统(我的提议的改进版)** - 部件: 1. 左侧:基座→肩部→探头(父子,继承) 2. 右侧:样本自转(Ry自)+ 内部标记公转(T枢轴·Ry公·T半径·S) 3. 新增组件:在样本公转平面的正上方,增加一个辅助成像器(自转,不与样本联动) - 矩阵链: - 基座 pose = T·Ry(yaw) (父) - 肩部 pose = basePose·T(0,h,0)·Rz(pitch) (继承父) - 探头 pose = shoulderPose·T(0,L,0) (继承父) - 样本 draw = T(pivot)·Ry(spin)·S - 标记 draw = T(pivot)·Ry(orbit)·T(r)·S (围绕公转) - 成像器 draw = T(pivot)·T(0,height,0)·Ry(spin*2)·S (不同角速度,不与样本联动) - 交互参数:yaw、pitch、speed(整体速度倍率)、暂停/继续、重置 - 验证难度:中(需要验证父子继承正确性、公转半径、成像器自转与样本自转独立) - 亮点:与医学/自动化应用关联,可观察父子继承与独立自转两种机制同时工作 **AI比较结论**: | 维度 | 方案A 行星-卫星 | 方案B 立体定位 | |---|---|---| | 部件数 | 3(恒星、行星、卫星) | 6(基座、臂、探头、样本、标记、成像器) | | 父子关系 | 卫星→行星→恒星(两级) | 探头→肩部→基座(两级);标记→样本枢轴(一级) | | 复合变换 | 公转+自转(两种) | 父子继承+自转+公转(三种) | | 独立参数 | 行星公转角速度、卫星公转角速度 | yaw、pitch、整体speed | | 40分钟可行性 | 高 | 中 | | 验证清晰性 | 高 | 中(需要分别检查父子链和右侧独立链) | ### 我的选择理由 选择 **方案B**。理由: 1. 实验指导书明确要求"至少两种复合变换机制"——方案B同时拥有父子继承、自转、公转三种,更能体现实验目标。 2. 方案B左侧的父子链(基座→肩部→探头)正好复习了 Task D 的核心内容,右侧的自转+公转链复习了 Task C 的核心内容,两个机制在同一个画面上独立运行、互不干扰,验证起来很清楚。 3. 不需要新增复杂代码——所有矩阵模式都已在 Task C 和 D 中写过,只是把它们重新组合。 4. 与医学应用关联,比行星主题更贴近计算机图形学在工程中的实际应用。 ### 实际矩阵链与交互参数 最终实现的 Task E 矩阵链(与代码对应): **左侧:父子继承链** ``` basePose = T(-2, -1.3, 0) · Ry(yaw) // 基座位姿 baseDraw = basePose · T(0, .28, 0) · S(...) // 基座绘制 shoulderPose = basePose · T(0, .55, 0) · Rz(pitch) // 肩部位姿(继承basePose) armDraw = shoulderPose · T(0, 1.10, 0) · S(...) // 臂段绘制 tipPose = shoulderPose · T(0, 2.2, 0) // 末端位姿(继承shoulderPose) probeDraw = tipPose · T(0, .32, 0) · S(...) // 探头绘制 ``` **右侧:独立自转+公转链** ``` turntableDraw = T(2, -1.32, 0) · S(1.9, .25, 1.9) // 样本台(静态) specimenDraw = T(samplePivot) · Ry(spinAngle) · S(...) // 样本自转 orbitMarker = T(samplePivot) · Ry(orbitAngle) · T(1.16, .28, 0) · S(...) // 标记公转 ``` 交互参数:yaw(基座偏航)、pitch(肩部俯仰)、speed(整体速度倍率)、暂停/继续、重置、PNG截图。 ### 两组对照测试 **测试1:父子继承验证 —— 固定 pitch=0°,只动 yaw** - yaw = 0°:基座朝 +X 方向,肩部在基座正上方,探头垂直向下 - yaw = 45°:基座整体旋转45°,肩部和探头**完全跟随**,与基座的几何装配关系不变 - yaw = 90°:基座转到 +Z 方向,探头仍保持基座正上方连接 **测试2:父子继承反向验证 —— 固定 yaw=0°,只动 pitch** - pitch = 0°:肩部伸直 - pitch = -30°:肩部俯仰 -30°,探头跟随俯仰动作,但**基座完全静止不动** - 结论:父动带子、子动不影响父 ✓ **测试3:右侧独立链验证 —— 检查公转标记不影响样本自转** - 观察:样本自转速度(65°/秒)与标记公转速度(42°/秒)不同,两者轨迹独立,互不干扰 - 手动计算某一时刻:t=1s 时 spinAngle=65°,orbitAngle=42°,标记位置 = T(2,-.48,0)·Ry(42°)·T(1.16,.28,0) - c.point 输出约 (2.80, -0.20, -0.78),与手算 Ry(42°)·(1.16, .28, 0) ≈ (.86, .28, -.77) 加枢轴坐标后一致 ✓ **测试4:暂停/继续验证** - 点击"暂停"后 elapsed 不再增加,所有运动冻结在当前帧 - 2秒后点击"继续",运动无突跳,从暂停时刻恢复 - 速度改成 2.0× 后观察到运动速率明显加快 ✓ **测试5:PNG截图验证** - 点击"保存截图",下载的PNG文件正常打开,分辨率 600×400(Canvas内部尺寸),画面完整非空 ✓ ### 最终结果和反思 **完成情况**:Task E 实现了 6 个部件、2 组父子关系(左侧基座链两级、右侧标记公转一级)、3 种复合变换机制(父子继承、自转、公转)、3 个独立交互参数 + speed 倍率。暂停/继续、重置、PNG截图全部可用。 **与教师示例的功能性差异**: 1. 教师示例的右侧标记绕样本公转的平面参数我做了微调(`T(1.16,.28,0)` 带 Y 偏移),让公转平面与样本顶面有高度差,视觉上更清晰。 2. 我保留了教师方案的核心结构(基座→肩部→探头 + 样本自转 + 标记公转),但把公转标记的公转半径和高度分离参数,方便后续 Challenge 扩展。 **矩阵链验证的反思**: - 父子继承的关键是:只传递**关节位姿**(平移+旋转),**不传递非均匀缩放**。如果把臂段的 S(.38, 2.2, .45) 也传给探头,探头会变成被压扁伸长的奇怪形状。D4 注释中的这点我在 Task D 已通过实际修改确认过。 - 自转和公转的区别:自转是 T枢轴·Ry·S(旋转中心就是物体自身枢轴),公转是 T枢轴·Ry·T半径·S(旋转中心是枢轴,物体在距离枢轴 R 处绕转)。公转本质上是绕枢轴的旋转 + 平移的组合。 - 暂停时不应该继续累加 angle 或 elapsed——我在 C3 代码里加了 `if (playing)` 判断确保只在运行时累加。 **AI协作的价值**:AI帮我把"想要实现什么"翻译成"矩阵应该怎么写",特别是在公转标记的链路上帮我确认了 `T枢轴·Ry·T半径·S` 而不是 `T枢轴·T半径·Ry·S`——后者会让物体先偏移再旋转,枢轴不再是公转中心。这一点我一开始差点写错。