关于unity3d:铰链关节的无损运动-Unity

Lossless movement in hinge joints - Unity

我已经在Unity中创建了一个简单的摆锤-带有刚体和铰链接头组件的GameObject。我已经将两个拖动角度和角度拖动都设置为0。在90度的起始位置,我希望钟摆能从90度到-90度来回摆动。但是,情况并非如此-幅度衰减非常快,但是对于小角度,摆似乎永远不会停止。

我的问题是:我应该如何配置铰链接头,以完全控制物理和抵抗运动的力???我的目标是使物理模拟尽可能精确,即使以性能为代价。

我已经尝试过减少固定步长的时间间隔并增加求解器的迭代次数-这些都没有解决。

我为什么需要它?我打算为一个推车上的多个倒立摆设计一个控制系统。我有一个在Matlab中实现的摆锤的数学模型,我想在Unity中使用一个简单的模型对其进行验证(因为在这种情况下,我会调整所有参数,初始条件等,而物理引擎正在为我计算一切)。如果事实证明支持Unity的物理引擎不够可靠,您还会推荐我使用什么其他软件?


将刚体上的maxAngularVelocity设置为Mathf.Infinity。

我知道这个话题已经有9个月了,但是由于这个问题,我最近一直在撞墙。由于某些原因,Unity开发人员认为将刚性体的最大旋转速度限制为每秒7弧度是一个好主意!每秒仅旋转一圈多一点,对于任何需要物理精度的应用来说太低了。最重要的是,该属性在检查器或物理设置中不可见!

希望对您有帮助(如果您还没有自己解决),以及将来可能与这个问题作斗争的其他任何人都可以欢呼!


我的理解是,由于Unity物理学的运行方式,如果仅使用铰链关节,则这种摆运动会随着时间的流逝而失去动能。基本上,如果您想进行精确的摆锤模拟,则必须绕过物理引擎并直接实现它。

MLM最初在gamedev stackexchange上有一篇非常好的文章,内容涉及如何在Unity中实现更精确的摆锤模拟,我在下面进行了粘贴。

我以为这是一个相对简单的问题,但是我花了几天的时间试图弄清楚如何模拟摆运动。我不想作弊,只是根据sin(theta)和cos(theta)曲线更改x,y位置。相反,我想应对现实生活中应用的两种力量:重力和张力。我所缺少的主要是向心力。

摆锤(数学)维基百科页面上有一个很棒的动画(在左下),用于说明摆锤的运动。您可以看到我的结果(右)与该图非常相似

" bob"是摆动的对象," pivot"是起点/根。

Pendulum Motion: velocity and acceleration >
</p>
<p>
我还发现本文和下面的图表很有帮助:
</p>
<p>
<img src=

当鲍勃接近平衡点(中间)时,张力更大的原因是由于向心力:

(m*v^2)/ropeLength

因此,随着鲍勃摆动,总体张力公式如下:

m*g*cos(theta) + (m*v^2)/ropeLength

摆系统中有两种作用力:

  • 重力

    • GravityForce = mass * gravity.magnitude
    • GravityDirection = gravity.normalized
  • 张力

    • TensionForce = (mass * gravity * Cos(theta)) + ((mass * velocityTangent^2)/ropeLength)
    • TensionDirection = ropeDirection = bob to pivot

只需像对待普通物体一样对物体施加重力,然后施加张力即可。施加力时,只需将力乘以方向和deltaTime。

下面是Pendulum.cs脚本(也作为GitHub Gist)。它工作得很好,但是如果您将其放置一段时间(不会返回到完全相同的位置),则会出现一些舍入误差。

该脚本可在3D模式下运行,但当然只能在2D平面内摆动。它也可以在任何方向上与重力共同作用。因此,例如,如果您反转重力,则钟摆将上下颠倒。 Edit->Project Settings->Physics->Gravity

在更新钟摆时,始终保持相对较小的deltaTime非常重要,这样您就不会在曲线周围反弹。我正在使用本文中发现的技巧,修复您的时间!由Glenn Fiedler完成。检查下面的Update()函数,了解我如何实现它。

>
</p>
<p>也作为GitHub Gist</p>
<div class=

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
using UnityEngine;
using System.Collections;

