How to match bone orientation between two objects in Python?
First of all, if you have 2 armatures with different object rotations relative to each-other, simply copying the roll value won't give you (visually) the same orientation (or if it does it will only be by accident).

The basic solution to this is:


Get an orthogonal direction from the bone (X or Z axis).
Transform that axis into world-space.
Transform the axis into the local space of the other object.
Algin all bones (X or Z) to that axis.


This is a working script written by @Tak, updates by @ideasman42:

import bpy
import math

def align_bone_x_axis(edit_bone, new_x_axis):
    """ new_x_axis is a 3D Vector the edit_bone's x-axis will point towards.
    """
    new_x_axis = new_x_axis.cross(edit_bone.y_axis)
    new_x_axis.normalize()
    dot = max(-1.0, min(1.0, edit_bone.z_axis.dot(new_x_axis)))
    angle = math.acos(dot)
    edit_bone.roll += angle
    dot1 = edit_bone.z_axis.dot(new_x_axis)
    edit_bone.roll -= angle * 2.0
    dot2 = edit_bone.z_axis.dot(new_x_axis)
    if dot1 &gt; dot2:
        edit_bone.roll += angle * 2.0

source = bpy.data.objects['Source']
target = bpy.data.objects['Target']

# Needed to account for object rotation
source_to_world_matrix = source.matrix_world.to_3x3()
world_to_target_matrix = target.matrix_world.inverted().to_3x3()

source_x_axis = {}
bpy.ops.object.select_all(action='DESELECT')
source.select = True
bpy.context.scene.objects.active = source
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.armature.select_all(action='SELECT')

for b in source.data.edit_bones:
    source_x_axis[b.name] = source_to_world_matrix * b.x_axis

bpy.ops.object.mode_set(mode='OBJECT')

bpy.ops.object.select_all(action='DESELECT')
target.select = True
bpy.context.scene.objects.active = target
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.armature.select_all(action='SELECT')

for b in target.data.edit_bones:
    align_bone_x_axis(b, world_to_target_matrix * source_x_axis[b.name])

bpy.ops.object.mode_set(mode='OBJECT')