SymPy 力学模块实战:用 Kane 方法求解滚动圆盘的非贡献约束力(rollingdisc_example_kane_constraints)
【免费下载链接】sympyA computer algebra system written in pure Python项目地址: https://gitcode.com/GitHub_Trending/sy/sympy
导读
本篇技术指南围绕 SymPy 力学模块(sympy.physics.mechanics)中的经典教程 rollingdisc_example_kane_constraints.rst 展开,完整演示如何基于 Kane 方法建立"无限薄圆盘在地面无滑动纯滚动"的多体系统模型,并通过引入**辅助广义速度(auxiliary generalized speeds)把通常被剔除的非贡献力(non-contributing / constraint forces)**显式求解出来。读完本文,你将掌握KanesMethod中u_auxiliary参数的完整用法、auxiliary_eqs属性的含义,以及如何从质量矩阵与 forcing 向量出发得到系统的显式运动方程,并最终写出约束力关于广义坐标与广义速度的闭式表达式。
一、背景:为什么要"把约束力带入证据"
在 rollingdisc_example_kane.rst 中,圆盘模型直接从接触点向上定义运动学,滚动无滑动条件被"构造性地"满足,因此不需要引入广义速度,最终只需求解 3 个广义坐标q1, q2, q3与 3 个广义速度u1, u2, u3下的运动方程。
但很多工程场景中,我们不仅关心运动,还关心约束力本身(例如地面法向反力、摩擦力),用于强度校核或控制系统设计。Kane 方法的核心思想是:广义主动力与广义惯性力在广义速度方向上的投影之和为零,而非贡献力(约束力)在允许运动方向上不做功,因此它们不会出现在常规的 Kane 方程中。
本教程的做法(原理详见 [Kane1985])是:在接触点人为引入三个辅助广义速度u4, u5, u6,它们在纯滚动条件下恒等于零;同时引入与它们同方向的三个约束力分量f1, f2, f3。这样一来,约束力从"隐藏项"变成"显式未知量",可以在 Kane 方程之外得到一组额外的**辅助方程(auxiliary equations)**用于反解它们。
在 SymPy 源码中,这一机制由 kane.py 中的KanesMethod实现:构造器接受u_auxiliary参数(见 kane.py 第 71-72 行),并提供auxiliary_eqs属性(见 kane.py 第 110-113 行)。下面按教程的完整流程逐步实现。
二、环境准备:符号与动力学符号声明
教程首先开启力学模块打印并导入需要的符号。注意这一行:
>>> from sympy import symbols, sin, cos, tan >>> from sympy.physics.mechanics import * >>> mechanics_printing(pretty_print=False)mechanics_printing(pretty_print=False)用于开启向量运算时的自动简化。文档明确提示:它会让小型问题的输出更美观,但较大的向量运算可能因此挂起(It makes the outputs nicer for small problems, but can cause larger vector operations to hang)。因此这是本教程特意开启的开关,在大规模建模时建议谨慎使用。
接着声明广义坐标、广义速度及其一阶导数,以及圆盘半径、质量和重力加速度:
>>> q1, q2, q3, u1, u2, u3 = dynamicsymbols('q1 q2 q3 u1 u2 u3') >>> q1d, q2d, q3d, u1d, u2d, u3d = dynamicsymbols('q1 q2 q3 u1 u2 u3', 1) >>> r, m, g = symbols('r m g')这里dynamicsymbols(..., 1)生成对应符号关于时间的一阶导数,供后续运动学微分方程(kd)使用。
三、核心新增:辅助广义速度与约束力符号
与无约束版本相比,本教程唯一新增的两行声明是:
>>> u4, u5, u6, f1, f2, f3 = dynamicsymbols('u4 u5 u6 f1 f2 f3')u4, u5, u6:接触点处的三个辅助广义速度,分别沿L.x方向(圆盘侧向)、cross(Y.z, L.x)方向(沿滚动路径)、Y.z方向(垂直地面)。由于纯滚动约束,它们按定义恒为零;f1, f2, f3:与上述三个速度方向一一对应的约束力幅值。
从源码结构看,KanesMethod会把u_auxiliary列表中的速度从主广义速度中分离出来单独处理(见 kane.py 第 842-861 行),这正是后续能得到auxiliary_eqs的关键。
四、参考系与角速度运动学
圆盘姿态采用 3-1-2(Z、X、Y)系列的**简单旋转(simple rotation)**逐级建立中间参考系:
>>> N = ReferenceFrame('N') >>> Y = N.orientnew('Y', 'Axis', [q1, N.z]) >>> L = Y.orientnew('L', 'Axis', [q2, Y.x]) >>> R = L.orientnew('R', 'Axis', [q3, L.y]) >>> w_R_N_qd = R.ang_vel_in(N) >>> R.set_ang_vel(N, u1 * L.x + u2 * L.y + u3 * L.z)N:惯性参考系(地面);Y:绕N.z旋转q1(方位角),即圆盘"前进方向"所在的竖直平面;L:绕Y.x旋转q2(侧倾/lean 角),称侧倾参考系,惯性主轴在此系中不变;R:绕L.y旋转q3(自转角),附着于圆盘本体。
w_R_N_qd是从旋转序列解析得到的角速度表达式;随后用广义速度u1, u2, u3在L系基向量上的组合显式指定角速度。之后kd方程正是用来建立这两者之间的联系。
五、平动运动学:接触点速度与质心速度
无滑滚动要求接触点速度为零。但为了把约束力带入证据,这里反其道而行:给接触点显式赋予一个由辅助速度合成的"名义速度",它在约束成立时归零:
>>> C = Point('C') >>> C.set_vel(N, u4 * L.x + u5 * cross(Y.z, L.x) + u6 * Y.z) >>> Dmc = C.locatenew('Dmc', r * L.z) >>> vel = Dmc.v2pt_theory(C, N, R)C:接触点,其速度的三个分量分别沿圆盘侧向、滚动路径方向、竖直方向;Dmc:圆盘质心,位于接触点正上方r * L.z处;v2pt_theory(C, N, R):基于两点速度关系(两点位于同一刚体R上),由C的速度与R的角速度自动推出质心速度。
六、惯量张量
圆盘关于质心的惯量张量在L系中写出(圆盘绕自身对称轴L.y的转动惯量为m*r**2/2,两个直径方向各为m*r**2/4):
>>> I = inertia(L, m / 4 * r**2, m / 2 * r**2, m / 4 * r**2)由于圆盘在L系中滚动时惯量不变化(圆盘轴对称),在L系表达惯量可显著简化最终方程——这一建模技巧在 rollingdisc_example_kane.rst 中有同样说明。
七、运动学微分方程(kd)
kd 方程把广义坐标导数与广义速度联系起来,通过对R的角速度在L系三个基向量上取点积得到:
>>> kd = [dot(R.ang_vel_in(N) - w_R_N_qd, uv) for uv in L]即dot(角速度表达式之差, L.x)、dot(..., L.y)、dot(..., L.z)三个标量方程,形式为q1d、q2d、q3d与u1、u2、u3之间的线性关系。
八、力清单与刚体定义
力清单同时包含主动力(重力)与约束力(三个未知分量):
>>> ForceList = [(Dmc, - m * g * Y.z), (C, f1 * L.x + f2 * cross(Y.z, L.x) + f3 * Y.z)] >>> BodyD = RigidBody('BodyD', Dmc, R, m, (I, Dmc)) >>> BodyList = [BodyD](Dmc, -m*g*Y.z):作用于质心的重力;(C, f1*L.x + f2*cross(Y.z, L.x) + f3*Y.z):作用于接触点的约束力,三个分量方向与辅助速度u4, u5, u6一一对应(力与速度同方向投影,正是"广义力=约束力在虚位移上的虚功"的离散化形式)。
RigidBody('BodyD', Dmc, R, m, (I, Dmc))指定:质心点为Dmc、附着参考系为R、质量为m、惯量张量I相对Dmc给出。
九、构建 KanesMethod 并求解运动方程
构造器传入u_auxiliary=[u4, u5, u6],这是与普通 Kane 建模唯一的结构性差异:
>>> KM = KanesMethod(N, q_ind=[q1, q2, q3], u_ind=[u1, u2, u3], kd_eqs=kd, ... u_auxiliary=[u4, u5, u6]) >>> (fr, frstar) = KM.kanes_equations(BodyList, ForceList) >>> MM = KM.mass_matrix >>> forcing = KM.forcing >>> rhs = MM.inv() * forcing >>> kdd = KM.kindiffdict() >>> rhs = rhs.subs(kdd) >>> rhs.simplify() >>> mprint(rhs) Matrix([ [(4*g*sin(q2) + 6*r*u2*u3 - r*u3**2*tan(q2))/(5*r)], [ -2*u1*u3/3], [ (-2*u2 + u3*tan(q2))*u1]])推导过程是标准流程:kanes_equations返回广义主动力fr与广义惯性力frstar;由mass_matrix与forcing组装M·u̇ = forcing;rhs = MM.inv() * forcing得到u̇的表达式;再用kindiffdict()把q̇替换为广义速度,最终显式给出:
u̇1 = (4·g·sin(q2) + 6·r·u2·u3 − r·u3²·tan(q2)) / (5·r)u̇2 = −2·u1·u3 / 3u̇3 = (−2·u2 + u3·tan(q2)) · u1
注意这个结果与不引入约束力的 Kane 版本(见 rollingdisc_example_kane.rst 输出)完全一致——这印证了辅助速度的引入不改变真实运动方程,符合"非贡献力不影响运动"的理论预期,同时验证了建模的正确性。
十、求解约束力:auxiliary_eqs 与化简技巧
运动方程求出后,约束力藏在KM.auxiliary_eqs中。直接输出的表达式可能比较冗长,教程给出了一个专门的化简管线:
>>> from sympy import trigsimp, signsimp, collect, factor_terms >>> def simplify_auxiliary_eqs(w): ... return signsimp(trigsimp(collect(collect(factor_terms(w), f2), m*r))) >>> mprint(KM.auxiliary_eqs.applyfunc(simplify_auxiliary_eqs)) Matrix([ [ -m*r*(u1*u3 + u2') + f1], [-m*r*u1**2*sin(q2) - m*r*u2*u3/cos(q2) + m*r*cos(q2)*u1' + f2], [ -g*m + m*r*(u1**2*cos(q2) + sin(q2)*u1') + f3]])化简策略说明:
factor_terms(w):提出公共因子;collect(..., f2)/collect(..., m*r):按约束力符号和质量×半径聚集同类项;trigsimp:利用三角恒等式化简sin/cos项;signsimp:规范化符号形式。
最终辅助方程为约束力 = 惯性项,解出三个分量:
- 侧向约束力
f1 = m·r·(u1·u3 + u̇2) - 滚动路径方向约束力
f2 = m·r·u1²·sin(q2) + m·r·u2·u3/cos(q2) − m·r·cos(q2)·u̇1 - 法向约束力
f3 = g·m − m·r·(u1²·cos(q2) + sin(q2)·u̇1)
例如f3表达式中的−g·m + m·r·(u1²·cos(q2) + sin(q2)·u̇1)即为圆盘法向反力:当圆盘侧倾角q2与侧倾速率u1耦合时,法向反力会偏离重力m·g,这正是侧倾-自转耦合(类似陀螺效应)的体现。将上一节的u̇1代入即可得到纯关于(q2, u1, u2, u3)的显式约束力公式。
十一、源码级原理:辅助方程是如何生成的
从 kane.py 的实现可以看到完整的机制:
KanesMethod.__init__接受u_auxiliary参数并存入self._uaux;- 在
kanes_equations中,若存在辅助速度(if self._uaux:),会构造一个以辅助速度为主广义速度的临时KanesMethod(km = KanesMethod(self._inertial, self.q, self._uaux, u_auxiliary=self._uaux, ...),见 kane.py 第 842-854 行),并复用同样的力清单与刚体清单:fraux = km._form_fr(loads) frstaraux = km._form_frstar(bodies) self._aux_eq = fraux + frstaraux self._fr = fr.col_join(fraux) self._frstar = frstar.col_join(frstaraux)即:
auxiliary_eqs = fraux + frstaraux(见 kane.py 第 855-861 行)。辅助广义速度在约束下恒为零,但其"名义速度"使得约束力在该方向上的广义力分量进入方程,从而可以被反解。 auxiliary_eqs作为只读属性返回self._aux_eq(见 kane.py 第 909-915 行)。
同时kanes_equations的 docstring(见 kane.py 第 806-817 行)说明:设有s个辅助速度、o个广义速度、m个运动约束,返回向量的长度为o − m + s,前o − m个是约束后的 Kane 方程,后s个即辅助 Kane 方程。
十二、测试与验证:仓库中的对应用例
仓库测试目录对该功能有专门覆盖,可作为实现正确性的证据:
- test_kane.py 第 220-259 行:在同一滚动圆盘系统上对比"手动引入 2 个辅助速度"与"使用内置
u_auxiliary=[u4, u5]"两种方式,验证二者等价; - test_kane2.py:覆盖了同时含辅助速度、配置约束与非完整约束的复杂用例(第 47-49 行注释明确标注
ua[0]/ua[1]/ua[2]为接触点三个方向的辅助广义速度),并断言auxiliary_eqs与手动推导一致; - test_lagrange.py、test_linearize.py 中的
rollingdisc用例则验证了不同方法(Lagrange、线性化)对同一系统的结果一致性。
这些测试从侧面印证:本教程给出的建模流程(接触点引入辅助速度→同名约束力→u_auxiliary传入)是官方推荐且经过验证的标准做法。
十三、与其他建模方式的对比
同一物理系统在教程目录doc/src/tutorials/physics/mechanics/下有三种建模视角,入口见 rollingdisc_example.rst:
| 教程文件 | 方法 | 特点 |
|---|---|---|
| rollingdisc_example_kane.rst | Kane(无约束力) | 3 坐标 + 3 速度,最简洁,只给运动方程 |
| 本文(kane_constraints) | Kane + 辅助速度 | 额外引入 3 个零速辅助速度与 3 个约束力,得到运动方程 + 约束力闭式解 |
| rollingdisc_example_lagrange.rst | Lagrange | 从能量角度建模,可对照验证 |
三种方式对同一系统的运动学结果应当一致,互为交叉验证。
十四、使用注意事项小结
- 自动简化开关:
mechanics_printing(pretty_print=False)仅适合小规模问题,大系统建议关闭以避免向量运算挂起; - 辅助速度方向选择:约束力分量必须与辅助速度方向一一对应(本教程为
L.x、cross(Y.z, L.x)、Y.z),否则广义力投影会丢失对应分量; - 辅助速度恒为零:它们只是"名义自由度",不出现在最终运动方程中,
rhs结果与无约束版本一致可作为正确性检验; - 约束力求解:
KM.auxiliary_eqs是求解约束力的唯一入口,通常需要配合factor_terms、collect、trigsimp等化简手段(如教程中的simplify_auxiliary_eqs)才能得到可读的闭式表达式; - 扩展阅读:完整可运行脚本位于教程目录 doc/src/tutorials/physics/mechanics/,
KanesMethod的全部参数(含configuration_constraints、velocity_constraints、u_dependent等)见 kane.py 的类 docstring(kane.py 第 60-99 行)。
结语
本教程完整展示了 SymPy 力学模块中"在 Kane 方法框架下显式求解非贡献约束力"的标准工程流程:通过u_auxiliary引入辅助广义速度、构造同名约束力分量、经kanes_equations获得常规运动方程与auxiliary_eqs辅助方程,再借助符号化简管线得到三个约束力的闭式表达式。该能力对需要同时进行动力学仿真与力/力矩分析的场景(如机器人足端力、车辆轮胎力等)具有直接实用价值。
【免费下载链接】sympyA computer algebra system written in pure Python项目地址: https://gitcode.com/GitHub_Trending/sy/sympy
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考