// Author: Eric Eastwood (ericeastwood.com)
//
// Description:
//      Written for this gd.se question: http://gamedev.stackexchange.com/a/75748/16587
//      Simulates/Emulates pendulum motion in code
//      Works in any 3D direction and with any force/direciton of gravity
//
// Demonstration: https://i.imgur.com/vOQgFMe.gif
//
// Usage: https://i.imgur.com/BM52dbT.png
public class Pendulum : MonoBehaviour {

    public GameObject Pivot;
    public GameObject Bob;


    public float mass = 1f;

    float ropeLength = 2f;

    Vector3 bobStartingPosition;
    bool bobStartingPositionSet = false;

    // You could define these in the `PendulumUpdate()` loop
    // But we want them in the class scope so we can draw gizmos `OnDrawGizmos()`
    private Vector3 gravityDirection;
    private Vector3 tensionDirection;

    private Vector3 tangentDirection;
    private Vector3 pendulumSideDirection;

    private float tensionForce = 0f;
    private float gravityForce = 0f;


    // Keep track of the current velocity
    Vector3 currentVelocity = new Vector3();

    // We use these to smooth between values in certain framerate situations in the `Update()` loop
    Vector3 currentStatePosition;
    Vector3 previousStatePosition;

    // Use this for initialization
    void Start () {
        // Set the starting position for later use in the context menu reset methods
        this.bobStartingPosition = this.Bob.transform.position;
        this.bobStartingPositionSet = true;

        this.PendulumInit();
    }


    float t = 0f;
    float dt = 0.01f;
    float currentTime = 0f;
    float accumulator = 0f;

    void Update()
    {
        /* */
        // Fixed deltaTime rendering at any speed with smoothing
        // Technique: http://gafferongames.com/game-physics/fix-your-timestep/
        float frameTime = Time.time - currentTime;
        this.currentTime = Time.time;

        this.accumulator += frameTime;

        while (this.accumulator >= this.dt)
        {
            this.previousStatePosition = this.currentStatePosition;
            this.currentStatePosition = this.PendulumUpdate(this.currentStatePosition, this.dt);
            //integrate(state, this.t, this.dt);
            accumulator -= this.dt;
            this.t += this.dt;
        }

        float alpha = this.accumulator/this.dt;

        Vector3 newPosition = this.currentStatePosition*alpha + this.previousStatePosition*(1f-alpha);

        this.Bob.transform.position = newPosition; //this.currentStatePosition;
        /* */

        //this.Bob.transform.position = this.PendulumUpdate(this.Bob.transform.position, Time.deltaTime);
    }


    // Use this to reset forces and go back to the starting position
    [ContextMenu("Reset Pendulum Position")]
    void ResetPendulumPosition()
    {
        if(this.bobStartingPositionSet)
            this.MoveBob(this.bobStartingPosition);
        else
            this.PendulumInit();
    }

    // Use this to reset any built up forces
    [ContextMenu("Reset Pendulum Forces")]
    void ResetPendulumForces()
    {
        this.currentVelocity = Vector3.zero;

        // Set the transition state
        this.currentStatePosition = this.Bob.transform.position;
    }

    void PendulumInit()
    {
        // Get the initial rope length from how far away the bob is now
        this.ropeLength = Vector3.Distance(Pivot.transform.position, Bob.transform.position);
        this.ResetPendulumForces();
    }

    void MoveBob(Vector3 resetBobPosition)
    {
        // Put the bob back in the place we first saw it at in `Start()`
        this.Bob.transform.position = resetBobPosition;

        // Set the transition state
        this.currentStatePosition = resetBobPosition;
    }


