Avoid obstacles overlap with BezierCurves
Raycast into the scene

As a proof of concept have put together a &quot;dodgy&quot; artillery trajectory.

Define the two end points in scene
Target from point to point and shoot a ray into scene
If it hits an object, raise elevation by one degree and try again
If it does not hit, using current aim vector,  extend the right handle of the source knot to where it intersects the plane defined by target point and source to target normal.
The incoming handle is lazily set to vertical.

Note haven't put any checks and balances in to test if there is no possible LOS trajectory A to B.
To improve could use same method going other way.  Have found only the trajectory on the vertical plane.  Spinning the aim around point to point axis could be considered to find shortest two point bezier path that misses object obstacles between two points.  Another way to put this would be we are looking at pitch, could also consider roll, moving our aim around ever increasing concentric circles until a miss
Test code.
from bpy import ops, context, data
from mathutils import Vector, Matrix
from mathutils.geometry import intersect_line_plane
from math import radians

scene = context.scene
pt0 = Vector((-5, 0, 0))
pt1 = Vector((5, 0, 0))
up = Vector((0, 0, 1))
t = d = (pt1 - pt0).normalized()
axis = d.cross(up)
d_elev = radians(1)

hit = scene.ray_cast(
        context.view_layer, 
        pt0,
        d,
        )[0]

R = Matrix.Rotation(d_elev, 3, axis)
while hit:
    t = R @ t
    
    hit = scene.ray_cast(
            context.view_layer, 
            pt0,
            t,
            
            )[0]

t = R @ t # rotate one more time
pt2 = intersect_line_plane(
    pt0,
    pt0 + 1000 * t,
    pt1,
    d,
    )

ops.curve.primitive_bezier_curve_add()
curve = context.active_object
curve.name = 'Trajectory'
bez_points = curve.data.splines[0].bezier_points        

bez_points[0].co = pt0
bez_points[0].handle_left = pt0 - t
bez_points[0].handle_right = pt2

bez_points[1].co = pt1
bez_points[1].handle_left = pt2
bez_points[1].handle_right = pt1

Added one more rotation after miss. Raycast can be a little dodgy if it hits an edge.

Some test results of moving two cubes, active curve is last result with cubes in current location.
As mentioned this is simply a proof of concept of manipulating the handle of a bezier curve to emulate lobbing over an obstacle in LOS changing degree of elevation.
Doing this using points of a right angled triangle with right angle at destination point.
To make this a &quot;shortest&quot; curve that misses obstacles would require more bezier points or investigating looking from other end moving  the apex of the triangle that is the intersection of handle right of source point and handle left of destination point vectors.