Auto-IK: Automating IK constraint creation in Blender

This article explains how to write a Python script in Blender that creates multiple IK constraints for a selected armature. This script will do the following:

  1. Ensure that the selected object is an armature.
  2. Find IK bones indicated with '.ik' at the end of their names.
  3. Create and label target and pole bones for each IK bone.
  4. Apply an IK constraint and set the target and pole.

Auto-IK script caveats:

Prerequisite knowledge: creating armatures with IK constraints, Python programming, and using the Blender console.

1) Ensure that the selected object is an armature

This article uses the bpy module to access and modify the properties of Blender objects. Import the module at the top of the script:

import bpy

All modules and sub-modules of bpy can be viewed by hovering the cursor over GUI elements in Blender. You can enable this feature under User Preferences > Interface and check 'Python Tooltips' under Development.

Get selected object

Set obj as the current selected object with bpy.context.active_object:

obj = bpy.context.active_object 

The selected object obj is not explicltly an armature because bpy.context.active_object can be an object of any type.

You can check obj's object type with .type:

print(obj.type)

This will print an all-caps string of the selected object's type (eg. MESH or ARMATURE).

Check the selected object's type

Define a new method check_obj() with obj as a parameter:

def check_obj(obj):

This method must be the first entry point of your script so that obj's type is an armature before other methods attempt to modify it.

Add the following conditional statement to check obj's type with .type and print a message to the console if it is not an armature type:

def check_obj(obj):
    if obj.type != 'ARMATURE':
        print("No armature selected.")
    else:
        # proceed with code

Get armature data from selected object

Define two separate variables to modify the armature in different contexts:

Set armature as the variable to access the armature's readonly data with obj.data:

def check_obj(obj):
    if obj.type != 'ARMATURE':
        print("No armature selected.")
    else:
        armature = obj.data

Set armature_pose as the variable for modifying the armature in Pose Mode with obj.pose:

def check_obj(obj):
    if obj.type != 'ARMATURE':
        print("No armature selected.")
    else:
        armature = obj.data
        armature_pose = obj.pose

2) Identify IK bones within the armature

Access individual bones

There are multiple modules for editing or getting the properties of individual bones. This article uses the following modules to access the bones within an armature:

Each module takes a bone's name as a key. For example:

obj.data.bones["Bone.001"]     
obj.pose.bones["Bone.001"]     
obj.data.edit_bones["Bone.001"] 

Check for '.ik' at the end of bone names

Define a new method ik_bone_names() with armature as a parameter:

def ik_bone_names(armature):

Create an empty list names to store identified IK bone names as strings:

def ik_bone_names(armature):
    names = []

Iterate through each bone name in armature.bones and use a substring of bone.name to check if the bone's name ends in '.ik'. Then add the names of IK bones to names and return the list:

def ik_bone_names(armature):
    names = []
    for bone in armature.bones:
        if bone.name[-3:] == ".ik":
            names.append(bone.name)
    return names     

If names is returned as empty, then no IK bones were found. In the check_obj() method, add a conditional statement to print a message to the console if ik_bone_names()'s length is 0:

# check_obj()
...
armature = obj.data
armature_pose = obj.pose
if len(ik_bone_names(armature)) == 0:
    print("No IK bones found. Did you add '.ik' to the end of the bone names?")

If the length of ik_bone_names() is not 0, then proceed with a new autoik() method that takes armature, armature_pose, and ik_bone_names(armature) as arguments:

# check_obj()
...
if len(ik_bone_names(armature)) == 0:
    print("No IK bones found. Did you add '.ik' to the end of the bone names?")
else:
    autoik(armature, armature_pose, ik_bone_names(armature))

The resulting code for check_obj() should be as shown below:

def check_obj(obj):
    if obj.type != 'ARMATURE':
        print("No armature selected.")
    else:
        armature = obj.data # armature
        armature_pose = obj.pose # armature for editing bones in pose mode
        if len(ik_bone_names(armature)) == 0:
            print("No IK bones found. Did you add '.ik' to the end of the bone names?")
        else:
            autoik(armature, armature_pose, ik_bone_names(armature))

3) Create target and pole

An IK constraint requires a target and a pole. The following steps explain how to create a target and pole bone.

Define the method autoik() with armature, armature_pose, and ik_bone_names as parameters:

def autoik(armature, armature_pose, ik_bone_names):

The autoik() method will create the target and pole bones and apply an IK constraint to each IK bone.

Modifying individual bones

The following example represents a single IK bone with two variables:

Iterate through the names in ik_bone_names and use them as keys for armature.bones and armature_pose.bones. Set ik_bone and ik_bone_pose to be our variables for modifying the current IK bone:

# autoik()
...
for name in ik_bone_names: 
    ik_bone = armature.bones[name] # current IK bone's readonly data
    ik_bone_pose = armature_pose.bones[name] # current IK bone's constraints

Enumerate target and pole names

Since your script will be creating target and pole bones for each IK bone, there will be multiple target and pole pairs total. You want to the target and pole names enumerated with respect to each IK bone. For example:

target1, pole1, target2, pole2, target3, pole3 ...

In the autoik() method before the for name in ik_bone_names: statement, set a new variable n to 0 and increment it by 1 for every new IK bone:

def autoik(armature, armature_pose, ik_bone_names:)
    n = 0
    for name in ik_bone_names:
        ik_bone = armature.bones[name] 
        ik_bone_pose = armature_pose.bones[name] 
        n += 1

This is your counter for labeling targets and pole pairs in accordance with the order of each IK bone. Concatenate n to the target and pole names, t and p:

# autoik()
...
ik_bone = armature.bones[name] 
ik_bone_pose = armature_pose.bones[name] 
n += 1
t = 'target' + str(n) # pole name
p = 'pole' + str(n) # target name

Now t and p are the names of each target and pole enumerated with respect to every IK bone.

Create target bone

To add new bones to the selected armature, set the object interaction mode to Edit Mode with bpy.ops.object.mode_set():

# autoik()
...
for name in ik_bone_names: 
    ...
    bpy.ops.object.mode_set(mode='EDIT')

Set edit as the variable to modify the armature in Edit Mode:

 edit = bpy.context.object.data.edit_bones

You want the target's head to intersect the IK bone's tail and its tail to extend along the X-axis by 1 unit as shown in the diagram below:

Positioning bones requires the following:

Create a new bone with .new and set its name to t:

edit.new(t)

Set the global location of t's head to the local location of the current IK bone's tail:

edit[t].head = ik_bone.tail_local

Set the global location of t's tail to the local location of the current IK bone's tail:

edit[t].tail = ik_bone.tail_local

Note that if a bone has no length, it is automatically deleted. Use .x to move t's tail from its head in the X direction by 1 unit:

t.tail.x += 1

Create the pole bone

To maintain control over the IK bone's rotation, you want the pole's head to intersect the IK bone's head and its tail to extend along the X-axis by 1 unit as shown in the image below:

Create a new bone with .new and set its name to p:

edit.new(p)

Set the global location of p's head to the local location of the current IK bone's head:

edit[p].head = ik_bone.head_local

Set the global location of p's tail to the local location of the current IK bone's head:

edit[p].tail = ik_bone.heaad_local

Use .x to move t's tail from its head in the X direction by 1 unit:

t.tail.x += 1

The resulting code for autoik() should be as shown:

def autoik(armature, armature_pose, ik_bone_names):
    n = 0
    for name in ik_bone_names:
        ik_bone = armature.bones[name] 
        ik_bone_pose = armature_pose.bones[name] 
        n += 1
        t = 'target'+str(n)
        p = 'pole'+str(n)
        bpy.ops.object.mode_set(mode='EDIT')
        edit = bpy.context.object.data.edit_bones
        # create target bone
        edit.new(t)
        edit[t].head = ik_bone.tail_local
        edit[t].tail = ik_bone.tail_local
        edit[t].tail.x += 1
        # create pole bone
        edit.new(p)
        edit[p].head = ik_bone.head_local
        edit[p].tail = ik_bone.head_local
        edit[p].tail.x += 1  

4) Apply IK constraint

In the autoik() method, set the object interaction mode to Pose Mode:

# autoik()
...
bpy.ops.object.mode_set(mode='POSE')

Now that you are in Pose Mode, the variable for modifying the current IK bone is ik_bone_pose. Add a new constraint 'IK' to the current IK bone with .constraints.new():

ik_bone_pose.constraints.new('IK') 

Use 'IK' as a key for .constraints and set the selected armature object, obj, as the target of the IK constraint with .target:

ik_bone_pose.constraints['IK'].target = obj

(Note: obj is not a parameter of autoik(), but is still accessible through check_obj() which encapsulates autoik().)

Set the target bone's .subtarget to the target bone's name, t:

ik_bone_pose.constraints['IK'].subtarget = t

Set the .pole_target to obj. Then set the pole's .pole_subtarget to the pole bone's name, p:

ik_bone_pose.constraints['IK'].pole_target = obj
ik_bone_pose.constraints['IK'].pole_subtarget = p

Entire script:

This is the final auto-IK Python script which applies IK constraints to a selected armature:
import bpy

# Get list of names of ik bones
def ik_bone_names(armature):
    names = [] # name of ik bones
    for bone in armature.bones:
        if bone.name[-3:] == ".ik":
            names.append(bone.name)
    return names

# Apply IK constraints to armature
def autoik(armature, armature_pose, ik_bone_names):
    n = 0
    for name in ik_bone_names:
        # current IK bone
        ik_bone = armature.bones[name] 
        ik_bone_pose = armature_pose.bones[name] 
        n += 1
        t = 'target'+str(n)
        p = 'pole'+str(n)
        # create target bone
        bpy.ops.object.mode_set(mode='EDIT')
        edit = bpy.context.object.data.edit_bones
        edit.new(t)
        edit[t].head = ik_bone.tail_local
        edit[t].tail = ik_bone.tail_local
        edit[t].tail.x += 1
        # create pole bone
        edit.new(p)
        edit[p].head = ik_bone.head_local
        edit[p].tail = ik_bone.head_local
        edit[p].tail.x += 1  
        # add ik constraint to ik bone
        bpy.ops.object.mode_set(mode='POSE')
        ik_bone_pose.constraints.new('IK')
        ik_bone_pose.constraints['IK'].target = obj
        ik_bone_pose.constraints['IK'].subtarget = t
        ik_bone_pose.constraints['IK'].pole_target = obj
        ik_bone_pose.constraints['IK'].pole_subtarget = p

# Check if selected object is an armature
def check_obj(obj):
    if obj.type != 'ARMATURE':
        print("No armature selected.")
    else:
        armature = obj.data # armature
        armature_pose = obj.pose # armature for editing bones in pose mode
        if len(ik_bone_names(armature)) == 0:
            print("No IK bones found. Did you add '.ik' to the end of the bone names?")
        else:
            autoik(armature, armature_pose, ik_bone_names(armature))

obj = bpy.context.active_object     
check_obj(obj)