    Vector3 PendulumUpdate(Vector3 currentStatePosition, float deltaTime)
    {
        // Add gravity free fall
        this.gravityForce = this.mass * Physics.gravity.magnitude;
        this.gravityDirection = Physics.gravity.normalized;
        this.currentVelocity += this.gravityDirection * this.gravityForce * deltaTime;

        Vector3 pivot_p = this.Pivot.transform.position;
        Vector3 bob_p = this.currentStatePosition;


        Vector3 auxiliaryMovementDelta = this.currentVelocity * deltaTime;
        float distanceAfterGravity = Vector3.Distance(pivot_p, bob_p + auxiliaryMovementDelta);

        // If at the end of the rope
        if(distanceAfterGravity > this.ropeLength || Mathf.Approximately(distanceAfterGravity, this.ropeLength))
        {

            this.tensionDirection = (pivot_p - bob_p).normalized;

            this.pendulumSideDirection = (Quaternion.Euler(0f, 90f, 0f) * this.tensionDirection);
            this.pendulumSideDirection.Scale(new Vector3(1f, 0f, 1f));
            this.pendulumSideDirection.Normalize();

            this.tangentDirection = (-1f * Vector3.Cross(this.tensionDirection, this.pendulumSideDirection)).normalized;


            float inclinationAngle = Vector3.Angle(bob_p-pivot_p, this.gravityDirection);

            this.tensionForce = this.mass * Physics.gravity.magnitude * Mathf.Cos(Mathf.Deg2Rad * inclinationAngle);
            float centripetalForce = ((this.mass * Mathf.Pow(this.currentVelocity.magnitude, 2))/this.ropeLength);
            this.tensionForce += centripetalForce;

            this.currentVelocity += this.tensionDirection * this.tensionForce * deltaTime;
        }

        // Get the movement delta
        Vector3 movementDelta = Vector3.zero;
        movementDelta += this.currentVelocity * deltaTime;


        //return currentStatePosition + movementDelta;

        float distance = Vector3.Distance(pivot_p, currentStatePosition + movementDelta);
        return this.GetPointOnLine(pivot_p, currentStatePosition + movementDelta, distance <= this.ropeLength ? distance : this.ropeLength);
    }

    Vector3 GetPointOnLine(Vector3 start, Vector3 end, float distanceFromStart)
    {
        return start + (distanceFromStart * Vector3.Normalize(end - start));
    }

    void OnDrawGizmos()
    {
        // purple
        Gizmos.color = new Color(.5f, 0f, .5f);
        Gizmos.DrawWireSphere(this.Pivot.transform.position, this.ropeLength);

        Gizmos.DrawWireCube(this.bobStartingPosition, new Vector3(.5f, .5f, .5f));


        // Blue: Auxilary
        Gizmos.color = new Color(.3f, .3f, 1f); // blue
        Vector3 auxVel = .3f * this.currentVelocity;
        Gizmos.DrawRay(this.Bob.transform.position, auxVel);
        Gizmos.DrawSphere(this.Bob.transform.position + auxVel, .2f);

        // Yellow: Gravity
        Gizmos.color = new Color(1f, 1f, .2f);
        Vector3 gravity = .3f * this.gravityForce*this.gravityDirection;
        Gizmos.DrawRay(this.Bob.transform.position, gravity);
        Gizmos.DrawSphere(this.Bob.transform.position + gravity, .2f);

        // Orange: Tension
        Gizmos.color = new Color(1f, .5f, .2f); // Orange
        Vector3 tension = .3f * this.tensionForce*this.tensionDirection;
        Gizmos.DrawRay(this.Bob.transform.position, tension);
        Gizmos.DrawSphere(this.Bob.transform.position + tension, .2f);

        // Red: Resultant
        Gizmos.color = new Color(1f, .3f, .3f); // red
        Vector3 resultant = gravity + tension;
        Gizmos.DrawRay(this.Bob.transform.position, resultant);
        Gizmos.DrawSphere(this.Bob.transform.position + resultant, .2f);


        /* * /
        // Green: Pendulum side direction
        Gizmos.color = new Color(.3f, 1f, .3f);
        Gizmos.DrawRay(this.Bob.transform.position, 3f*this.pendulumSideDirection);
        Gizmos.DrawSphere(this.Bob.transform.position + 3f*this.pendulumSideDirection, .2f);
        /* */

        /* * /
        // Cyan: tangent direction
        Gizmos.color = new Color(.2f, 1f, 1f); // cyan
        Gizmos.DrawRay(this.Bob.transform.position, 3f*this.tangentDirection);
        Gizmos.DrawSphere(this.Bob.transform.position + 3f*this.tangentDirection, .2f);
        /* */
    }
}

更多魅力照片:

> <img src=