四杆连杆机构¶
四杆连杆是力学中常用的例子,可以用两个完整约束来描述。这个例子将使用 sympy.physics.mechanics
提供的关节功能。简而言之,我们将使用刚体和关节来定义开环系统。接下来,我们定义配置约束以闭合环路。System
将用于对整个系统进行“记录”,而 KanesMethod
作为后端。
首先,我们需要创建描述系统所需的 dynamicsymbols()
,如上图所示。在这种情况下,广义坐标 \(q_1\)、\(q_2\) 和 \(q_3\) 表示连杆之间的角度。同样,广义速度 \(u_1\)、\(u_2\) 和 \(u_3\) 表示连杆之间的角速度。我们还创建了一些 symbols()
来表示连杆的长度和密度。
>>> from sympy import Matrix, linear_eq_to_matrix, pi, simplify, symbols
>>> from sympy.physics.mechanics import *
>>> mechanics_printing(pretty_print=False)
>>> q1, q2, q3, u1, u2, u3 = dynamicsymbols('q1:4, u1:4')
>>> l1, l2, l3, l4, rho = symbols('l1:5, rho')
定义了所有符号后,我们现在可以定义主体并初始化我们的 System
实例。:
>>> N = ReferenceFrame('N')
>>> mass_centers = [Point(f'mc{i}') for i in range(1, 5)]
>>> inertias = [Inertia.from_inertia_scalars(P, N, 0, 0, rho*l**3/12)
... for P, l in zip(mass_centers, (l1, l2, l3, l4))]
>>> link1 = RigidBody('Link1', frame=N, mass=rho*l1,
... masscenter=mass_centers[0], inertia=inertias[0])
>>> link2 = RigidBody('Link2', mass=rho*l2, masscenter=mass_centers[1],
... inertia=inertias[1])
>>> link3 = RigidBody('Link3', mass=rho*l3, masscenter=mass_centers[2],
... inertia=inertias[2])
>>> link4 = RigidBody('Link4', mass=rho*l4, masscenter=mass_centers[3],
... inertia=inertias[3])
>>> system = System.from_newtonian(link1)
接下来,我们还定义了前三个关节,它们构成了开环摆,并将它们添加到系统中。:
>>> joint1 = PinJoint('J1', link1, link2, coordinates=q1, speeds=u1,
... parent_point=l1/2*link1.x,
... child_point=-l2/2*link2.x, joint_axis=link1.z)
>>> joint2 = PinJoint('J2', link2, link3, coordinates=q2, speeds=u2,
... parent_point=l2/2*link2.x,
... child_point=-l3/2*link3.x, joint_axis=link2.z)
>>> joint3 = PinJoint('J3', link3, link4, coordinates=q3, speeds=u3,
... parent_point=l3/2*link3.x,
... child_point=-l4/2*link4.x, joint_axis=link3.z)
>>> system.add_joints(joint1, joint2, joint3)
现在我们可以制定一个将闭合运动学环的完整约束。:
>>> start_point = link1.masscenter.locatenew('start_point', -l1/2*link1.x)
>>> end_point = link4.masscenter.locatenew('end_point', l4/2*link4.x)
>>> loop = end_point.pos_from(start_point)
>>> system.add_holonomic_constraints(loop.dot(link1.x), loop.dot(link1.y))
在生成运动方程之前,我们需要指定哪些广义坐标和速度是独立的,哪些是依赖的。之后,我们可以运行 validate_system()
来进行一些基本的连贯性检查。:
>>> system.q_ind = [q1]
>>> system.u_ind = [u1]
>>> system.q_dep = [q2, q3]
>>> system.u_dep = [u2, u3]
>>> system.validate_system()
由于我们已经准备好了整个系统,我们现在可以使用 KanesMethod
作为后端来形成运动方程。:
>>> simplify(system.form_eoms())
Matrix([[l2*rho*(-2*l2**2*sin(q3)*u1' + 3*l2*l3*u1**2*sin(q2 + q3)*sin(q2) + 3*l2*l3*sin(q2)*cos(q2 + q3)*u1' - 3*l2*l3*sin(q3)*u1' + 3*l2*l4*u1**2*sin(q2 + q3)*sin(q2) + 3*l2*l4*sin(q2)*cos(q2 + q3)*u1' + 3*l3**2*u1**2*sin(q2)*sin(q3) + 6*l3**2*u1*u2*sin(q2)*sin(q3) + 3*l3**2*u2**2*sin(q2)*sin(q3) + 2*l3**2*sin(q2)*cos(q3)*u1' + 2*l3**2*sin(q2)*cos(q3)*u2' - l3**2*sin(q3)*cos(q2)*u1' - l3**2*sin(q3)*cos(q2)*u2' + 3*l3*l4*u1**2*sin(q2)*sin(q3) + 6*l3*l4*u1*u2*sin(q2)*sin(q3) + 3*l3*l4*u2**2*sin(q2)*sin(q3) + 3*l3*l4*sin(q2)*cos(q3)*u1' + 3*l3*l4*sin(q2)*cos(q3)*u2' + l4**2*sin(q2)*u1' + l4**2*sin(q2)*u2' + l4**2*sin(q2)*u3')/(6*sin(q3))]])
揭示非贡献力¶
为了揭示闭合关节处的非贡献力,我们必须在端点处引入x和y方向的辅助速度。
>>> uaux1, uaux2 = dynamicsymbols('uaux1:3')
>>> end_point_aux = end_point.locatenew('end_point_aux', 0)
>>> end_point_aux.set_vel(N, end_point.vel(N) + uaux1*N.x + uaux2*N.y)
为了确保速度包含在速度约束中,我们必须手动覆盖速度约束,因为这些约束默认指定为完整约束的时间导数。
>>> system.velocity_constraints = [
... end_point_aux.vel(N).dot(N.x), end_point_aux.vel(N).dot(N.y)]
在添加非贡献力时,我们需要它们仅依赖于辅助速度,而不是由约束消除的速度。这可以通过对非辅助端点施加相等且相反的力来实现。
>>> faux1, faux2 = dynamicsymbols('faux1:3')
>>> noncontributing_forces = [
... Force(end_point_aux, faux1*N.x + faux2*N.y),
... Force(end_point, -(faux1*N.x + faux2*N.y)),
... ]
或者,我们可以指定一个新的点,该点已经减去了由约束消除的速度。
>>> end_point_forces = end_point.locatenew('end_point_forces', 0)
>>> end_point_forces.set_vel(N, uaux1*N.x + uaux2*N.y)
>>> noncontributing_forces = [Force(end_point_forces, faux1*N.x + faux2*N.y)]
接下来,我们可以将辅助速度和非贡献力添加到系统中。
>>> system.add_loads(*noncontributing_forces)
>>> system.u_aux = [uaux1, uaux2]
要包含重力,我们可以在验证系统并形成运动方程之前使用 apply_uniform_gravity()
。
>>> g = symbols('g')
>>> system.apply_uniform_gravity(-g*N.y)
>>> system.validate_system()
>>> eoms = system.form_eoms()
有了运动方程,我们可以求解非贡献力的辅助方程,并计算其在简单配置下的值。
>>> auxiliary_eqs = system.eom_method.auxiliary_eqs
>>> forces_eqs = Matrix.LUsolve(
... *linear_eq_to_matrix(auxiliary_eqs, [faux1, faux2]))
>>> subs = {
... l1: 2, l2: 1, l3: 2, l4: 1,
... rho: 5, g: 9.81,
... q1: pi/2, q2: pi/2, q3: pi/2,
... u1: 0, u2: 0, u3: 0, u1.diff(): 0, u2.diff(): 0, u3.diff(): 0,
... }
>>> forces_eqs.xreplace(subs)
Matrix([
[ 0],
[-98.1]])