0

I am trying to use Unity to make a "Robot arm" controlled by commands on the keyboard.

enter image description here

My design is really basic:

enter image description here

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.

derHugo
  • 83,094
  • 9
  • 75
  • 115
mpsido
  • 167
  • 4

3 Answers3

1

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)
Morion
  • 10,495
  • 1
  • 24
  • 33
  • in my simulation the object actually moves: he spins and bounces randomly with no apparent order. I also tried this version of the update function: no better result: `this.transform.Rotate(25.0f, 25.0f, 25.0f, Space.Self);` – mpsido Mar 01 '23 at 10:02
1

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);
derHugo
  • 83,094
  • 9
  • 75
  • 115
  • Thanks for the reply I rearranged my game objects's angles so they are more simple to visualize. I set the rotation of all the game objects as to (0, 0, 0). I did the same for the AnchorRotation. I try to rotate the cylinder representing the arm around the Z axis (back/front axis). The `anchorRotation` seems to be only rotating the "anchor" with no effect on the attached cylinder. If I just use `Transform.Rotate` the cylinder (Arm) spins around its own axis. last update is I have been trying this: `transform.RotateAround(shoulder.anchorPosition, Vector3.back, tiltAngle);` – mpsido Mar 02 '23 at 14:41
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.

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 the currentDirection 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

enter image description here

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.

mpsido
  • 167
  • 4
  • Still looking for the right formula to compute the target Vector3. Trying this `transform.parent.rotation * (Quaternion.AngleAxis(targetYAngle, initialDir) * Quaternion.AngleAxis(targetZAngle, initialUpDir) * initialDir).normalized;` I believe I still need to learn a bit of mathematics about how to chain rotations using quaternions – mpsido Mar 17 '23 at 06:37