Zum Hauptinhalt springen

Einführung in die Kraftrückmeldung

In diesem Tutorial erfahren Sie, wie Sie eine einfache taktile Simulation erstellen, die sowohl Steifigkeit als auch Dämpfung berücksichtigt, um die physikalischen Eigenschaften beim Kontakt mit statischen Objekten (z. B. einer Kugel) zu simulieren. Nach Abschluss dieses Tutorials verfügen Sie über ein Simulationsprogramm, das die Anwesenheit der Kugel wahrnimmt und dessen Steifigkeits- und Dämpfungseigenschaften angepasst werden können, um unterschiedliche taktile Erfahrungen zu erzielen.

Einführung​

Die zentrale Herausforderung dieses Tutorials besteht darin, eine Funktion zu entwickeln, mit der sich die Kräfte berechnen lassen, die beim Kontakt mit einer Kugel entstehen, die sowohl über Steifigkeit als auch über Dämpfung verfügt. In diesem Zusammenhang verhält sich die Steifigkeit ähnlich wie eine Feder: Je stärker sie zusammengedrückt wird, desto größer ist die erzeugte Kraft. Die Dämpfung hingegen bezeichnet den Widerstand, den ein Objekt der Bewegung entgegenbringt: Je schneller sich das Objekt bewegt, desto größer ist der Widerstand, dem es ausgesetzt ist.

Szeneneinstellungen​

Zunächst einmal gemäß Einführungsleitfaden Befolgen Sie die Anweisungen zum Einrichten des Haptic Rig und stellen Sie sicher, dass Haptischer Ursprung Position, Drehung und Skalierung wurden jeweils auf folgende Werte eingestellt: (0, 0, 0) und (1, 1, 1)。

Erstellen Sie anschließend eine Datei mit dem Namen Sphere eine Kugel und legen Sie folgende Eigenschaften fest:

  • Standort:(0, 0, -0.1)(etwa 10 cm vor dem Gerät)
  • Zoom:(0.2, 0.2, 0.2)(entspricht einer Kugel mit einem Durchmesser von 20 cm)

Skript für Kraftrückmeldung​

für Sphäre Fügen Sie dem Spielobjekt ein neues C#-Skript hinzu und benennen Sie es SphereForceFeedback.cs。 Dieses Skript berechnet bei Kontakt mit einer Kugel die auf den Cursor „ Inverse3 “ wirkende Kraft unter Berücksichtigung von Steifigkeit und Dämpfung. Initialisieren Sie das Skript mit den folgenden Eigenschaften:

[SerializeField]
private Inverse3 inverse3;

[Range(0, 800)]
public float stiffness = 300f;
[Range(0, 3)]
public float damping = 1f;

private Vector3 _ballPosition;
private float _ballRadius;
private float _cursorRadius;

Die Kraftberechnung sollte nur dann erfolgen, wenn der Cursor die Kugel durchdringt, um das Gefühl bei der Berührung eines realen Objekts mit bestimmter Steifigkeit und Dämpfung zu simulieren. ForceCalculation Die Methode übernimmt diese Berechnung unter Berücksichtigung der Position und Geschwindigkeit des Cursors:

private Vector3 ForceCalculation(Vector3 cursorPosition, Vector3 cursorVelocity, float cursorRadius,
Vector3 otherPosition, float otherRadius)
{
var force = Vector3.zero;

var distanceVector = cursorPosition - otherPosition;
var distance = distanceVector.magnitude;
var penetration = otherRadius + cursorRadius - distance;

if (penetration > 0)
{
// Normalize the distance vector to get the direction of the force
var normal = distanceVector.normalized;

// Calculate the force based on penetration
force = normal * penetration * stiffness;

// Apply damping based on the cursor velocity
force -= cursorVelocity * damping;
}

return force;
}

private void OnDeviceStateChanged(object sender, Inverse3EventArgs args)
{
var inverse3 = args.DeviceController;
// Calculate the ball force
var force = ForceCalculation(inverse3.CursorLocalPosition, inverse3.CursorLocalVelocity,
_cursorRadius, _ballPosition, _ballRadius);

inverse3.SetCursorLocalForce(force);
}

in Awake In der Methode: Initialisierung _ballPosition、_ballRadius und _cursorRadius, um die Szenendaten festzulegen:

private void SaveSceneData()
{
var t = transform;
_ballPosition = t.position;
_ballRadius = t.lossyScale.x / 2f;

_cursorRadius = inverse3.Cursor.Radius;
}

