英文:
how to move articulations from script and user keyboard
问题
我正在尝试使用Unity制作一个通过键盘命令控制的“机器人手臂”。
我的设计非常基础:
我选择使用“关节”来设计我的机器人手臂,遵循文档中的建议(链接:https://docs.unity3d.com/Manual/physics-articulations.html)。
当我点击播放按钮时,机器人手臂因重力而下降并弹跳:看起来很难看,但这并不是我目前关心的问题。
我想做的是使用键盘上的按钮来控制关节的角度。
我在“手臂”上添加了一个脚本,试图将手臂围绕肩关节移动。
如果我在问这个问题,那是因为它不起作用。
我尝试编辑anchorRotation
的值。但似乎并没有任何效果。
我甚至还没有尝试编写捕捉用户键盘按键的代码,因为显然我甚至不能以编程方式移动关节。
我认为这可能是我没有掌握的某种“Unity概念”有关关节的问题。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shoulder : MonoBehaviour
{
public ArticulationBody shoulder;
// Start is called before the first frame update
void Start()
{
shoulder = GetComponent<ArticulationBody>();
}
// Update is called once per frame
void Update()
{
float tiltAngle = -25.0f;
// float smooth = 30.0f;
float tiltAroundZ = Input.GetAxis("Horizontal") * tiltAngle;
// float tiltAroundX = Input.GetAxis("Vertical") * tiltAngle;
// 通过将角度转换为四元数来旋转立方体。
Quaternion target = Quaternion.Euler(0, 0, tiltAroundZ);
shoulder.anchorRotation = target;
}
}
非常感谢您的帮助。
英文:
I am trying to use Unity to make a "Robot arm" controlled by commands on the keyboard.
My design is really basic:
I chose to use "Articulations" for my design following the recommendations in the documentation here.
When I hit the play button the arm drops and bounces pulled down by gravity: it is quite ugly to see but that is not my concern at the moment.
What I want to do is Command the articulations angles using buttons on my keyboard.
I added a script on the "Arm" attempting to move the arm up around the shoulder joint.
Well if I am asking: that's because it doesn't work.
I tried to edit the value of anchorRotation
. But it doesn't really seem to have any effect.
I didn't even try to write the code that capture user keyboard press yet because obviously I cannot even programmatically move articulations yet.
I believe it is probably some "Unity concept" that I didn't grasp concerning the articulations.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shoulder : MonoBehaviour
{
public ArticulationBody shoulder;
// Start is called before the first frame update
void Start()
{
shoulder = GetComponent<ArticulationBody>();
}
// Update is called once per frame
void Update()
{
float tiltAngle = -25.0f;
// float smooth = 30.0f;
float tiltAroundZ = Input.GetAxis("Horizontal") * tiltAngle;
// float tiltAroundX = Input.GetAxis("Vertical") * tiltAngle;
// Rotate the cube by converting the angles into a quaternion.
Quaternion target = Quaternion.Euler(0, 0, tiltAroundZ);
shoulder.anchorRotation = target;
}
}
Thank you in advance for your help.
答案1
得分: 1
根据我所见,您始终在Update()方法中为您的对象设置相同的旋转。这就是为什么对象不移动的原因。
所以您至少有两个选择:
- 使用transform.Rotate(实际上,我认为这对您的任务足够了)
- 您可以在每次更新时计算不同的值(需要存储对象旋转的当前值,调整更改并将其应用回去)
英文:
As far as I can see, you always set the same rotation for your object in your Update() method. That's why the object does not move.
So you have at least two options:
-
use transform.Rotate (actually, I think, it will be enough for your task)
-
you can calculate different values every update (you need to store the current value of the object's rotation, adjust changes to it and apply it back)
答案2
得分: 1
基于Morion已经尝试解释的内容:
目前,您正在根据输入硬设定旋转。
只要轴的值保持不变(例如键盘上的0
或1
),旋转就会硬性地切换到0
或25
,根据您的值而定。
您实际想要的是从当前旋转开始添加旋转!
您可以使用例如:
shoulder.anchorRotation *= Quaternion.Euler(0, 0, tiltAroundZ);
然而,这样旋转仍然过于快速!
您将在每帧中旋转对象25度
!
您实际想要的是每秒旋转一次,然后执行:
var tiltAroundZ = Input.GetAxis("Vertical") * tiltAngle * Time.deltaTime;
其中Time.deltaTime
是自上一帧以来经过的时间(以秒为单位),从而将您的值从“每帧值”转换为“每秒值”。
然后,正如也提到的,对您来说可能更有趣的是不直接应用旋转,而是确实跟踪已经旋转的数量,以便以后能够像这样进行夹紧:
// 在类级别的字段
private float tiltAroundZ;
...
tiltAroundZ += Input.GetAxis("Vertical") * tiltAngle * Time.deltaTime;
tiltAroundZ = Mathf.Clamp(tiltAroundZ, minAngle, maxAngle);
shoulder.anchorRotation = Quaternion.Euler(0, 0, tiltAroundZ);
英文:
Based on what Morion already tried to explain
Currently you are hard setting the rotation based on the input.
So as long as the axis keeps having the same value (e.g. for keyboard 0
or 1
) the rotation will just hard snap to either 0
or 25
according to your values.
What you rather want to do is rotate your object starting from the current rotation and adding to it!
You can use e.g.
soulder.anchorRotation *= Quaternion.Euler(0, 0, tiltAroundZ);
HOWEVER, this still rotates way too fast!
You would be rotating the object with 25°
per frame!
You rather want to use a rotation per second and do
var tiltAroundZ = Input.GetAxis("Vertical") * tiltAngle * Time.deltaTime;
where Time.deltaTime
is the time in seconds passed since the last frame and thereby converts your value from value per frame
into value per second
.
Then as also mentioned it might get interesting for you to not directly apply the rotation but indeed keep track of the already rotated amount in order to be able to clamp it later on like e.g.
// field at class level
private float tiltAroundZ;
...
tiltAroundZ += Input.GetAxis("Vertical") * tiltAngle * Time.deltaTime;
tiltAroundZ = Mathf.Clamp(tileAroundZ, minAngle, maxAngle);
shoulder.anchorRotation = Quaternion.Euler(0, 0, tiltAroundZ);
答案3
得分: 0
以下是翻译好的部分:
-
"It took me some time to advance on that problem. The problem is not completely solved in the case of "two articulations" but it works for one."(我花了一些时间来解决这个问题。在“两个关节”的情况下问题尚未完全解决,但对于一个关节来说可以工作。)
-
"The answers from Morion and derHugo had been useful (it bootstraped me reading further the unity documentation and made me discover Quaternions) but I had a problem when using the
transform
methods."(来自Morion和derHugo的答案很有用(它启发我深入阅读Unity文档并让我了解了四元数),但在使用transform
方法时遇到了问题。) -
"The problem if I am using
transform.Rotate
method or hard writing aQuaternion
intransform.rotation
is that it positions the object without any concern for the "articulation constraints" or the "physics"."(如果我使用transform.Rotate
方法或在transform.rotation
中硬编写Quaternion
的问题是,它会定位物体而不考虑“关节约束”或“物理”。) -
"If I am using it: why bothering defining an articulation ?"(如果我使用它:为什么要定义关节?)
-
"I was looking for a sort of a function that imposes a movement on an articulation while still respecting the angle and position limitations."(我在寻找一种在尊重角度和位置限制的同时对关节施加运动的函数。)
-
"I found those: * AddForce * AddTorque"(我找到了这些:* AddForce * AddTorque)
-
"These functions apply a force or a torque on the articulation."(这些函数对关节施加力或扭矩。)
-
"If I use these functions, not only the articulation constraints will be respected but also the physics is respected."(如果我使用这些函数,不仅会尊重关节约束,还会尊重物理规则。)
-
"Problem: implementation becomes more challenging."(问题是:实现变得更加具有挑战性。)
-
"I want a "rotation" so I need
AddTorque
but I still need to find the rotation axis of the torque, this axis may vary if the shoulder is moving."(我想要“旋转”,所以我需要AddTorque
,但我仍然需要找到扭矩的旋转轴,如果肩膀在移动,这个轴可能会变化。) -
"I also want the torque to apply rotation in one direction or the other depending on the angle between the direction I want and the direction of the object."(我还希望根据我想要的方向与物体方向之间的角度,扭矩能够施加旋转方向。)
-
"Another thing I understood during this journey is that there is a "local coordinates" axis local to every game object."(在这个过程中我理解的另一件事是,每个游戏对象都有一个本地坐标轴。)
-
"What will prove useful is that these coordinates rotate and move together with the game object when the simulation runs."(有用的是,当模拟运行时,这些坐标会随着游戏对象一起旋转和移动。)
-
"In the local coordinates of the arm the arm is always pointing to the same direction. All I have to do is to figure out: * the global coordinates of where my arm is pointing to (
currentDirection
). * the global coordinates of where I want the arm to point to (target
). For thecurrentDirection
it fits in one line of code:"(在手臂的本地坐标中,手臂始终指向同一个方向。我所要做的就是找出:* 我的手臂指向的全局坐标(currentDirection
) * 我希望手臂指向的全局坐标(target
) 对于currentDirection
来说,它适用于一行代码:) -
"Vector3 currentDirection = transform.TransformVector(localArmInitialDir).normalized;"(Vector3 currentDirection = transform.TransformVector(localArmInitialDir).normalized;)
-
"For the target I believe I am not too far if I do this:"(对于目标,我相信如果我这样做不会太远:)
-
"Vector3 target = (Quaternion.Euler(0, targetYAngle, targetZAngle) * initialDir).normalized;"(Vector3 target = (Quaternion.Euler(0, targetYAngle, targetZAngle) * initialDir).normalized;)
-
"However for the second articulation whose rotation depends on the rotation of the previous one I think that is closer to the truth:"(但是对于第二个关节,其旋转取决于前一个关节的旋转,我认为这更接近事实:)
-
"Vector3 target = (Quaternion.Euler(0, targetYAngle, targetZAngle) * transform.parent.rotation * initialDir).normalized;"(Vector3 target = (Quaternion.Euler(0, targetYAngle, targetZAngle) * transform.parent.rotation * initialDir).normalized;)
-
"I am not sure I can use the
Quaternion.Euler
here that is maybe the reason why it doesn't work well with the elbow."(我不确定我能否在这里使用Quaternion.Euler
,这可能是为什么它在肘部效果不佳的原因。) -
"Then the torque I want is proportional to the cross vectorial product between those two:"(然后我想要的扭矩与这两者之间的
英文:
It took me some time to advance on that problem. The problem is not completely solved in the case of "two articulations" but it works for one.
The answers from Morion and derHugo had been useful (it bootstraped me reading further the unity documentation and made me discover Quaternions) but I had a problem when using the transform
methods.
The problem if I am using transform.Rotate
method or hard writing a Quaternion
in transform.rotation
is that it positions the object without any concern for the "articulation constraints" or the "physics".
If I am using it: why bothering defining an articulation ?
I was looking for a sort of a function that imposes a movement on an articulation while still respecting the angle and position limitations.
I found those:
These functions apply a force or a torque on the articulation.
If I use these functions, not only the articulation constraints will be respected but also the physics is respected.
Problem: implementation becomes more challenging.
I want a "rotation" so I need AddTorque
but I still need to find the rotation axis of the torque, this axis may vary if the shoulder is moving. I also want the torque to apply rotation in one direction or the other depending on the angle between the direction I want and the direction of the object.
Another thing I understood during this journey is that there is a "local coordinates" axis local to every game object. What will prove useful is that these coordinates rotate and move together with the game object when the simulation runs.
In the local coordinates of the arm the arm is always pointing to the same direction. All I have to do is to figure out:
- the global coordinates of where my arm is pointing to (
currentDirection
). - the global coordinates of where I want the arm to point to (
target
).
For thecurrentDirection
it fits in one line of code:
Vector3 currentDirection = transform.TransformVector(localArmInitialDir).normalized;
For the target I believe I am not too far if I do this:
Vector3 target = (Quaternion.Euler(0, targetYAngle, targetZAngle) * initialDir).normalized;
However for the second articulation whose rotation depends on the rotation of the previous one I think that is closer to the truth:
Vector3 target = (Quaternion.Euler(0, targetYAngle, targetZAngle) * transform.parent.rotation * initialDir).normalized;
I am not sure I can use the Quaternion.Euler
here that is maybe the reason why it doesn't work well with the elbow.
Then the torque I want is proportional to the cross vectorial product between those two:
Vector3 torqueVector = force * Vector3.Cross(currentDirection, target);
First a few member variables
public string articulationName; // name of the articulation for the logs
public float targetYAngle; // desired angle around Y axis
public float targetZAngle; // desired angle around Z axis
public float force; // amplify the torque using this factor
public ArticulationBody articulation; // articulation component
public Vector3 localArmInitialDir; // unit axis "forward" of the arm in the local coordinates of the arm referential
public Vector3 initialDir; // forward direction of the arm in the begining
In this example the "forward" axis of the arm is (0, -1, 0)
"arm pointing downwards" and the rotation axis is the z axis (0, 0, 1)
.
The initialDir
is computed like this in the Start
function:
void Start()
{
initialDir = transform.TransformVector(localArmInitialDir).normalized;
}
Next important thing is I need to write the code in the FixedUpdate
function instead of Update
because I am hitting on physics.
Then the code is:
void FixedUpdate() {
Vector3 currentDirection = transform.TransformVector(localArmInitialDir).normalized;
Vector3 target = (Quaternion.Euler(0, targetYAngle, targetZAngle) * transform.parent.rotation * initialDir).normalized;
Debug.Log(articulationName + " current direction: " + currentDirection);
Debug.Log(articulationName + " current target: " + target);
float angle = Quaternion.Angle(Quaternion.FromToRotation(initialDir, currentDirection), Quaternion.FromToRotation(initialDir, target));
Debug.Log(articulationName + " current angle: " + angle);
Vector3 torqueVector = force * Vector3.Cross(currentDirection, target);
Debug.Log(articulationName + " force: " + torqueVector);
articulation.AddTorque(torqueVector);
}
I can apply the script on the shoulder and it "kind of works". The arm bounces a bit, I can reduce that effect by increasing both the torque's force and the articulation "joint friction".
My problem now is when I run that script on the elbow. The movements of the elbow are affecting the shoulder articulation as well.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论