How do I copy children of an object using copy() method, without messing up their positions?
Set the matrix_parent_inverse of the copy.
Thought I'd add a supplementary answer, using recursion to get all the children up to a certain level. Without it the code is going to get long and unwieldy for more levels.
import bpy

def copy_ob(ob, parent,  collection=bpy.context.collection):
    # copy ob
    copy = ob.copy()
    copy.parent = parent
    copy.matrix_parent_inverse = ob.matrix_parent_inverse.copy()
    # copy particle settings
    for ps in copy.particle_systems:
        ps.settings = ps.settings.copy()
    collection.objects.link(copy)
    return copy
    
def tree_copy(ob, parent, levels=3):
    def recurse(ob, parent, depth):
        if depth &gt; levels: 
            return
        copy = copy_ob(ob, parent)
        
        for child in ob.children:
            recurse(child, copy, depth + 1)
    recurse(ob, ob.parent, 0)
    
#test
tree_copy(bpy.context.object, None)