Forum Discussion

🚨 This forum is archived and read-only. To submit a forum post, please visit our new Developer Forum. 🚨
lufinkey's avatar
lufinkey
Protege
3 years ago

Randomly large velocities from standard velocity calculator, but only in APK

I keep having extremely large values coming from the StandardVelocityCalculator that don't make sense with how the hand is moved. At first i thought it was tangential velocity influence, but turning that down still showed issues. However these issues would ONLY show up when making an APK and building to a device. Has anyone else experienced this?

Note: This can be seen in the TransformerExample scene by just attempting to pick up and release an object (often with no controller movement at all)

8 Replies

Replies have been turned off for this discussion
  • Ornlu2's avatar
    Ornlu2
    Honored Guest

    Hi yes I too am experiencing this exact issue. Im still trying to figure out how to fix it? lufinkey have you made any headway on this issue? I looked through the release notes/known issues of the interaction toolkit (we are on v43) and didnt see anything in future releases since that indicate that there is a known issue/bug with this script.

  • We're having the same issue. It's extremely inconsistent. Here's a pic of the logs we got of each drop:

    inconsistent, gigantic velocities, being applied an interactable object after dropping it while holding it as motionless as possible. Doesn't happen in editor, only in build.

    • lufinkey's avatar
      lufinkey
      Protege

      I ended up making my own custom IVelocityCalculator implementation. I'll try to remember to share tomorrow when I'm back on my machine. Feel free to remind me if you don't hear back though

  • Awesome! That was the thought we had as well. We're also going to try clamping the values in the Oculus VelocityCalculator... Not super amped with that solution though. I appreciate your input for sure! Thanks!!

  • Here's the custom "SimpleVelocityCalculator". The only remaining issue with it is that the poses are currently stored in world space, so any teleport will immediately make velocity really large. In our game, users don't need to hold anything while teleporting, so we just haven't gotten around to fixing it. But there's a small TODO in the code with a bit more detail on the fix that it needs (let me know if you have any questions on that and feel free to share if you make the fix). We also call ResetVelocityHistory in InteractableSelected in our custom "HandGrabInteractor" implementation, but you could probably do this within an event callback if you're using the oculus HandGrabInteractor:

    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using UnityEngine;
    using Oculus.Interaction;
    using Oculus.Interaction.Throw;
    
    public class SimpleVelocityCalculator: MonoBehaviour, IVelocityCalculator {
    	public IPoseInputDevice poseInputDevice {
    		get => __poseInputDevice;
    		set {
    			if (__poseInputDevice != value) {
    				__poseInputDevice = value;
    				_poseInputDevice = value as MonoBehaviour;
    				_previousInputPose = GetInputDevicePose();
    			}
    		}
    	}
    	[SerializeField, Interface(typeof(IPoseInputDevice))]
    	private MonoBehaviour _poseInputDevice;
    	private IPoseInputDevice __poseInputDevice;
    	
    	public float maxVelocityChange {
    		get => _maxVelocityChange;
    		set => _maxVelocityChange = value;
    	}
    	[SerializeField]
    	private float _maxVelocityChange = 10.0f;
    	
    	public float maxAngularVelocityChange {
    		get => _maxAngularVelocityChange;
    		set => _maxAngularVelocityChange = value;
    	}
    	[SerializeField]
    	private float _maxAngularVelocityChange = 20.0f;
    	
    	// IMPORTANT TODO!! store poses relative to a tracking space root, to prevent large velocities when teleporting (and then convert velocity vector to world space in CalculateThrowVelocity)
    	private Pose? _previousInputPose = null;
    	private Pose _previousValidInputPose = Pose.identity;
    	
    	private Vector3 _linearVelocity = Vector3.zero;
    	private Vector3 _angularVelocity = Vector3.zero;
    	
    	private List<ReleaseVelocityInformation> _lastThrowVelocities = new List<ReleaseVelocityInformation>();
    	
    	
    	
    	#region Unity Events
    
    	protected void Awake() {
    		__poseInputDevice = _poseInputDevice as IPoseInputDevice;
    	}
    
    	protected void Update() {
    		UpdateVelocities();
    	}
    	#endregion
    	
    	
    	
    	#region IVelocityCalculator
    	float IVelocityCalculator.UpdateFrequency => Mathf.Epsilon;
    	void IVelocityCalculator.SetUpdateFrequency(float frequency) {
    		Debug.LogError("Update frequency cannot be modified on SimpleVelocityCalculator");
    	}
    	public event System.Action<List<ReleaseVelocityInformation>> WhenThrowVelocitiesChanged = delegate { };
    	public event System.Action<ReleaseVelocityInformation> WhenNewSampleAvailable = delegate { };
    	
    	IReadOnlyList<ReleaseVelocityInformation> IVelocityCalculator.LastThrowVelocities() => _lastThrowVelocities;
    	
    	public ReleaseVelocityInformation CalculateThrowVelocity(Transform objectThrown) {
    		_lastThrowVelocities.Clear();
    		ReleaseVelocityInformation releaseInfo = new ReleaseVelocityInformation(
    			_linearVelocity,
    			_angularVelocity,
    			origin: _previousValidInputPose.position,
    			isSelectedVelocity: true);
    		_lastThrowVelocities.Add(releaseInfo);
    		ResetVelocityHistory();
    		WhenThrowVelocitiesChanged(_lastThrowVelocities);
    		return releaseInfo;
    	}
    	#endregion
    	
    	
    	
    	public Pose? GetInputDevicePose() {
    		var inputDevice = this.poseInputDevice;
    		if (inputDevice == null || !inputDevice.IsInputValid || !inputDevice.IsHighConfidence || !inputDevice.GetRootPose(out Pose referencePose)) {
    			return null;
    		}
    		return referencePose;
    	}
    	
    	public void ResetVelocityHistory() {
    		_previousInputPose = null;
    		_linearVelocity = Vector3.zero;
    		_angularVelocity = Vector3.zero;
    	}
    	
    	private void UpdateVelocities() {
    		var newInputPose = GetInputDevicePose();
    		var prevInputPose = _previousInputPose;
    		_previousInputPose = newInputPose;
    		if (newInputPose != null) {
    			_previousValidInputPose = newInputPose.Value;
    		}
    		if (newInputPose == null || prevInputPose == null) {
    			return;
    		}
    		var newPoseVal = newInputPose.Value;
    		var prevPoseVal = prevInputPose.Value;
    		
            var positionDelta = (newPoseVal.position - prevPoseVal.position);
            var rotationDelta = FromToRotationGlobal(prevPoseVal.rotation, newPoseVal.rotation);
    		
            Vector3 velocityTarget = (positionDelta / Time.deltaTime);
            if (!float.IsNaN(velocityTarget.x)) {
                _linearVelocity = Vector3.MoveTowards(_linearVelocity, velocityTarget, _maxVelocityChange);
            }
    		
            rotationDelta.ToAngleAxis(out float angle, out Vector3 axis);
            angle = NormalizeSignedAngle(angle);
            if (angle != 0) {
                Vector3 angularTarget = angle * axis;
                if (!float.IsNaN(angularTarget.x)) {
                    angularTarget = (angularTarget / Time.deltaTime);
                    _angularVelocity = Vector3.MoveTowards(_angularVelocity, angularTarget, _maxAngularVelocityChange);
                }
            }
            
            var newReleaseInfo = new ReleaseVelocityInformation(_linearVelocity, _angularVelocity, newPoseVal.position);
            WhenNewSampleAvailable.Invoke(newReleaseInfo);
    	}
    	
    	private static Quaternion FromToRotationGlobal(Quaternion from, Quaternion to) {
    		return to * Quaternion.Inverse(from);
    	}
    	
    	public static Vector3 NormalizeSignedAngles(Vector3 euler) {
    		euler.x = NormalizeSignedAngle(euler.x);
    		euler.y = NormalizeSignedAngle(euler.y);
    		euler.z = NormalizeSignedAngle(euler.z);
    		return euler;
    	}
    	
    	public static float NormalizeSignedAngle(float angle) {
    		angle %= 360.0f;
    		while (angle > 180.0f) {
    			angle -= 360.0f;
    		}
    		while (angle < -180.0f) {
    			angle += 360.0f;
    		}
    		return angle;
    	}
    }
    • simonmersus's avatar
      simonmersus
      Explorer

      Hey, thanks for the script, it helped me implement a velocity tracker for hand tracking that's working pretty smotthly. I have a question, how do you handle spikes in velocity when tracking is lost ? I tried to implement a frame buffer to discard the first frame after tracking is back but i'm still getting spikes after that (worst scenario is when I'm occluding one of my hand and it is jumping over the place)

      • lufinkey's avatar
        lufinkey
        Protege

        Within this code? Using Vector3.MoveTowards with a max change in velocity might be what you're looking for