compas
compas copied to clipboard
Spherical Linear Interpolation Function (slerp) for Frames
Feature Request
In order to develop robotic motion planners, I want a slerp function between two frames so that I can create interpolated frames between two frames that have not only positional differences but also orientation differences.
Details
Is your feature request related to a problem? Please describe. Given two frames with different origin positions and orientations. I want to interpolate between them linearly, controlled by a single parameter. Given a series of uniformly spaced parameters, the resulting set of interpolated Frames should have equal distances (origin points) and natural-looking gradual orientation change.
Describe the solution you'd like
I would imagine a function signature like this:
Frame.slerp_interpolation(start_frame, end_frame, parameter) -> Frame
where:
parameter is a float between 0.0 and 1.0
Additional context I do not know how this is done properly, but I believe animation software has algorithms like this where the position and orientation of the Frames are interpolated separately and combined later. The origin position can be a simple interpolation and the orientation is interpolated by slerp in quaternion representation. https://en.wikipedia.org/wiki/Slerp
https://docs.unity3d.com/ScriptReference/Quaternion.Slerp.html
Maybe this provides some idea about the slerp function
https://github.com/Unity-Technologies/UnityCsReference/blob/d0e1a5b25e8d3c8e1b4e6baf0073032d97254a0c/Editor/Mono/Utils/MathUtils.cs#L222-L262
https://splines.readthedocs.io/en/latest/rotation/slerp.html
This webpage explains the importance of Slerp having the property of constant angular velocity.
@yck011522 this can be useful for slerp:
t - [0.0, 1.0]
def interpolate_frames(frame0, frame1, t):
"""
Interpolate smoothly between two frames using Slerp and Lerp.
Parameters:
frame0 (Frame): The starting frame.
frame1 (Frame): The ending frame.
t (float): Interpolation parameter between 0 and 1.
Returns:
Frame: The interpolated frame.
"""
# Perform linear interpolation for the position (point) component
point = Point(*frame0.point) * (1 - t) + Point(*frame1.point) * t
# Perform spherical linear interpolation (Slerp) for the orientation (quaternion) component
q0 = Quaternion.from_frame(frame0)
q1 = Quaternion.from_frame(frame1)
def interpolate_quaternion(q0, q1, t):
# Perform subtraction: q1 - q0
q_diff = Quaternion(q1.w - q0.w, q1.x - q0.x, q1.y - q0.y, q1.z - q0.z)
# Perform scaling: t * (q1 - q0)
q_scaled = Quaternion(q_diff.w * t, q_diff.x * t, q_diff.y * t, q_diff.z * t)
# Perform addition: q0 + t * (q1 - q0)
interpolated_quaternion = Quaternion(
q0.w + q_scaled.w, q0.x + q_scaled.x, q0.y + q_scaled.y, q0.z + q_scaled.z
)
return interpolated_quaternion
interpolated_quaternion = interpolate_quaternion(q0, q1, t) # q0 * (1 - t) + q1 * t
# Convert the interpolated quaternion back to a frame
point = Point(*frame0.point) * (1 - t) + Point(*frame1.point) * t
interpolated_frame = Frame.from_quaternion(interpolated_quaternion, point)
return interpolated_frame
Thank you @petrasvestartas I'll test it out in the compas_fab context. But probably it would be good if this feature gets into compas core too.
scipy has slerp: https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.transform.Slerp.html https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.geometric_slerp.html
@tomvanmele what's your take: @romanarust' suggestion of building on top of scipy.spatial || @petrasvestartas implementation as per above?
in general, if we can use something that already exists, we should. and since we stopped caring about IronPython, there is no need to make a pure Python version...
ah, thanks. given that scipy is a dependency in requirements.txt, then that seems the right call.
Thanks all for the effort.