Inverse Kinematics Issue

I am currently trying to make a robotic arm with inverse kinematics using the script below to calculate it. But there is an issue when you bring the arm into certain positions which I think is caused by the position from P1 to P2 being less than the distance to the midpoint.

I’ve scanned the devforum a bit but couldnt find a solution.

Video Example of the issue: https://gyazo.com/59a03af92014e86e8e41db85038ee88a

local P1, P2 = game.Workspace.P1, workspace.P2


local joint = P1.Position + P2.Position
joint = joint/2

local Distance = (P1.Position - P2.Position).Magnitude/2

local Segment1 = game.Workspace.S1
local Segment2 = game.Workspace.S2

function raysegment(Segment,P1,P2)
	Segment.CFrame = CFrame.new(P1,P2) * CFrame.new(0,0,-Distance/2)
end

function FindEndpoint(Segment, point)
	local p1 = Segment.Position + Vector3.new(0,0,Segment.Size.Z/2)
	local p2 = Segment.Position - Vector3.new(0,0,Segment.Size.Z/2)
	local x,y,z = Segment.CFrame:toEulerAnglesXYZ()
	local Angle = CFrame.Angles(x,y,z)
	local cf1 = Segment.CFrame * Angle * (Segment.CFrame:Inverse() * CFrame.new(p1)).p
	local cf2 = Segment.CFrame * Angle * (Segment.CFrame:Inverse() * CFrame.new(p2)).p
	
	local mag1 = (cf1 - point).Magnitude
	local mag2 = (cf2 - point).Magnitude
	
	if mag1 < mag2 then
		return cf1
	else
		return cf2
	end
end

raysegment(Segment1, P1.Position, joint)
raysegment(Segment2, joint, P2.Position)

function Solve()
	raysegment(Segment2, P2.Position, joint)
	joint = FindEndpoint(Segment2, P1.Position)
	raysegment(Segment1, joint, P1.Position)
	if Distance == joint then
		
	end
	
	raysegment(Segment1, P1.Position, joint)
	joint = FindEndpoint(Segment1, P2.Position)
	raysegment(Segment2,joint,P2.Position)
end

P1.Changed:Connect(function()
	Solve()
end)

P2.Changed:Connect(function()
	Solve()
end)

Can you explain a little about what FindEndpoint is supposed to do?

It finds the end point of a segment which are the actual sections of the arm which there are 2 of them that are equal lengths

Oh, that’s very confusing for what it does. Just for my own understanding I simplified it a bit. This does the same thing with less CFrame math:

-- Returns the CFrame for the front/back end of `Segment` which is
-- closer to `point`
function FindEndpoint(Segment, point)
	local halfLength = Segment.Size.Z / 2
	
	local relative = Segment.CFrame:PointToObjectSpace(point)
	
	if relative.Z > 0 then
		return Segment.CFrame * Vector3.new(0, 0, halfLength)
	else
		return Segment.CFrame * Vector3.new(0, 0, -halfLength)
	end
end

But that doesn’t matter anyway, because FindEndpoint is the problem.

You don’t want to decide which endpoint is closest to the joint. You already know which one it is.

In other words, your whole Solve function can just be:

function Solve()
	raysegment(Segment2, P2.Position, joint)
	joint = Segment2.CFrame * Vector3.new(0, 0, -Segment2.Size.Z / 2)
	raysegment(Segment1, joint, P1.Position)

	raysegment(Segment1, P1.Position, joint)
	joint = Segment1.CFrame * Vector3.new(0, 0, -Segment1.Size.Z / 2)
	raysegment(Segment2,joint,P2.Position)
end

And you can throw out the FindEndpoint method entirely.

1 Like

Thanks so much, I’ll try it out and see how it goes.

Also, you didn’t ask, but you’ll have trouble if your IK rig is straight, and you move the target straight back.

You can fix this by checking if the arm is straight first, and jiggling the joint a bit if so (at the top of the Solve function):

	-- quick check if things are colinear so we can jiggle it a bit
	if math.abs(Segment1.CFrame.LookVector:Dot(Segment2.CFrame.LookVector)) > 0.999 then
		joint = joint + Vector3.new(0, 0.001, 0)
	end

I’ll try it now

many characters are needed

Works perfectly

many characters are needed