当你想认真研究一套控制算法,却又要从硬件接线、资料收集开始一路摸爬滚打时,很容易被各种零散信息劝退。这篇内容是我用 AI 辅助学习“四电机绳驱控制算法”的第一份整理笔记,把运动学建模、PID 位置控制、张力分配和完整 Python 仿真串成一条可复现的学习路径。
之所以把“四电机绳驱”作为第一集,是因为它既包含多电机协同、冗余驱动,又涉及绳索这类只能“拉”不能“推”的执行结构。理解它之后,再看并联机器人、绳索机械臂、张力反馈系统都会轻松很多。
1. 背景与核心概念
1.1 什么是四电机绳驱系统
四电机绳驱系统,简单说是用四台电机、四条绳索驱动一个末端执行器或动平台。每根绳索一端绕在电机驱动的绞盘上,另一端连接末端执行器。通过收放不同绳索的长度,末端执行器就能在平面或空间内移动。
它和我们熟悉的关节型机械臂不同。工业机械臂通常每个关节有一个电机,关节之间通过刚性连杆连接。绳驱系统的特点是:
- 电机不一定安装在末端执行器附近,可以把电机集中放在基座或固定框架上,末端质量更小。
- 绳索只能承受拉力,不能承受推力,这是控制时必须处理的约束。
- 驱动冗余度高,四个电机驱动平面内两个自由度,存在冗余配置空间。
如果按平面内二维运动来理解,末端执行器本来只需要两个独立驱动就能到达平面内任意位置,但四电机绳驱仍然被广泛使用,是因为它有更好的力封闭能力、更大的工作空间,以及更灵活的结构布局。
1.2 控制算法要解决的核心问题
四电机绳驱系统的控制问题可以拆成两层:
- 位置控制:让末端执行器从当前位置移动到目标位置,并保持稳定。
- 张力分配:决定每根绳索需要输出多大的张力。
这两层问题是相互耦合的。末端执行器的加速度由四根绳索的张力和方向共同决定,而绳索方向又取决于末端当前位置与锚点位置。也就是说,只有把运动学、动力学和控制算法放在一起看,才能理解整个系统。
控制算法的核心任务,就是在满足“绳索张力必须为正”“绳索长度不能超出物理范围”等约束的前提下,让末端执行器按期望轨迹运动。
1.3 用 AI 辅助做教程的思路
传统学习路线通常是:找教材、读论文、复现仿真、调参数、做实物。这个过程并没有什么问题,但在入门阶段,容易卡在公式推导不顺畅、代码不知道从哪里写起、报错信息看不懂这些点上。
AI 辅助学习不是替代学习,而是把“查资料、写代码、排错”这些环节的响应速度提上来。例如:
- 输入一个控制算法关键词,让 AI 先用通俗语言解释。
- 让 AI 生成一段 Python 仿真框架,再自己补充动力学模型。
- 把报错信息粘贴给 AI,快速定位是维度问题、数据类型问题还是算法逻辑问题。
这套流程的优点是把学习重心放在“理解模型、验证算法、分析结果”上,而不是消耗在格式问题和语法报错里。与此同时,AI 生成内容不一定完全正确,需要认真核对。
2. 环境准备与仿真模型设计
2.1 软件环境
本文的仿真代码使用 Python 编写,不依赖特定硬件。建议环境如下:
- 操作系统:Windows、Linux、macOS 均可。
- Python:3.9 或以上版本。
- 第三方库:NumPy、SciPy、Matplotlib。
版本不必完全一致,只要保证两个核心功能可用:矩阵运算和线性最小二乘求解。SciPy 的lsq_linear函数在张力分配时会用到,它比手写伪逆加截断要规范一些。
如果本机还没有安装第三方库,可以参考下面的命令:
pip install numpy scipy matplotlib也可以创建一个虚拟环境,避免和系统 Python 环境互相影响:
python -m venv cable_env source cable_env/bin/activate # Windows 使用 cable_env\Scripts\activate pip install numpy scipy matplotlib2.2 系统参数与符号约定
为了方便建模,本文使用一个平面内四电机绳驱系统作为例子。四个锚点位于正方形框架的四角:
- 锚点 A 坐标为 (-1.0, 1.0)
- 锚点 B 坐标为 (1.0, 1.0)
- 锚点 C 坐标为 (1.0, -1.0)
- 锚点 D 坐标为 (-1.0, -1.0)
末端执行器当作一个质量为 1 kg 的质点,在平面内运动。重力加速度方向指向 y 轴负方向,大小为 9.8 m/s²。每根绳索理论上只能输出正张力。
符号定义如下:
- (p):末端位置向量,(p = [x, y]^T)
- (v):末端速度向量
- (a):末端加速度向量
- (l_i):第 i 根绳索的长度
- (T_i):第 i 根绳索的张力
- (g):重力加速度向量
这些参数会被写进仿真代码中,后续所有控制算法都基于这套模型展开。
2.3 项目结构
为了让代码更容易维护,建议把运动学、控制器和仿真主循环拆分成不同文件。目录结构如下:
four_motor_cable/ ├── main.py ├── kinematics.py ├── controller.py ├── simulation.py └── requirements.txt模块职责划分得越清晰,后面替换控制算法时就越方便。例如想从 PID 换成 LQR,只需要修改controller.py,不必改动运动学文件。
3. 核心概念拆解:从运动学到控制算法
3.1 运动学:绳索长度与末端位置的关系
已知末端位置 (p),可以计算第 i 根绳索的长度:
[ l_i = | anchor_i - p | ]
这里 (anchor_i) 是第 i 个锚点坐标。这个公式非常简单,但它决定了位置控制的基本逻辑:如果我们想让末端到达某个目标位置,可以反推出四根绳索的目标长度,然后控制每个电机将绳索收放到对应长度。
末端位置变化时,绳索方向也在变化。从末端指向锚点的单位向量是一个关键量:
[ u_i = \frac{anchor_i - p}{| anchor_i - p |} ]
这个单位向量表示绳索拉力的方向,在计算合力时必不可少。
需要注意的是,当末端位置非常接近锚点时,绳子长度趋近于 0,单位向量会出现数值不稳定。真实系统中也应当避免末端进入锚点附近区域。
3.2 静力学:张力、合力与定向
四根绳索同时拉动末端执行器,每根绳索产生的拉力为:
[ F_i = T_i \cdot u_i ]
末端受到的合力就是四根绳索拉力与重力之和:
[ F_{total} = \sum_{i=1}^{4} T_i \cdot u_i + m \cdot g ]
如果给定期望加速度 (a_{des}),根据牛顿第二定律:
[ m \cdot a_{des} = F_{total} ]
可以得到绳索张力需要满足的力平衡条件:
[ \sum_{i=1}^{4} T_i \cdot u_i = m \cdot a_{des} - m \cdot g ]
这个方程看起来简单,但真正的难点在于:四根绳索要在一个二维平面力平衡方程中分配张力,未知数有 4 个,方程只有 2 个,系统是冗余的。
3.3 位置环 PID 控制
在控制系统中,最常用的位置控制算法是 PID。PID 控制器的输出基于三部分:
- 比例项:根据当前位置与目标位置的偏差输出控制量。
- 积分项:消除稳态误差。
- 微分项:抑制速度过快变化,增加阻尼。
在本文的仿真中,PID 输出的是一个期望加速度:
[ a_{des} = K_p \cdot e + K_i \cdot \int e , dt - K_d \cdot v ]
其中 (e = p_{des} - p) 表示位置误差。这里使用 ( -K_d \cdot v ),而不是对误差求导,因为速度本身就是误差的变化率,直接对速度做阻尼会更稳定。
PID 参数整定没有固定万能公式。一般先从小到大调比例增益 (K_p),波形开始振荡后加入微分增益 (K_d),最后再考虑积分项。积分增益 (K_i) 不宜过大,否则容易产生超调。
3.4 张力分配:四电机的冗余自由度
力平衡方程中,4 个张力未知数,2 个力平衡方程,意味着系统有 2 个冗余自由度。这种冗余可以让系统:
- 在约束范围内寻找最小张力解,降低能耗。
- 增加预紧力,避免绳索松驰。
- 在某些绳索失效时仍尽量维持控制。
最常见的做法是求解一个最小二乘问题:
[ \min_{T} \frac{1}{2} | T |^2 ]
约束条件为:
[ A \cdot T = b ] [ 0 \le T_i \le T_{max} ]
其中矩阵 (A) 的每一列就是绳索单位向量 (u_i),向量 (b) 是期望合力。这里必须加上张力非负约束,因为绳索不能“推”。
如果忽略约束,直接用伪逆可以得到最小范数解,但结果可能包含负张力,直接截断会产生控制偏差。因此工程中推荐使用带边界约束的优化求解器。
3.5 常见控制算法对比
除了 PID,绳驱系统还会用到其他控制算法。下方表格适用于学习路线中的选型参考:
| 算法 | 适用场景 | 优点 | 缺点 | 复杂度 |
|---|---|---|---|---|
| PID | 位置环、速度环 | 简单直观,实现成本低 | 调参依赖经验,抗扰动能力一般 | 低 |
| LQR | 线性化模型的最优控制 | 能处理多变量耦合,理论成熟 | 需要线性化模型,约束处理弱 | 中 |
| ADRC | 参数不确定、扰动较大的系统 | 能估计并补偿总扰动 | 参数较多,需要理解扩张状态观测器 | 中高 |
| MPC | 带约束的轨迹跟踪 | 天然处理张力非负、绳长限位等约束 | 在线计算量大,模型不准会影响效果 | 高 |
| Stanley | 轨迹跟踪横向控制 | 在移动机器人轨迹跟踪中效果好 | 直接用于绳驱需要改造模型 | 中 |
| 模糊 PID | 非线性、难以精确建模的系统 | 根据经验规则调整 PID 参数 | 规则设计依赖经验,可解释性一般 | 中 |
| 神经网络控制 | 强非线性、强耦合系统 | 不依赖精确建模,表达能力强 | 数据要求高,稳定性分析较难 | 高 |
这张表不是为了比出“谁最强”,而是帮助你理解不同算法的适用边界。入门阶段建议先把 PID 和运动学吃透,再逐步接触 LQR 和 MPC。
4. 完整 Python 仿真与 AI 辅助实现
4.1 创建项目结构
在命令行中创建项目目录,并进入目录:
mkdir four_motor_cable cd four_motor_cable然后按照规划创建四个 Python 文件。可以先建立requirements.txt:
numpy scipy matplotlib这一行文件不是必须的,但放到项目中可以方便自己和他人安装依赖。
4.2 编写运动学模块
kinematics.py负责计算绳索长度和单位方向向量。代码如下:
# four_motor_cable/kinematics.py import numpy as np # 四个锚点坐标:A 左上,B 右上,C 右下,D 左下 ANCHORS = np.array([ [-1.0, 1.0], [ 1.0, 1.0], [ 1.0, -1.0], [-1.0, -1.0], ]) def cable_lengths(p, anchors=ANCHORS): """根据末端位置 p 计算四根绳索的长度。""" delta = anchors - p return np.linalg.norm(delta, axis=1) def cable_unit_vectors(p, anchors=ANCHORS): """根据末端位置 p 计算四根绳索的单位方向向量。 方向从末端指向锚点,代表拉力方向。 """ delta = anchors - p dist = np.linalg.norm(delta, axis=1) # 防止末端与锚点重合时出现除零 dist = np.where(dist < 1e-6, 1e-6, dist) return delta / dist.reshape(-1, 1), distcable_lengths用于逆运动学,cable_unit_vectors用于后续的合力计算和张力分配。代码中把除零保护提前做了,仿真过程中即使末端离锚点很近,也不会直接崩溃。
4.3 编写控制器模块
controller.py里包含位置 PID 控制器和张力分配函数。代码如下:
# four_motor_cable/controller.py import numpy as np from scipy.optimize import lsq_linear class PositionPID: """平面内末端位置 PID 控制器。""" def __init__(self, kp, ki, kd, dt): self.kp = np.array(kp, dtype=float) self.ki = np.array(ki, dtype=float) self.kd = np.array(kd, dtype=float) self.dt = dt self.integral = np.zeros(2) def compute(self, p, v, p_des): """根据位置误差和速度计算期望加速度。""" error = p_des - p self.integral += error * self.dt # 抗积分饱和,限制积分累计 self.integral = np.clip(self.integral, -0.5, 0.5) a_des = ( self.kp * error + self.ki * self.integral - self.kd * v ) return a_des def allocate_tensions(unit_vecs, F_des, T_max=50.0): """通过最小二乘方法分配四根绳索的张力。 参数: unit_vecs: 形状为 (4, 2) 的绳索单位方向向量 F_des: 期望合力,二维向量 T_max: 最大张力限制 返回: T: 四根绳索的张力数组 """ A = np.array(unit_vecs).T # 矩阵形状为 (2, 4) res = lsq_linear(A, F_des, bounds=(0, T_max)) return res.xPositionPID.compute的核心是输出期望加速度。积分项加了clip限制,防止长时间位置误差导致积分项无限增大。这种做法在工程上叫“抗积分饱和”。
张力分配函数使用了scipy.optimize.lsq_linear,它能在 (0 \le T_i \le T_{max}) 的边界条件下寻找最优解。虽然它是在最小二乘意义下求解,不能严格保证等式约束一定成立,但对于二维平面绳驱系统,只要期望合力在可行范围内,残差一般很小。
4.4 编写仿真主循环
simulation.py负责把运动学、控制器、张力分配和动力学模型串联起来。代码如下:
# four_motor_cable/simulation.py import numpy as np from kinematics import cable_unit_vectors from controller import PositionPID, allocate_tensions def run_simulation(p0, p_des, dt=0.01, total_time=6.0): """运行四电机绳驱系统仿真。 参数: p0: 末端初始位置,例如 [0.4, -0.2] p_des: 末端目标位置,例如 [0.0, 0.3] dt: 仿真步长,单位秒 total_time: 总仿真时间,单位秒 返回: traj: 末端轨迹,形状为 (N, 2) tensions: 四根绳索张力历史,形状为 (N, 4) """ p = np.array(p0, dtype=float) v = np.zeros(2) m = 1.0 g = np.array([0.0, -9.8]) controller = PositionPID( kp=[8.0, 8.0], ki=[0.2, 0.2], kd=[4.0, 4.0], dt=dt ) steps = int(total_time / dt) traj = np.zeros((steps, 2)) tensions = np.zeros((steps, 4)) for i in range(steps): # 计算当前绳索方向 unit_vecs, _ = cable_unit_vectors(p) # 位置 PID 生成期望加速度 a_des = controller.compute(p, v, p_des) # 期望合力 = m * a_des - m * g F_des = m * a_des - m * g # 张力分配 T = allocate_tensions(unit_vecs, F_des) # 实际合力 F_actual = unit_vecs.T @ T # 系统加速度 a = (F_actual + m * g) / m # 半隐式欧拉积分 v += a * dt p += v * dt # 保持末端始终在可达范围内 p = np.clip(p, -0.9, 0.9) traj[i] = p tensions[i] = T return traj, tensions仿真主循环的思路是这样的:
- 用当前位置计算绳索方向向量。
- PID 控制器根据位置误差生成期望加速度。
- 根据动力学方程反推期望合力。
- 在张力非负约束下分配四根绳索的张力。
- 用实际合力计算加速度,并更新速度和位置。
半隐式欧拉积分是先更新速度,再用新速度更新位置。它比显式欧拉更稳定,适合入门学习。
4.5 运行与结果可视化
main.py负责设置初始条件、目标位置,并绘制末端轨迹:
# four_motor_cable/main.py import numpy as np import matplotlib.pyplot as plt from simulation import run_simulation from kinematics import ANCHORS if __name__ == "__main__": p0 = np.array([0.4, -0.2]) p_des = np.array([0.0, 0.3]) traj, tensions = run_simulation( p0=p0, p_des=p_des, dt=0.01, total_time=6.0 ) print("仿真完成,轨迹点数:", traj.shape[0]) print("末端最终位置:", traj[-1]) print("期望位置:", p_des) plt.figure(figsize=(8, 8)) plt.scatter(ANCHORS[:, 0], ANCHORS[:, 1], marker="s", s=80, label="anchor") plt.plot(traj[:, 0], traj[:, 1], linewidth=2, label="trajectory") plt.plot(p0[0], p0[1], "go", label="start") plt.plot(p_des[0], p_des[1], "r*", markersize=16, label="target") plt.legend() plt.axis("equal") plt.grid(True) plt.title("Four-motor cable system: trajectory") plt.savefig("trajectory.png", dpi=150) plt.show() # 查看张力变化 plt.figure(figsize=(8, 5)) for i in range(4): plt.plot(tensions[:, i], label=f"T{i+1}") plt.xlabel("step") plt.ylabel("Tension") plt.legend() plt.grid(True) plt.title("Tension of four cables") plt.savefig("tensions.png", dpi=150) plt.show()运行代码:
python main.py预期结果是末端从起点逐步移动到目标位置附近,整个过程中四根绳索的张力都保持非负。如果 PID 参数合适,末端轨迹会平滑收敛,不会出现剧烈震荡。
trajectory.png展示四根锚点、起点、终点和末端运动轨迹。tensions.png展示四根绳索张力的变化过程。
5. 用 AI 做教程的实用经验
5.1 用 AI 拆解概念与推导公式
学习控制算法时,最大障碍往往是公式符号多、物理含义不清晰。AI 可以把抽象公式转成通俗语言。例如直接提问:为什么要对张力做最小二乘分配?AI 会回答,因为未知数比方程多,存在无穷多组解,需要选一组最优的。
但这里必须提醒一点:AI 给出的解释不一定有物理直觉。让它解释“绳索只能拉不能推”这个约束时,可以再追问一句:如果不加非负约束,伪逆解会出现什么现象?通过这种追问,可以更深入理解张力分配的本质。
AI 适合当“陪练”,不适合当“权威”。关键公式建议自己推导一遍,再用小算例验证结果。
5.2 用 AI 生成代码与测试用例
AI 在生成代码框架方面效率很高。以本文的控制器为例,你可以先写一个大纲:
- 输入:末端位置、末端速度、目标位置。
- 输出:期望加速度。
- 限制:积分项需要抗饱和。
然后让 AI 补全代码。真正需要自己做的是调试和验证,例如检查矩阵维度是否正确、单位向量方向是否合理、张力是否可能为负。
还可以让 AI 生成边界测试用例:
- 初始位置在中心点。
- 目标位置靠近锚点。
- 目标位置超出可达范围。
- 目标位置非常远,张力上限会被触发。
这些测试能暴露控制器的薄弱点。
5.3 用 AI 做初步排错
仿真中遇到报错时,将完整报错信息和相关代码粘贴给 AI,通常能快速定位问题。常见问题包括:
- ndarray 形状不匹配。
- np.linalg.norm 的 axis 参数用错。
- lsq_linear 的边界参数类型不对。
- matplotlib 绘图时传入单个点坐标导致尺寸错误。
AI 排错的思路大多是“先看报错位置,再看数据类型和维度”。这和学习者自己排查的方向一致,只是速度更快。但遇到涉及物理意义的错误,AI 可能意识不到问题,需要结合控制理论判断。
5.4 如何识别和避免 AI 幻觉
AI 幻觉是使用 AI 学习时最需要警惕的问题。所谓幻觉,是指 AI 生成了看起来合理、实际错误的内容。
例如 AI 可能在解释 LQR 时输出错误的矩阵维度,或者把 ADRC 的扩张状态观测器阶数弄错。这些问题在一段文字的“流畅性”掩盖下,很难一眼发现。
比较有效的防御方法:
- 交叉验证:同一个问题用不同方式问多次,对比回答。
- 小规模验证:让 AI 给一个 2x2 矩阵算例,手算核对。
- 对照教材:遇到关键定理和公式,以经典教材或论文为准。
- 实验验证:代码能不能跑通、控制目标能不能达到,是最后的裁判。
在“用 AI 写文章骗不了人了”这类讨论的背后,本质是人们对内容质量要求变高了。技术教程尤甚,因为读者会拿着代码运行,错误会在几分钟内暴露。
6. 常见问题与排查思路
6.1 仿真发散或剧烈震荡
末端位置在目标点附近来回震荡,通常是因为 PID 参数过大或积分项过强。可以尝试降低 (K_p),增加 (K_d),把系统阻尼提上来。
如果初始误差很大,比例项会输出很大的期望加速度,瞬时张力容易达到上限,造成非线性饱和。解决办法是不要直接给阶跃目标,而是规划一条平滑轨迹。
6.2 张力出现负值或不可行
如果绕过优化直接使用伪逆,负张力很容易出现。造成负张力的本质是期望合力超出了当前绳索布局能提供的范围,或者目标点位于可达工作空间之外。
解决办法是使用带边界约束的lsq_linear,并检查优化残差。如果残差过大,说明期望合力可能不可行,需要降低控制增益或调整目标点。
6.3 末端靠近锚点时算子异常
绳索长度接近 0 时,单位方向向量计算会不稳定。虽然代码中加了1e-6的下限保护,但这种保护只是避免崩溃,并不能让控制变得合理。
真实系统中,末端靠近锚点意味着该绳索几乎没有控制能力,同时很容易触发最大张力。建议在轨迹规划阶段就把它当作禁区处理。
6.4 AI 生成代码运行失败
AI 生成的代码经常会出现类名、函数名不存在,或者不同库版本接口不一致的问题。例如scipy.optimize.lsq_linear的边界参数要求是元组列表,写成数组可能导致报错。
遇到这种情况,先检查运行环境,再用最小化复现法定位问题。把出错代码缩减到最小规模,再让 AI 基于这段精简代码修复。
6.5 目标位置不可达
四电机绳驱在平面内的可达空间并不是整个正方形平面。绳索只能拉不能推,末端在任何位置都必须满足“四根绳索能提供足够合力”的条件。目标点太靠近边界、太靠近锚点,都可能不可达。
仿真前可以先写一个静态可达性检查函数,在任意位置计算绳索方向矩阵的可行性,避免控制算法在不可达目标上浪费调整时间。
7. 工程化最佳实践建议
7.1 仿真与实物之间的差距
仿真里把绳索当作理想直线、忽略质量、忽略弹性、忽略电机动态,这种简化在入门阶段是有必要的。但实物系统中这些因素都会影响控制效果。
把仿真算法迁移到实物时,至少还要增加:
- 电机速度环和电流环。
- 绳索弹性和松弛检测。
- 绞盘半径和码盘反馈标定。
- 张力传感器或力估计器。
建议先用软硬件在环仿真验证算法,再切换到低速小负载的实物测试。
7.2 张力分配的正规解法
本文为了降低门槛,使用了带边界的lsq_linear。在实际工程中,张力分配更常用的做法是二次规划:
[ \min_{T} \frac{1}{2} T^T W T ]
约束条件为等式力平衡和张力边界。引入权重矩阵 (W) 后,可以控制各绳索张力的优先级,比如让某些绳索承担更大的预紧力。
二次规划也比最小二乘更直观地处理“等式约束必须满足”的需求。如果系统可行,求解结果会严格满足力平衡方程。
7.3 轨迹规划与平滑
直接给阶跃目标会让控制算法长期处于大误差状态,好处是代码简单,坏处是容易超调和触达张力上限。工程中更推荐规划一条目标轨迹,例如使用 S 形速度规划或贝塞尔曲线。
轨迹规划的意义不只是让运动更好看,更是让控制器始终工作在线性区间附近,减小积分饱和和非线性影响。
7.4 控制算法学习路线
如果从零开始系统学习控制算法,建议按照这个顺序展开:
- 掌握 PID,理解比例、积分、微分的物理意义。
- 学会建立运动学模型,至少要能写出正解和逆解。
- 理解动力学模型,明白力和加速度之间的关系。
- 学习状态空间表达,掌握 LQR 等现代控制方法。
- 学习带约束的最优控制,例如 MPC。
- 根据实际系统需求,再研究 ADRC、模糊 PID、神经网络控制等进阶方向。
控制算法这门课最怕眼高手低。看懂公式和能跑通仿真之间差着很多细节,建议每个阶段都留下可复现的代码和实验记录。
8. 下一集可以继续深入的方向
到这里,第一集的内容已经覆盖了四电机绳驱控制算法的完整入门链路:运动学建模、PID 位置控制、张力分配、Python 仿真和 AI 辅助学习的方法。这个项目本身还有很多值得继续展开的切入点。
如果你对当前代码感兴趣,可以尝试修改末端目标位置、PID 参数、锚点坐标,观察轨迹和控制力的变化。也可以把 PID 换成 LQR,对比两种控制器的响应时间和稳定性差异。更进一步的实验,是在仿真中加入绳子弹性模型,模拟真实绳索的松紧过程。
下一集可以考虑做三件事:一是整理真实四电机绳驱平台的硬件选型与电机控制方案;二是把控制算法迁移到 ROS 或 MATLAB Simulink 环境中进行联合仿真;三是用 AI 辅助生成一套更完整的测试用例,把工作空间边界、张力约束和断绳异常场景都覆盖上。