SymPy 力学模块 Actuator 完全指南:力、弹簧、阻尼器与扭矩执行器的符号建模
2026/9/15 0:07:20 网站建设 项目流程

SymPy 力学模块 Actuator 完全指南:力、弹簧、阻尼器与扭矩执行器的符号建模

【免费下载链接】sympyA computer algebra system written in pure Python项目地址: https://gitcode.com/GitHub_Trending/sy/sympy

导读

SymPy 的sympy.physics.mechanics是面向多体动力学的符号计算模块,而Actuator(执行器)是其用于"描述系统外载荷"的核心抽象:它把弹簧、阻尼器、摩擦、电机扭矩等元件统一封装成"产生力或扭矩的组件",并通过to_loads()方法一键生成KanesMethodLagrangesMethod等方程推导方法所需的(Point, Vector)载荷列表。本文以仓库文档 doc/src/modules/physics/mechanics/api/actuator.rst 为主体,结合 actuator.py 源码与 test_actuator.py 测试,系统讲解 Actuator 的设计、6 种内置执行器的使用与背后的符号约定,读完即可在自己的多体动力学建模中直接落地使用。


一、文档与 API 概览:Actuator 在 SymPy 力学体系中的位置

doc/src/modules/physics/mechanics/api/actuator.rst是力学模块 API 参考页之一(见 api/index.rst 中的 "Guide to Mechanics" 章节列表),页面正文只有两行 Sphinx 指令:

.. automodule:: sympy.physics.mechanics.actuator :members:

这意味着文档正文完全由模块源码 docstring 自动生成。因此该文档的"真实内容"即是 actuator.py 中 6 个类及其方法的全部说明与示例,它们是本文展开的核心素材。从源码结构看,Actuator 与同目录下的 pathway.py(路径)是一对孪生概念:Pathway 描述力的作用线(作用在哪些点上、长度与伸长速度如何计算),Actuator 描述力/扭矩的大小(标量表达式),二者结合才构成完整的载荷描述。

模块通过 sympy/physics/mechanics/init.py 将下列类导出到sympy.physics.mechanics命名空间:

继承关系作用
ActuatorBaseABC所有执行器的抽象基类
ForceActuatorActuatorBase通用力执行器(沿路径产生膨胀力)
LinearSpringForceActuator线性弹簧(力与长度成线性)
LinearDamperForceActuator线性阻尼器(力与伸长速度成线性)
DuffingSpringForceActuator非线性弹簧(Duffing 方程)
CoulombKineticFrictionForceActuator库仑动摩擦(含 Stribeck 与黏性效应)
TorqueActuatorActuatorBase扭矩执行器(在一对刚体上施加等大反向扭矩)

测试文件 test_actuator.py 对应为每个类都维护了TestForceActuatorTestLinearSpringTestLinearDamperTestTorqueActuatorTestDuffingSpringTestCoulombKineticFriction等测试类,可当作官方使用样例库。


二、设计基石:ActuatorBaseto_loads()契约

2.1 抽象基类

ActuatorBase继承自abc.ABC,定义了两个核心成员(actuator.py):

  • __init__(self):空初始化,用户不能直接实例化它,但可以通过子类化自定义新的执行器类型;
  • to_loads(self)@abstractmethod抽象方法,是每个执行器必须实现的核心契约;
  • 默认__repr__返回ClassName()形式的字符串。

2.2to_loads():连接"载荷"与"运动方程"的桥梁

to_loads()的语义在多个类的 docstring 中反复强调:KanesMethodkanes_equations方法要求传入一组(Point, Vector)元组列表作为loads参数;to_loads()就是产生这种"正确结构"载荷列表的工具,返回结果既可以与其他载荷直接拼接,也可以同样传递给LagrangesMethod等其它运动方程方法类。

对力执行器而言,ForceActuator.to_loads()的实现非常简洁(actuator.py):

def to_loads(self): return self.pathway.to_loads(self.force)

把"力的大小"交给 pathway 去换算成各个附着点上的力向量。例如线性路径LinearPathwayto_loads(force)会返回作用在两个端点上的等大反向力对。


三、ForceActuator:通用的力执行器与膨胀力符号约定

3.1 概念与符号约定

