# Robotic Arm Inverse Kinematics

Basically the rundown here is I’m replicating the kinematic system of the Canada Arm which operates on the principle of adjusting joint angles to position the end effector at a specific Vector3 value.

I have it working when the arm is operating from a level base as such: https://gyazo.com/8dbe298712a459bfec14b4c6e5784c7e

However when the base is rotated as displayed in the following gif, the arm is offset from the actual point it is meant to be following.

https://gyazo.com/7c018f46cf0baae323e0cfd01c4de93b

I’m not the best with inverse kinematics and this is really my first experiment with the concept. Any ideas how to simply this function or correct any calculation errors to prevent this undesired effect?

The function for the entire arm is listed below:

function updatejoints()
–//Shoulder Rotation
local differencez = script.Parent.ShoulderBase.CFrame:PointToObjectSpace(script.Parent.WristPos.Position)

``````--//Vertical Computation

--//Shoulder Pitch
local armlength = ((script.Parent.ShoulderBase.Position - script.Parent.WristPos.Position).Magnitude)
local lowerarmlength = ((script.Parent.ShoulderBase.Position - script.Parent.UpperArm.Elbow.Base.Position).Magnitude)
local upperarmlength = ((script.Parent.UpperArm.Elbow.Base.Position - script.Parent.Wrist2.Base.Position).Magnitude)

--//Elbow (calculates opposite and then subtracts both from 180 to find elbow)
``````

end

3 Likes

I transformed your code into a format that made more sense to me. I had to guess what most parts are, could you provide an explanation of the parts involved and their orientations? Otherwise, I’m not immediately seeing any issues.

``````local up = Vector3.new(0, 1, 0)
local function lawOfCosines(a, b, c)
return acos((a*a + b*b - c*c) / (2 * a * b))
end
function updatejoints()
--//Shoulder Rotation
local base_wrist = script.Parent.ShoulderBase.CFrame:Inverse() * script.Parent.WristPos.Position
local aZ = math.atan2(base_wrist.Z, base_wrist.X) - math.pi/2
joints.ShoulderRotation.C0 = CFrame.Angles(0, 0, aZ))

--//Vertical Computation
local s2 = script.Parent.ShoulderBase2
s2.CFrame = CFrame.new(
s2.Position,
script.Parent.WristPos.Position
)

local s2_wrist = script.Parent.WristPos.Position - script.Parent.ShoulderBase2.Position
local angleOfDepression = math.pi/2 - math.acos(up:Dot(s2_wrist.Unit))

--//Shoulder Pitch
local a = (script.Parent.ShoulderBase.Position - script.Parent.WristPos.Position).Magnitude
local b = (script.Parent.ShoulderBase.Position - script.Parent.UpperArm.Elbow.Base.Position).Magnitude
local c = (script.Parent.UpperArm.Elbow.Base.Position - script.Parent.Wrist2.Base.Position).Magnitude

local C = lawOfCosines(a, b, c)
local A = lawOfCosines(b, c, a)

script.Parent.Joints.ShoulderPitch.C0 = CFrame.Angles(0, 0, angleOfDepression + C)
script.Parent.Joints.Elbow.C0 = CFrame.Angles(0, 0, math.pi + A)
end
``````

Pretty sure this is your problem, as you offset the ShoulderAngle only by the Base’s X orientation. Just calculate the angle between the level plane and the plane of the Base instead of using Orientation.X

I was suspicious of this line as well, but for different reasons. First, there are many ways to represent a CFrame in Eular angles and so the X component may never actually change (hence why I choose a different method to calculate it). I believe the current implementation of the CFrame look at constructor attempts to keep the x axis orthogonal to the global y axis, so this may not be an issue in this case. Second, the angle / offset needed is based upon how the joints between parts are configured and their starting CFrames. We need more information.