# Driving contol surfaces.

Just testing animating the control surfaces of an airplane. They respond to the rotations of the airplane. In order to make the movement of the plane lag slightly behind the movement of the control surfaces, I baked their actions to create f-curves, then shifted those curves to the left about 12 frames. The drawback is that if you change the animation of the plane, you need to bake new actions. Does anyone know if it’s possible to do this using strictly drivers?

Here’s a still from a camera-tracked animation I’m working on…

Steve S

Wow this is amazing would love to learn this :yes:

It’s not difficult. I created an Empty for each control surface. Those were parented to a main Empty for the rest of the plane. Parent the control surfaces to their respective Empty.

Then select an Empty (such as the right aeleron) and place the cursor over the appropriate axis of rotation in the Object:Transform panel. Right click and select Add Single Driver. The slot for the X rotation should change color. Also, lock out all but the X axis in the Transform Locks panel. You’ll also want to give this Empty a Limit Rotation constraint to limit the amount of deflection. I chose 25 degrees, but I’m not sure what the real value is for a Spitfire.

Now go into Curves editor and change the view from curves to drivers. Click on the name of the driver at upper left and the driver panels should appear along the right.
Change “Type” to Sum Values. Also choose “Transform Channel.” In the Ob/Bone box, select the main empty for the airplane. Under “Type,” you need to choose the axis which rolls the main Empty left and right (in this case the ‘Y’ axis.) Under Space choose Transform Space. In the Expanded Polynomial, leave the Poly Order at 1, and leave the first Y value at zero.
The next number is one by default, which means that for every degree the plane rolls, the aeleron will move one degree. If you change it to two, then the aeleron will move two degrees for each degree the plane rolls. I chose three just to exaggerate the movement for the video.
Also, using a negative number makes the aeleron move in the opposite direction. One aeleron will need to be positive and one negative, since they move in opposite directions. The elevator and rudder can probably be left positive.

As I mentioned, I wanted the motion of the control surfaces to precede the movement of the plane. In the 3D view, select an Empty for one of the control surfaces, hit the spacebar to bring up the search box and type in Bake Action. The default settings should do, so hit OK. It will create f-curves for the surface.
Go back into the curves editor and deactivate the driver by clicking on the Speaker icon at upper left.
Select the f-curve for the appropriate axis and shift it over to the left about 12 to 15 frames.

Steve S

baking a driver and shifting it forward is probably the safest and most straightforward way to have the driven motion precede the keyframed motion, but you can script it entirely within the driver’s scripted expression if you can handle a little python

ex:
bpy.data.objects[‘KEYFRAMED_OBJECT’].animation_data.action.fcurves[INDEX_OF_FCURVE].evaluate(bpy.context.scene.frame_current+OFFSET)

where:
-KEYFRAMED_OBJECT is the name of the object who’s keyframe data you want to use as the input to your driver
-INDEX_OF_FCURVE is the numerical index of which fcurve of the keyframed object you want to use. i don’t know if there’s any easier way to access a specific fcurve because obviously array indices aren’t exactly self evident. although from my quick tests, it appears that the fcurve editor orders the curves by index, so the first one in the list is index 0, the second one is index 1, and so on.
-OFFSET is the number of frames you want your driven animation to be offset. a positive number will evaluate the keyframed object’s future state, and a negative number will evaluate a past state.

obviously this expression will simply give you a 1:1 mapping of the input animation channel, but you can from there do stuff like multiply or divide to remap it to a different output range.

good luck!

update: i’ve attached an example video and drivenControls.blend (525 KB)

Thanks for the input, shteeve. I figured it could probably be done with Python, but unfortunately my knowledge of it is zilch. I’ve updated the description on the Youtube page with your script.

Steve S

I’ve found one drawback to this technique. If you’re having the plane fly along a Path, the path takes care of the pitch and yaw, so the plane has zero pitch and yaw in terms of local space, thus the rudder and elevator don’t move. You can still use it for the aelerons since you’ll need to animate the roll axis yourself.

Steve S

I just tried this and didn’t run into that problem.

Here are two cubes, one driven by the local x-rotation (pitch) and the other by the local z-rotation (yaw). Both of them did indeed respond to the local motion of the plane following the curve, and I even baked the action of the Z cube (neat, I had never seen that option before.)

Does this work for you, or is this not your problem?

LocalSpaceRoll.blend (1.09 MB)

ah, if the animation is being driven by a follow curve constraint, you can actually avoid the heavy python stuff altogether. you create a null that also follows the curve, and use a driven expression so that the null copies the airplane’s curve offset, and add an additional amount so that it will lead the plane. you can then use that null like you’d been using the plane originally to gather the orientation data to drive the flaps.

Edit: as a bonus, you can include the roll of the plane in the follow curve by editing the curve’s roll value at various control points.

Here’s a quick demo following a path. as i was doing the yaw driver, i actually realized that the best way to get flap orientations is to calculate the rotational difference between the plane and its leading empty, then you are actually getting a more meaningful delta value (turning relative to where it was several frames earlier, vs being rotated relative to some static value)
drivenControls_v2.blend (539 KB)