# Is there an easy way to set the speed in units like km/h for an object travelling along a path?

I've made a scene with a car travelling along a path; is there a way that I can simply set the speed of the vehicle in m/s or km/h?

Manually calculating the distance my object has to travel in a certain time, or setting the correct slope in the graph editor seems like a hassle that shouldn't be necessary.

• Please check my answer again, I derped and the speed wasn't right. – someonewithpc Apr 11 '15 at 18:04

I think I finally have a working solution:

    import bpy, mathutils
from mathutils import Vector

le = 0

#Speed in m/s
targetSpeed = 10
obj_name_original = bpy.context.active_object.name

def getLenght():
ob = bpy.context.active_object
if ob and ob.select and ob.type == 'CURVE':

em = (ob.mode == 'EDIT')
bpy.ops.object.duplicate_move()
bpy.ops.object.mode_set()
bpy.ops.object.transform_apply(scale=True)
bpy.ops.object.convert(target='MESH', keep_original=False)
ve = bpy.context.active_object.data.vertices
global le
dd = le = 0
for i in bpy.context.active_object.data.edges:
dd = ve[i.vertices[0]].co - ve[i.vertices[1]].co
le += dd.length
le = round(le,4)
bpy.ops.object.delete()
print(le)
if em: bpy.ops.object.mode_set(mode='EDIT', toggle=False)

global obj_name_original
bpy.context.scene.objects.active = bpy.context.scene.objects[obj_name_original]
bpy.context.object.select = True

else:
raise Exception("The selected object is not a curve")

targetC = 0

def findC():
global obj_name_original
for ob in bpy.data.objects:
if ob and ob.type == 'MESH':
for c in ob.constraints:
if c.target.name == obj_name_original:
global targetC
targetC = c
#print(c)

def setO(scene):
global targetSpeed
global targetC
global le #scene.render.fps    ((le/targetSpeed))*(scene.frame_current/(scene.frame_end*targetSpeed))
targetC.offset_factor = scene.frame_current/((le/targetSpeed)*scene.render.fps)

getLenght()
findC()

def register():
bpy.app.handlers.frame_change_pre.append(setO)

def unregister():
bpy.app.handlers.frame_change_pre.remove(bpy.app.handlers.frame_change_pre[-1])

if __name__ == "__main__":
register()


The way it works is that it approximates the length of the curve, and then changes the offset of the object following it every frame - essentially animating it. It does require that you select the curve before running the script, otherwise, it will purposely crash.

If you want to use km/h instead of m/s you just need to divide the speed by 3.6, which will convert it to m/s.

• I gather Blender doesn't have the functionality built in, but this script will do. Thanks. – cybrbeast Apr 11 '15 at 8:28
• No, not that I know of. – someonewithpc Apr 11 '15 at 9:25

you must know the total travel time

after that you must configure the parameters of frames per second (ftps) and the path animation parameters as frame and Evaluation time.

that is a simple rule of three

also you can calc the length of a Bezier curve please check: http://blenderartists.org/forum/showthread.php?206790-Length-of-a-Bezier-curve-in-2-5 or you can try with my script that will print the lenght in console:

import bpy, bmesh

for area in bpy.context.screen.areas:
if area.type == 'VIEW_3D':

ob1 = bpy.context.active_object # this is the curve

bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":(0, 0, 0), "constraint_axis":(False, False, False), "constraint_orientation":'GLOBAL', "mirror":False, "proportional":'DISABLED', "proportional_edit_falloff":'SMOOTH', "proportional_size":1, "snap":False, "snap_target":'CLOSEST', "snap_point":(0, 0, 0), "snap_align":False, "snap_normal":(0, 0, 0), "gpencil_strokes":False, "texture_space":False, "remove_on_cancel":False, "release_confirm":False})

bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)

bpy.ops.object.convert(target='MESH')

bpy.ops.object.editmode_toggle()

obj = bpy.context.object
me = obj.data
bm = bmesh.from_edit_mesh(me)

edges= [e for e in bm.edges]

total = 0
for edge in edges:

total = (total + edge.calc_length())

print("total: ",total)

bmesh.update_edit_mesh(me, True)
bpy.ops.object.editmode_toggle()
bpy.ops.object.delete(use_global=False)

ob1.select = True
bpy.context.scene.objects.active = ob1

• And how does that give you the speed in kph or m/s, unless you know the exact length of the curve? For a bezier curve, like the one you've got (looks quadratic to me), the arc length is quite complicated. If you go up to a cubic one, you'll need elliptic integrals. I believe the OP wanted to avoid number crunching. – user7952 Apr 10 '15 at 18:51
• @SixthOfFour I'm update the post.. please check – yhoyo Apr 10 '15 at 19:28
• As far as I can tell, that sums the length of the straight lines between the vertices. A good approximation, as that is likely what and object with a Follow Curve constraint actually follows. +1, but I still feel that the OP has a point: it should be simpler. If you could just set the speed, you could even animate the shape of the curve, if it was properly implemented. – user7952 Apr 10 '15 at 19:38
• @SixthOfFour please check the someonewithpc script.. that is a good aproximation to this solution too – yhoyo Apr 10 '15 at 19:43