ForceActuator是"沿其长度产生(膨胀)力的执行器"(actuator.py)。它用一个 pathway 实例决定力的方向与作用点数量。以最简单的LinearPathway为例:路径由两个可相对运动的点构成,产生一对作用在端点上的等大反向力。若定义两点间的时变欧氏距离为正,则"伸长速度"(extension velocity)是这一距离对时间的导数——两点远离时为正、靠近时为负。作用在任一点上的力方向由"从另一点指向该点的单位向量"决定,从而确立符号约定:正力幅值趋向于把两点推开,这就是 "expansile"(膨胀)的含义。文档给出如下示意图:

P Q o<--- F --->o | | |<--l(t)--->|

3.2 构造参数

参数类型说明
forceExpr执行器产生的(膨胀)力标量表达式
pathwayPathwayBase执行器的作用线,必须是PathwayBase具体子类实例(如LinearPathway

3.3 使用示例

构造一个随坐标q沿N.x方向移动的执行器(完整示例见 actuator.py):

>>> from sympy import symbols >>> from sympy.physics.mechanics import (ForceActuator, LinearPathway, ... Point, ReferenceFrame) >>> from sympy.physics.vector import dynamicsymbols >>> N = ReferenceFrame('N') >>> q = dynamicsymbols('q') >>> force = symbols('F') >>> pA, pB = Point('pA'), Point('pB') >>> pA.set_vel(N, 0) >>> pB.set_pos(pA, q*N.x) >>> pB.pos_from(pA) q(t)*N.x >>> linear_pathway = LinearPathway(pA, pB) >>> actuator = ForceActuator(force, linear_pathway) >>> actuator ForceActuator(F, LinearPathway(pA, pB))

3.4 属性不可变与类型校验(源码细节)

forcepathway均为只读属性:setter 中一旦检测到_force/_pathway已存在即抛出AttributeError("immutable");forcesympify(force, strict=True)严格符号化,无法 sympify 的值(如None、字符串)会触发SympifyErrorpathway若不是PathwayBase实例则抛TypeError。这些行为在 test_actuator.py 中有完整断言覆盖(test_properties_are_immutabletest_invalid_constructor_force_not_sympifyable等)。

3.5 用ForceActuator手搓弹簧与阻尼器

docstring 示范了如何用ForceActuator模拟线性弹簧(注意执行器采用"膨胀力为正"的约定,弹簧产生的是收缩力,因此取负号):

>>> stiffness = symbols('k') >>> spring_force = -stiffness*pathway.length >>> spring = ForceActuator(spring_force, pathway) >>> spring.to_loads() [(pA, k*q(t)*N.x), (pB, - k*q(t)*N.x)]

线性阻尼器同理,阻尼力与伸长速度成正比且方向相反:

>>> damping_coefficient = symbols('c') >>> damping_force = -damping_coefficient*pathway.extension_velocity >>> damper = ForceActuator(damping_force, pathway) >>> damper.to_loads() [(pA, c*Derivative(q(t), t)*N.x), (pB, - c*Derivative(q(t), t)*N.x)]

可以看到to_loads()直接产出了KanesMethod所需的(Point, Vector)元组列表,可以直接拼入loads参数。


四、LinearSpring:线性弹簧

4.1 "线性"的含义

LinearSpring名字中的 "linear" 指的是弹力是弹簧长度的线性函数(actuator.py):对刚度k、两端距离x、平衡长度 0 的弹簧,弹力为-k*x。若想让弹簧沿直线路径布置,需传入LinearPathway实例。

4.2 构造参数

参数类型默认值说明
stiffnessExpr弹簧常数(刚度)
pathwayPathwayBase弹簧的作用线(具体子类,如LinearPathway
equilibrium_lengthExpr0弹簧的平衡长度,即不产生力时的长度;默认 0 表示弹力是路径长度的线性函数、无常数偏移

4.3 构造示例与力的计算

>>> from sympy import symbols >>> from sympy.physics.mechanics import (LinearPathway, LinearSpring, ... Point, ReferenceFrame) >>> from sympy.physics.vector import dynamicsymbols >>> N = ReferenceFrame('N') >>> q = dynamicsymbols('q') >>> stiffness = symbols('k') >>> pA, pB = Point('pA'), Point('pB') >>> pA.set_vel(N, 0) >>> pB.set_pos(pA, q*N.x) >>> linear_pathway = LinearPathway(pA, pB) >>> spring = LinearSpring(stiffness, linear_pathway) >>> spring LinearSpring(k, LinearPathway(pA, pB)) >>> spring.force -k*sqrt(q(t)**2)

力为负——因为 SymPy 的执行器符号约定是负力为收缩(contractile),而弹簧被拉伸时正是要收缩回平衡长度。

4.4 非零平衡长度

传入equilibrium_length后,q(t)等于l时弹力为零;当q(t) < l时力变为正(膨胀),符合物理直觉:

>>> l = symbols('l') >>> spring = LinearSpring(stiffness, linear_pathway, equilibrium_length=l) >>> spring LinearSpring(k, LinearPathway(pA, pB), equilibrium_length=l) >>> spring.force -k*(-l + sqrt(q(t)**2))

4.5 源码实现要点

force是一个计算属性-self.stiffness*(self.pathway.length - self.equilibrium_length)(actuator.py),其 setter 直接抛AttributeError("Can't set computed attribute 'force'.")stiffnessequilibrium_length同样不可变且经sympify(strict=True)__repr__在平衡长度为 0 时会省略该参数。这些均在TestLinearSpring(test_actuator.py)中验证,例如test_valid_constructor参数化断言了不同刚度/平衡长度组合下的force表达式。


五、LinearDamper:线性阻尼器

5.1 概念

LinearDamper的阻尼力是伸长速度的线性函数(actuator.py):对阻尼系数c、伸长速度v,阻尼力为-c*v。它同样是ForceActuator子类,遵循相同的长度、伸长速度与力的方向符号约定。

阻尼器的力学语义:阻尼力总是抵抗长度的变化方向。当伸长速度为正(伸长)时,端点上的力标量为负以翻转方向;当伸长速度为负(缩短)时,符号相消后端点力仍与路径端点力的正约定同向(即趋向推开端点)。因此阻尼力始终与运动方向相反。

5.2 构造参数与示例

参数类型说明
dampingExpr阻尼常数
pathwayPathwayBase阻尼器的作用线(具体子类,如LinearPathway
>>> from sympy import symbols >>> from sympy.physics.mechanics import (LinearDamper, LinearPathway, ... Point, ReferenceFrame) >>> from sympy.physics.vector import dynamicsymbols >>> N = ReferenceFrame('N') >>> q = dynamicsymbols('q') >>> damping = symbols('c') >>> pA, pB = Point('pA'), Point('pB') >>> pA.set_vel(N, 0) >>> pB.set_pos(pA, q*N.x) >>> pB.vel(N) Derivative(q(t), t)*N.x >>> linear_pathway = LinearPathway(pA, pB) >>> damper = LinearDamper(damping, linear_pathway) >>> damper LinearDamper(c, LinearPathway(pA, pB)) >>> damper.force -c*sqrt(q(t)**2)*Derivative(q(t), t)/q(t)

force计算属性实现为-self.damping*self.pathway.extension_velocity(actuator.py),setter 同样禁止赋值。TestLinearDamper(test_actuator.py)覆盖了构造、不可变性与to_loads输出。


六、TorqueActuator:扭矩执行器

6.1 概念

TorqueActuator直接继承ActuatorBase,在一对刚体上产生等大反向的扭矩对(actuator.py)。注意:扭矩实际作用在**参考系(frame)**上,因此当传入RigidBody时,TorqueActuator会自动提取其关联的ReferenceFrame

6.2 构造参数

参数类型默认值说明
torqueExpr执行器产生的扭矩标量表达式
axisVector扭矩作用的轴(必须是Vector
target_frameReferenceFrame \| RigidBody扭矩施加的主参考系
reaction_frameReferenceFrame \| RigidBody \| NoneNone承受等大反向反作用扭矩的副参考系;传None则只产生单一扭矩

6.3 基本用法

>>> from sympy import symbols >>> from sympy.physics.mechanics import (ReferenceFrame, RigidBody, ... TorqueActuator) >>> N = ReferenceFrame('N') >>> A = ReferenceFrame('A') >>> torque = symbols('T') >>> axis = N.z >>> parent = RigidBody('parent', frame=N) >>> child = RigidBody('child', frame=A) >>> bodies = (child, parent) >>> actuator = TorqueActuator(torque, axis, *bodies) >>> actuator TorqueActuator(T, axis=N.z, target_frame=A, reaction_frame=N)

6.4 类方法at_pin_joint:从铰链一键构造

TorqueActuator.at_pin_joint(torque, pin_joint)备用构造函数(classmethod):从PinJoint实例提取joint_axis作为扭矩轴,并分别以child_interframeparent_interframe作为目标/反应参考系。正扭矩引起关节坐标的正位移,即扭矩施加在 child 体上、反作用扭矩施加在 parent 体上。传入非PinJoint对象会抛TypeError

>>> from sympy.physics.mechanics import (PinJoint, ReferenceFrame, ... RigidBody, TorqueActuator) >>> N = ReferenceFrame('N') >>> A = ReferenceFrame('A') >>> parent = RigidBody('parent', frame=N) >>> child = RigidBody('child', frame=A) >>> pin_joint = PinJoint( ... 'pin', ... parent, ... child, ... joint_axis=N.z, ... ) >>> from sympy import symbols >>> torque = symbols('T') >>> actuator = TorqueActuator.at_pin_joint(torque, pin_joint) >>> actuator TorqueActuator(T, axis=N.z, target_frame=A, reaction_frame=N)

6.5to_loads()输出

to_loads()内部构造Torque载荷(见 actuator.py):目标参考系上施加torque*axis,若存在反应参考系则再施加-torque*axis

>>> actuator.to_loads() [(A, T*N.z), (N, - T*N.z)] >>> actuator = TorqueActuator(torque, N.z, N) # 无 reaction_frame >>> actuator.to_loads() [(N, T*N.z)]

TestTorqueActuator(test_actuator.py)还验证了RigidBody自动解包为 frame、非法axis/frame 类型抛TypeError等行为。


七、DuffingSpring:基于 Duffing 方程的非线性弹簧

7.1 力-位移关系

DuffingSpring表示基于 Duffing 方程的非线性弹簧力(actuator.py):

$$ F = -\beta,x - \alpha,x^{3} $$

其中x为相对平衡位置的位移,beta为线性弹簧常数,alpha为非线性三次项系数。

7.2 构造参数

参数类型默认值说明
linear_stiffnessExpr线性刚度系数(对应 β)
nonlinear_stiffnessExpr非线性刚度系数(对应 α)
pathwayPathwayBase执行器的作用线(必须为PathwayBase实例,否则抛TypeError
equilibrium_lengthExpr0弹簧处于平衡(不产生力)时的长度

force计算属性实现(actuator.py):

displacement = self.pathway.length - self.equilibrium_length return -self.linear_stiffness * displacement - self.nonlinear_stiffness * displacement**3

TestDuffingSpring(test_actuator.py)对其构造与力表达式做了断言。此外,仓库还提供了 duffing-example.rst 教程,演示 Duffing 弹簧在多体系统中的完整建模范例,可作为进阶阅读。


八、CoulombKineticFriction:带 Stribeck 与黏性效应的库仑动摩擦

8.1 数学模型

CoulombKineticFriction实现了包含 Stribeck(斯特里贝克)与黏性效应的库仑动摩擦模型(actuator.py),摩擦力的完整表达式为:

$$ F = \left(\mu_k f_n + (\mu_s - \mu_k) f_n e^{-(v/v_s)^2}\right)\operatorname{sign}(v) + \sigma v $$

其中:

  • $\mu_k$:动摩擦系数(kinetic)
  • $\mu_s$:静摩擦系数(static)
  • $f_n$:法向力(必须为非负标量)
  • $v$:相对速度
  • $v_s$:Stribeck 摩擦系数(特征速度)
  • $\sigma$:黏性摩擦常数

默认摩擦为 $F = \mu_k f_n \operatorname{sign}(v)$;Stribeck 项 $(\mu_s-\mu_k) f_n e^{-(v/v_s)^2}$ 与黏性项 $\sigma v$ 均为可选增强。

8.2 模型假设

  • 假设相对运动非零(不处理粘滞静止状态);
  • 法向力为非负标量;
  • 合成摩擦力方向与速度方向相反(三项均与 $v$ 反向);
  • 路径两端点分别固定在被此相对滑动的不同物体上;
  • 该执行器已在简单平动场景(如块体在平面上滑动)中测试验证。

8.3 构造参数

参数类型默认值说明
mu_ksympifiable动摩擦系数
f_nsympifiable表面间法向力(应恒为非负标量)
pathwayPathwayBase执行器作用线
v_ssympifiable0.01Stribeck 摩擦系数
sigmasympifiable0黏性摩擦系数
mu_ssympifiable等于mu_k静摩擦系数;默认取mu_k使 Stribeck 项默认为 0

注意__init__v_s=None时会被赋为0.01sigma=None时为0mu_s=None时取mu_k(actuator.py)。

8.4 完整示例:带摩擦的质量-弹簧系统

docstring 给出了结合KanesMethod的端到端示例——这是把执行器接入运动方程推导的教科书案例:

>>> import sympy as sm >>> from sympy.physics.mechanics import (dynamicsymbols, ReferenceFrame, Point, ... LinearPathway, CoulombKineticFriction, LinearSpring, KanesMethod, Particle) >>> x, v = dynamicsymbols('x, v', real=True) >>> m, g, k, mu_k, mu_s, v_s, sigma = sm.symbols('m, g, k, mu_k, mu_s, v_s, sigma') >>> N = ReferenceFrame('N') >>> O, P = Point('O'), Point('P') >>> O.set_vel(N, 0) >>> P.set_pos(O, x*N.x) >>> pathway = LinearPathway(O, P) >>> friction = CoulombKineticFriction(mu_k, m*g, pathway, v_s=v_s, sigma=sigma, mu_s=mu_k) >>> spring = LinearSpring(k, pathway) >>> block = Particle('block', point=P, mass=m) >>> kane = KanesMethod(N, (x,), (v,), kd_eqs=(x.diff() - v,)) >>> friction.to_loads() [(O, (g*m*mu_k*sign(sign(x(t))*Derivative(x(t), t)) + sigma*sign(x(t))*Derivative(x(t), t))*x(t)/Abs(x(t))*N.x), (P, ...)] >>> loads = friction.to_loads() + spring.to_loads() >>> fr, frstar = kane.kanes_equations([block], loads) >>> eom = fr + frstar >>> eom Matrix([[-k*x(t) - m*Derivative(v(t), t) + ...]])

可以看到摩擦执行器的to_loads()返回的载荷与弹簧的载荷直接相加拼接,随后整体传给KanesMethod.kanes_equations即得到运动方程,工作流十分干净。


九、通往实操的路径:Pathway 与整体工作流

9.1 Pathway 是执行器的"作用线"

所有力执行器都依赖 pathway 提供lengthextension_velocityto_loads(force)三个能力(pathway.py)。模块导出了三种内置路径(__all__见 pathway.py):

  • LinearPathway:两点间最短直线段,不与其他物体交互,是最简单的路径。length返回sqrt(q(t)**2)这种"刻意复杂"的表达式,以保证长度恒为正(pathway.py);
  • ObstacleSetPathway:绕障碍点集折线布置;
  • WrappingPathway:绕圆柱、球等WrappingGeometryBase几何体缠绕布置(可用于绳索-滑轮系统建模)。

理解这层关系后,你会发现执行器与路径是正交的两个维度:执行器决定"力多大"(标量),路径决定"力怎么作用"(方向与端点)。API 文档页 pathway.rst 与之配套阅读效果最佳。

9.2 典型建模工作流

综合全部分类,在 SymPy 中使用执行器建模的标准流程可归纳为四步:

  1. 建参考系与质点/刚体:创建ReferenceFramePoint(或用RigidBody/Particle),设置位置与速度;
  2. 构造 pathway:用一对Point构造LinearPathway(或障碍/缠绕路径);
  3. 构造 actuator:选择LinearSpringLinearDamperTorqueActuatorDuffingSpringCoulombKineticFriction,传入标量表达式与 pathway;
  4. 组装载荷并推导方程:把各执行器的to_loads()结果拼接,传给KanesMethod.kanes_equationsLagrangesMethod得到运动方程,再交给数值积分器求解轨迹。

9.3 相关参考资源

  • API 参考总览:doc/src/modules/physics/mechanics/api/index.rst
  • Pathway API:doc/src/modules/physics/mechanics/api/pathway.rst
  • 铰链/关节 API:doc/src/modules/physics/mechanics/api/joint.rst
  • Duffing 弹簧教程:duffing-example.rst
  • 执行器实现源码:sympy/physics/mechanics/actuator.py
  • 执行器测试用例(官方用法样例):sympy/physics/mechanics/tests/test_actuator.py
  • 路径实现源码:sympy/physics/mechanics/pathway.py

结语

SymPy 的 Actuator 体系把"弹簧-阻尼器-摩擦-电机扭矩"等工程中常见的载荷元件抽象为一组高度一致、可组合的类:统一的to_loads()契约让载荷可以无缝接入 Kane 与 Lagrange 两大运动方程方法;统一的"膨胀力为正"符号约定消除了建模中反复出现的方向错误;不可变属性与严格类型/符号化校验则从源头拦截了大量低级错误。无论你是在做机器人关节驱动、车辆悬挂系统还是刚体碰撞摩擦分析,这套 API 都能显著缩短从物理模型到符号运动方程的路径。

【免费下载链接】sympyA computer algebra system written in pure Python项目地址: https://gitcode.com/GitHub_Trending/sy/sympy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询