Skip to content

Commit

Permalink
Add swing-twist decomposition feedback controller
Browse files Browse the repository at this point in the history
  • Loading branch information
glopesdev committed Aug 10, 2024
1 parent 1f583a2 commit bbde264
Showing 1 changed file with 59 additions and 0 deletions.
59 changes: 59 additions & 0 deletions OpenEphys.Commutator/QuaternionTwistController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;
using System.ComponentModel;
using System.Linq;
using System.Numerics;
using System.Reactive.Linq;
using Bonsai;

namespace OpenEphys.Commutator
{
/// <summary>
/// Calculates a feedback control signal to compensate for twisting about the specified axis,
/// in units of turns.
/// </summary>
[Description("Calculates a feedback control signal to compensate for twisting about the specified axis, in units of turns.")]
public class QuaternionTwistController : Combinator<Quaternion, double>
{
/// <summary>
/// Gets or sets the direction vector specifying the axis around which to calculate the twist feedback.
/// </summary>
[TypeConverter(typeof(NumericRecordConverter))]
[Description("The direction vector specifying the axis around which to calculate the twist feedback.")]
public Vector3 RotationAxis { get; set; } = Vector3.UnitZ;

/// <summary>
/// Calculates a feedback control signal from an observable sequence of rotation measurements
/// to compensate for twisting about the specified axis.
/// </summary>
/// <param name="source">The sequence of rotation measurements.</param>
/// <returns>The sequence of controller feedback values, in units of turns.</returns>
public override IObservable<double> Process(IObservable<Quaternion> source)
{
return Observable.Defer(() =>
{
double? previousTwist = default;
return source.Select(rotation =>
{
// project rotation axis onto the direction axis
var direction = RotationAxis;
var rotationAxis = new Vector3(rotation.X, rotation.Y, rotation.Z);
var dotProduct = Vector3.Dot(rotationAxis, direction);
var projection = dotProduct / Vector3.Dot(direction, direction) * direction;
var twist = new Quaternion(projection, rotation.W);
twist = Quaternion.Normalize(twist);
if (dotProduct < 0) // account for angle-axis flipping
{
twist = -twist;
}

// normalize twist feedback in units of turns
var twistAngle = 2 * Math.Acos(twist.W);
var feedback = previousTwist.HasValue
? (twistAngle - previousTwist.GetValueOrDefault() + 3 * Math.PI) % (2 * Math.PI) - Math.PI
: 0;
return -feedback / (2 * Math.PI);
});
});
}
}
}

0 comments on commit bbde264

Please sign in to comment.