private void Awake()
{
SaveSceneData();
}

Bitte stellen Sie sicher, dass Sie jeweils in OnEnable und OnDisable An- und Abmeldung in der Methode OnDeviceStateChanged Callback, um die Kraftrückmeldung während der Interaktion korrekt zu verarbeiten:

protected void OnEnable()
{
inverse3.DeviceStateChanged += OnDeviceStateChanged;
}

protected void OnDisable()
{
inverse3.DeviceStateChanged -= OnDeviceStateChanged;
}

Spielerlebnis​

Wenn Sie den Mauszeiger über „ Inverse3 “ halten, wechselt das Spiel in den Wiedergabemodus. Versuchen Sie, die Kugel zu berühren. Sie sollten die Kugel spüren können. Passen Sie ihre Steifigkeit und Dämpfung im Unity-Inspector an, um ein realistisches Gefühl bei der Interaktion mit dem virtuellen Objekt zu erzielen.

Der Cursor berührt die Kugel

Quelldatei​

Die vollständige Szene dieses Beispiels sowie alle zugehörigen Dateien können über den Unity-Paket-Manager aus den „Tutorials“-Beispielen importiert werden.

SphereForceFeedback.cs​

/*
* Copyright 2024 Haply Robotics Inc. All rights reserved.
*/

using Haply.Inverse.DeviceControllers;
using Haply.Inverse.DeviceData;
using UnityEngine;

namespace Haply.Samples.Tutorials._2_BasicForceFeedback
{
public class SphereForceFeedback : MonoBehaviour
{
public Inverse3Controller inverse3;

[Range(0, 800)]
// Stiffness of the force feedback.
public float stiffness = 300f;

[Range(0, 3)]
public float damping = 1f;

private Vector3 _ballPosition;
private float _ballRadius;
private float _cursorRadius;

/// <summary>
/// Stores the cursor and sphere transform data for access by the haptic thread.
/// </summary>
private void SaveSceneData()
{
var t = transform;
_ballPosition = t.position;
_ballRadius = t.lossyScale.x / 2f;

_cursorRadius = inverse3.Cursor.Radius;
}

/// <summary>
/// Saves the initial scene data cache.
/// </summary>
private void Awake()
{
inverse3 ??= FindObjectOfType<Inverse3Controller>();
inverse3.Ready.AddListener((device, args) => SaveSceneData());
}

/// <summary>
/// Subscribes to the DeviceStateChanged event.
/// </summary>
private void OnEnable()
{
inverse3.DeviceStateChanged += OnDeviceStateChanged;
}

/// <summary>
/// Unsubscribes from the DeviceStateChanged event.
/// </summary>
private void OnDisable()
{
inverse3.DeviceStateChanged -= OnDeviceStateChanged;
inverse3.Release();
}

/// <summary>
/// Calculates the force based on the cursor's position and another sphere position.
/// </summary>
/// <param name="cursorPosition">The position of the cursor.</param>
/// <param name="cursorVelocity">The velocity of the cursor.</param>
/// <param name="cursorRadius">The radius of the cursor.</param>
/// <param name="otherPosition">The position of the other sphere (e.g., ball).</param>
/// <param name="otherRadius">The radius of the other sphere.</param>
/// <returns>The calculated force vector.</returns>
private Vector3 ForceCalculation(Vector3 cursorPosition, Vector3 cursorVelocity, float cursorRadius,
Vector3 otherPosition, float otherRadius)
{
var force = Vector3.zero;

var distanceVector = cursorPosition - otherPosition;
var distance = distanceVector.magnitude;
var penetration = otherRadius + cursorRadius - distance;

if (penetration > 0)
{
// Normalize the distance vector to get the direction of the force
var normal = distanceVector.normalized;

// Calculate the force based on penetration
force = normal * penetration * stiffness;

// Apply damping based on the cursor velocity
force -= cursorVelocity * damping;
}

return force;
}

/// <summary>
/// Event handler that calculates and send the force to the device when the cursor's position changes.
/// </summary>
/// <param name="sender">The Inverse3 data object.</param>
/// <param name="args">The event arguments containing the device data.</param>
private void OnDeviceStateChanged(object sender, Inverse3EventArgs args)
{
var inverse3 = args.DeviceController;
// Calculate the ball force
var force = ForceCalculation(inverse3.CursorLocalPosition, inverse3.CursorLocalVelocity,
_cursorRadius, _ballPosition, _ballRadius);

inverse3.SetCursorLocalForce(force);
}
}
}