Advertisement

Create a Robotic Arm Using Forward Kinematics

by

Forward Kinematics (FK) is a positioning algorithm. It's not as complicated as it sounds if you associate it to sequence of actions from an excavator operator's point of view. Beginning with the node closest to it, the operator configures the orientation of each node in the hope that the scoop will reach the location intended. Often, several attempts are made before achieving success. FK is an algorithm to perform such task. In this tutorial, we shall attempt the implementation of FK and apply it onto an excavator from a 2D side view. Having understood this algorithm, one can easily adapt it to a 3D environment. Enjoy!


Step 1: Tutorial Flow

This tutorial will assume the following sequence:

  • Explanation for FK's Mathematical background.
  • Hard-coded mplementation will be done in a file according to sequential flow.
  • Next, object-oriented class developed to implement FK algorithm.
  • Demonstration of FK class used in a project.
  • Graphics applied to the demonstration.

Step 2: Chain

First of all, we must understand the concept of a chain. A chain abstracts the hierarchical order of nodes. There is a root node, the highest order of the chain, where all other nodes are joined to. Node that connects to the root is the first level child. Node that connects to first level child is the second level child.

The hierarchical relationship is simplified below:

root > node1 > node2 > node3 > node4 > node5 ...

The flash animation above shows the parent (higher in hierarchy) to child (lower in hierarchy) relationships, where each arrow points to a child node.


Step 3: Arrows

Each of these nodes are connected to adjacent ones by a constraint. I shall refer these constraints as "Arrows". Arrow are vectors. They have a length (better known as magnitude), and an angle that describes its orientation. In this tutorial, these arrows point from parent node to child node. For example, node1 is joined to root by arrow1. Arrow1 is pointing from root to node1.

Arrows follow a hierarchical order as well because they attempt to join a hierarchical chain of nodes. This heirarchical relationship is simplified as below:

arrow1 > arrow2 > arrow3 > arrow4 > arrow5 ...

So where's arrow0? Well, root is the highest in hierarchy. No arrow points to it so there's effectively no arrow0.



Step 4: Delta Rotation

Adjusting the rotation of arrows is the most important thing in the FK algorithm. Here, we shall rotate an arrow by delta angle. If we rotate arrow1, node1's position will update. But that's not all.

In FK algorithm, kinematics traverse from highest to lowest in hierarchy. Let us assume our chain has 6 nodes, joined by 5 arrows. If delta rotation is applied to arrow1, then arrow2 to arrow5 need to be rotated by delta angle as well. I have bolded the arrows that are affected by the change.

arrow1 > arrow2 > arrow3 > arrow4 > arrow5

If delta rotation is applied onto arrow3, then only arrow4 and arrow5 are affected.

arrow1 > arrow2 > arrow3 > arrow4 > arrow5

For each affected arrow, its associated node's position will also be affected. So node4 and node5 will update their positions.

In the above SWF, click the arrows to see the kinematics ripple down the nodes (darker arrows).


Step 5: Start Project

Lauch FlashDevelop and start a new Project. Add a "MileStone1.as" into your project.






Step 6: A Chain of Two Nodes: Two Graphics

Now to implement our first FK chain. First, we need to add the two sprites onto the stage as class variables. The root node is a blue circle and the first node is a purple circle.

 
rootNode = new Sprite(); 
rootNode.graphics.beginFill(0x4499FF); 
rootNode.graphics.drawCircle(0, 0, 20); 
rootNode.graphics.endFill(); 
addChild(rootNode); 
 
node1 = new Sprite(); 
node1.graphics.beginFill(0x772255); 
node1.graphics.drawCircle(0, 0, 20); 
node1.graphics.endFill(); 
addChild(node1);

Step 7: A Chain of Two Nodes: Arrow Constraints

node1 is joined to rootNode via vec1, which is an instance of Vector2D class (custom-written class). It is a class variable. We initiate it and set its magnitude to 60.

 
vec1 = new Vector2D(0, 0); 
vec1.setMagnitude(60);

Step 8: A Chain of Two Nodes: Positioning

Before we start assigning listeners to add interactivity to our little program, we need to set the initial position of the nodes. First the rootNode, which is pretty obvious. Next is node1 (highlighted) which depends upon rootNode's position.

 
//setting the nodes 
rootNode.x = 150; 
rootNode.y = 150; 
node1.x = rootNode.x + vec1.x; 
node1.y = rootNode.y + vec1.y;

Step 9: A Chain of Two Nodes: Listener

Almost there. We add a listener to keyboard event. On pressing an arbitary key, vec1 rotates and further change the orientation of node1 with respect to rootNode.

 
	stage.addEventListener(KeyboardEvent.KEY_DOWN, move); 
} 
 
private function move(e:KeyboardEvent):void  
{ 
	vec1 = vec1.rotate(Math2.radianOf(10)); 
	node1.x = rootNode.x + vec1.x; 
	node1.y = rootNode.y + vec1.y; 
}

Below is MileStone1 completed. Simple, right?

Click the SWF, then hit any key (repeatedly) to see the effect.


Step 10: A Chain of Three Nodes: Graphics & Arrow

Now that we have got the basic setup, lets push this a little further. We will add node2 (also purple) to the existing chain. node2 is joined to node1 via vec2, which is given a magnitude of 60

 
//a second child 
node2 = new Sprite(); 
node2.graphics.beginFill(0x772255); 
node2.graphics.drawCircle(0, 0, 20); 
node2.graphics.endFill(); 
addChild(node2); 
 
vec2 = new Vector2D(0, 0); 
vec2.setMagnitude(60);

Step 11: A Chain of Three Nodes: Positioning

We initialise the position of node2 in relation to node1 using vec2 as a constraint using the following code. Remember to also include the same piece of code into the keyboard listener (line 64 of MileStone2).

 
node2.x = node1.x + vec2.x; 
node2.y = node1.y + vec2.y;

Here's the MileStone2 completed:

Again, use the keyboard to see the effect.


Step 12: A Chain of Three Nodes: Uh-oh

Hmm... Something is wrong with this FK implementation. See, the kinematic does not traverse down the chain. Recall Step 4: Delta Rotation; kinematics need to ripple down from the current level of hierarchy to the end. I've highlighted the lines of code that do this. To fix the logical error, just paste line 65 to MileStone2. I've supplied MileStone3 just in case you face difficulties.

 
private function move(e:KeyboardEvent):void  
{ 
	vec1 = vec1.rotate(Math2.radianOf(10)); 
	node1.x = rootNode.x + vec1.x; 
	node1.y = rootNode.y + vec1.y; 
	 
	vec2 = vec2.rotate(Math2.radianOf(10)); 
	node2.x = node1.x + vec2.x; 
	node2.y = node1.y + vec2.y; 
}

Step 13: A Chain of Three Nodes: More Controls

Next, lets add flexibility to our control. Currently we control the positioning of node1 and node2. Lets allow control on positioning of node2 only.

 
private function move(e:KeyboardEvent):void  
{ 
	if (e.keyCode == Keyboard.PAGE_UP) { 
		vec1 = vec1.rotate(Math2.radianOf(10)); 
		node1.x = rootNode.x + vec1.x; 
		node1.y = rootNode.y + vec1.y; 
		 
		vec2 = vec2.rotate(Math2.radianOf(10)); 
		node2.x = node1.x + vec2.x; 
		node2.y = node1.y + vec2.y; 
	} 
	 
	else if (e.keyCode == Keyboard.PAGE_DOWN) { 
		vec2 = vec2.rotate(Math2.radianOf(10)); 
		node2.x = node1.x + vec2.x; 
		node2.y = node1.y + vec2.y; 
	} 
}

Use the Page Down and Page Up keys to move the different nodes.

Yes, rotation is still one way-- counter clockwise. The purpose of running through Steps 5 - 13 is to build a solid understanding of the FK algorithm. Now that it's built, let us bring this to another level: designing an FKChain class that allows an easy implementation of FK.


Step 14: FKChain Class: Variables

FKChain was designed to easily implement FK. I have designed the class with the following variables and functions. I will run you through them.

Variables Purpose Data Type
nodes To hold nodes of FK chain Vector array of Sprites
arrows To hold arrows connecting nodes Vector array of Vector2D
lowerLimits To hold lower boundary of allowable angle Vector array of Number
upperLimits To hold upper boundary of allowable angle Vector array of Number
selectedIndex To hold data of current selected node Integer

Step 15: FKChain Class: Data Structure

Association of nodes to arrows along with other constraints is depicted by the following diagram.


Note the association of nodes to arrows, lowerLimits and upperLimits. Angle marked in red is constrained according to the formula in image above.

Note as well that I mentioned in Step 3 that there is no arrow[0]. However if we were to implement boundaries, we will need arrow[0] to be a horizontal vector where angle of arrow[1] is measured from and evaluated by the constraints.


Step 16: FKChain Class: Functions

Now that you understand the bolts and nuts of FKChain class, we move on to define functions of FKChain.

Functions Input/ Data Type Purpose
FKChain() root node/ Sprite Initiates class variables. Sets the root node into FK chain along
with relevant constraints. Sets selectedNode to root node.
appendNode() next child node/ Sprite Append new node into chain along with relevant constraints. Sets
current selected. Sets selectedNode to new node.
removeNode() node to remove/ Sprite Remove input node from chain along with relevant constraints.
selectedNode() node to manipulate/ Sprite Set current selected node to perform manipulations like adjusting
its arrow (magnitude and angle) and limitations on the arrow angle.
updateLimits() lower, upper bounds/ Number Sets new limitations (upper and lower boundary) on  the arrow's
angle of selected node.
alterMagnitude() magnitude/ Number Sets new magnitude on the selected node's arrow.
alterAngle() angle to adjust to/ Number Inputs delta angle to selected node's arrow.
setPosition() (none) Ripple down kinematics from selected node's hierarchy to the end of the
chain.

Step 17: FKChain Class: Properties

I also introduced a few getters to facilitate development. They are used when scrolling through nodes.

Property Purpose
currentNode Returns current selected node.
child Returns the child of selected node.
parent Returns the parent of selected node.

Step 18: Using FKChain

Now we've got an overview of FKChain, let's have a look at its application and internal workings. In your project, create a class TestChain. We shall call upon a draw method to draw all required Sprites on our stage. There are altogether five sprites and a bracket to indicate current selected node.

 
public function draw():void 
{ 
	b0 = new Sprite(); 
	b0.graphics.beginFill(0); 
	b0.graphics.drawCircle(0, 0, 15); 
	b0.graphics.endFill(); 
	addChild(b0); 
	 
	b1 = new Sprite(); 
	b1.graphics.beginFill(0); 
	b1.graphics.drawRect(-80, -10, 80, 20); 
	b1.graphics.endFill(); 
	addChild(b1); 
	 
	b2 = new Sprite(); 
	b2.graphics.beginFill(0); 
	b2.graphics.drawRect(-40, -10, 40, 20); 
	b2.graphics.endFill(); 
	addChild(b2); 
	 
	b3 = new Sprite(); 
	b3.graphics.beginFill(0); 
	b3.graphics.drawRect(-40, -10, 40, 20); 
	b3.graphics.endFill(); 
	addChild(b3); 
	 
	b4 = new Sprite(); 
	b4.graphics.beginFill(0); 
	b4.graphics.drawRect(-40, -10, 40, 20); 
	b4.graphics.endFill(); 
	addChild(b4); 
	 
	target = new Sprite(); 
	target.graphics.lineStyle(3, 0xFF0000); 
	target.graphics.moveTo( -15, -10); 
	target.graphics.lineTo( -15, -15); 
	target.graphics.lineTo( -10, -15); 
	 
	target.graphics.moveTo( 15, -10); 
	target.graphics.lineTo( 15, -15); 
	target.graphics.lineTo( 10, -15); 
	 
	target.graphics.moveTo( 15, 10); 
	target.graphics.lineTo( 15, 15); 
	target.graphics.lineTo( 10, 15); 
	 
	target.graphics.moveTo( -15, 10); 
	target.graphics.lineTo( -15, 15); 
	target.graphics.lineTo( -10, 15); 
	addChild(target); 
}

Step 19: Initiate FKChain

 
public function initChain():void 
{ 
	c = new FKChain(b0); 
	 
	c.appendNode(b1); 
	c.alterMagnitude(120); 
	c.updateLimits(Math2.radianOf( -90), Math2.radianOf(45)); 
	 
	c.appendNode(b2); 
	c.updateLimits(Math2.radianOf( -45), Math2.radianOf(90)); 
	 
	c.appendNode(b3); 
	c.updateLimits(Math2.radianOf(0), Math2.radianOf(90)); 
	 
	c.appendNode(b4); 
	 
	//update all node's position 
	b0.x = 250; 
	b0.y = 300; 
	c.selectNode(b1); 
	c.setPosition(); 
	 
	//Place target onto root node initially 
	target.x = c.currentNode.x; 
	target.y = c.currentNode.y; 
}

First, we must instantiate the FKChain class by calling its constructor. Supply a root node into constructor. A further examination into the constructor function shows two main operations of the function. First is the instantiation of nodes, arrows, upperLimits and lowerLimits. Data is supplied into these variables thereafter.

Note that root node has an associated arrow. I did not explicitly show that arrows are always one member less than nodes. However, accept it for now as I explain on its use in later sections. The other highlighted lines are just fillers to ensure an easy addressing on constraints. Finally setting the currently-selected node to root for further manipulation.

 
public function FKChain(node0:Sprite)  
{ 
	//instantiate variables 
	nodes = new Vector.<Sprite>; 
	arrows = new Vector.<Vector2D>; 
	upperLimits = new Vector.<Number>; 
	lowerLimits = new Vector.<Number>; 
	 
	//introduce root node 
	nodes.push(node0); 
	arrows.push(new Vector2D(1, 0)); 
	lowerLimits.push(0); 
	upperLimits.push(0); 
	selectNode(node0); 
}

Step 20: Appending Nodes to FKChain

After initiation, we can start appending nodes to the instance of FKChain. The arrow's creation is abstracted from the users. I have highlighted the initial magnitude and limitation on angle of bend (to within -90 < angle < 90). These constraints are easily customised.

 
public function appendNode(node:Sprite):void 
{ 
	nodes.push(node); 
	 
	var newArrow:Vector2D = new Vector2D(0, 0); 
	newArrow.setMagnitude(60); 
	 
	arrows.push(newArrow); 
	lowerLimits.push(Math2.radianOf(-90)); 
	upperLimits.push(Math2.radianOf(90)); 
	selectNode(node); 
}

Step 21: Repositioning Nodes in FKChain

 
//update all node's position 
b0.x = 250; 
b0.y = 300; 
c.selectNode(b0); 
c.setPosition(); 
	 
//Place target onto root node initially 
target.x = c.currentNode.x; 
target.y = c.currentNode.y;

Now the root node's position is at the top-left corner of stage. We shall move the location of b1 close to the center. Then we need to reposition the other nodes in chain and set targeting bracket to the currently selected node. Let us examine further setPosition:

 
public function setPosition():void 
{ 
	var index:int = Math.max(1, selectedIndex); 
	 
	for (var i:int = index; i < nodes.length; i++)  
	{ 
		//set position 
		nodes[i].x = arrows[i].x + nodes[i - 1].x; 
		nodes[i].y = arrows[i].y + nodes[i - 1].y; 
		 
		//set orientation 
		nodes[i].rotation = Math2.degreeOf(arrows[i].getAngle()); 
	} 
}

This function attempts to reposition and reorient all nodes starting from currently selected node. I've set a cap on index (highlighted) because logically, as we perform relative repositioning, root node is not affected by any parent. The node highest in hierarchy to perform relative repositioning is b1. It doesn't matter whether you write c.selectNode(b0) or c.selectNode(b1) before c.setPosition() although it will affect the initial position of trageting bracket..


Step 22: Animating FKChain

 
public function TestChain()  
{ 
	this.draw(); 
	this.initChain(); 
	 
	stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown); 
}

Now its time to animate the chain. We shall assign a listener to keyboard event.

 
private function keyDown(e:KeyboardEvent):void  
{ 
	//scroll through node selection 
	if (e.keyCode == Keyboard.SPACE) 
	{ 
		if (c.currentNode == b4) 
		{ 
			c.selectNode(b1); 
		} 
		else 
		{ 
			c.selectNode(c.child); 
		} 
	} 
	 
	else if (e.keyCode == Keyboard.UP) 
	{ 
		c.alterAngle(Math2.radianOf( -1 * step)); 
		c.setPosition(); 
	} 
	else if (e.keyCode == Keyboard.DOWN) 
	{ 
		c.alterAngle(Math2.radianOf(step)); 
		c.setPosition(); 
	} 
	 
	target.x = c.currentNode.x; 
	target.y = c.currentNode.y; 
}

On pressing the space key, the targeting bracket will scroll through all nodes - except root node, b0. Pressing the Up arrow key will give a negative delta angle, -1 * step; while the Down arrow key will give a positive delta angle, step.

Here's a sample of TestChain.

Use the space bar and the Up and Down arrow keys to control it. Note how the constraints affect how far each node can "bend".


Step 23: Angle Constraint in FKChain

We used alterAngle() in the listener. Now lets dissect the internal workings of it.

 
public function alterAngle(ang:Number):void 
{ 
	var index:int = Math.max(1, selectedIndex); 
	 
	//calc deviation between previous arrow and current arrow 
	var deviation:Number = arrows[index - 1].angleBetween(arrows[index]); 
	var future:Number = deviation + ang; 
	 
	//ensure ang within defined limits 
	if (future > upperLimits[index]) 
	{ 
		ang = upperLimits[index] - deviation; 
		 
	} 
	else if (future < lowerLimits[index]) 
	{ 
		ang = lowerLimits[index] - deviation; 
	} 
	 
	//update relevant arrows 
	for (var i:int = selectedIndex; i < arrows.length; i++)  
	{ 
		arrows[i] = arrows[i].rotate(ang); 
	} 
}

Deviation of current node's arrow from that of its parent is calculated, this is its current angle. Input angle is added to current angle to form future angle. All is well and this value should be output. However, we want to implement a minimum and maximum boundary so future angle will be checked against these values and adjusted accordingly. After that we output the value to update all relevant arrows.


Step 24: Enhancing Choppy Animation

You may notice the animation is choppy. You press the key, and it nudges a bit. We can enhance this a little by putting in acceleration and deceleration of angular velocity. But it will require some rewiring of event handlers. Note that the draw() and initChain() functions are carried forward from previous steps.

 
public function TestChain2()  
{ 
	this.draw(); 
	this.initChain(); 
	 
	stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown); 
	stage.addEventListener(KeyboardEvent.KEY_UP, keyUp); 
	stage.addEventListener(Event.ENTER_FRAME, animate); 
}

Step 25: Keyboard Event Handlers

On pressing directional keys, flags will be toggled true. On releasing these keys, their associated flags will be toggled false. These flags will be captured and evaluated for further operations by an onEnterFrame event handler.

 
private function keyDown(e:KeyboardEvent):void  
{ 
	//scroll through node selection 
	if (e.keyCode == Keyboard.SPACE) 
	{ 
		if (c.currentNode == b4) 
		{ 
			c.selectNode(b0); 
		} 
		else 
		{ 
			c.selectNode(c.child); 
		} 
	} 
	 
	else if (e.keyCode == Keyboard.UP) 
	{ 
		upFlag = true; 
	} 
	else if (e.keyCode == Keyboard.DOWN) 
	{ 
		downFlag = true; 
	} 
	else if (e.keyCode == Keyboard.LEFT) 
	{ 
		leftFlag = true; 
	} 
	else if (e.keyCode == Keyboard.RIGHT) 
	{ 
		rightFlag = true; 
	} 
} 
 
private function keyUp(e:KeyboardEvent):void  
{ 
	if (e.keyCode == Keyboard.UP) 
	{ 
		upFlag = false; 
	} 
	else if (e.keyCode == Keyboard.DOWN) 
	{ 
		downFlag = false; 
	} 
	else if (e.keyCode == Keyboard.LEFT) 
	{ 
		leftFlag = false; 
	} 
	else if (e.keyCode == Keyboard.RIGHT) 
	{ 
		rightFlag = false; 
	} 
}

Step 26: Kinematics Variables

Below are the variables used for animating kinematics:

 
private var angVelo:Number = 0;		//current angular velocity 
private var maxVelo:Number = 5;		//setting a cap on angular velocity 
private var angAcc:Number = 0.2;	//incremental step on angular velocity 
private var angDec:Number = 0.8;	//decay multiplier on angular velocity 
private var step:Number = 5;		//directional steps when moving the whole chain

Step 27: EnterFrame Event Handler

By capturing flags from keyboard event, we can further manipulate kinematics of the chain.

 
private function animate(e:Event):void  
{ 
	//moving the whole chain 
	if (c.currentNode == b0) { 
		if (upFlag) { 
			b0.y -= step 
		} 
		else  if (downFlag) { 
			b0.y += step 
		} 
		if (leftFlag) { 
			b0.x -= step 
		} 
		else if (rightFlag) { 
			b0.x += step 
		} 
	} 
 
	//adjusting angle 
	else { 
		if (upFlag) { 
			angVelo -= angAcc; 
			 
			//setting cap on angular velocity 
			angVelo = Math.max(-1*maxVelo, angVelo); 
		} 
		else if (downFlag) { 
			angVelo += angAcc; 
			 
			//setting cap on angular velocity 
			angVelo = Math.min(maxVelo, angVelo); 
		} 
		else { 
			//decelerate when keys are released 
			angVelo *= angDec 
		} 
		 
		//set value to selected node&#39;s arrow. 
		c.alterAngle(Math2.radianOf(angVelo)); 
	} 
	 
	//refresh position 
	c.setPosition(); 
	target.x = c.currentNode.x; 
	target.y = c.currentNode.y; 
}

Note that there's a slightly different handling of the root node. As it is being selected, we will move the whole chain up, down, left or right. While other children are being selected we pump into angular velocity, angVelo, up or down within the minimum and maximum velocity. If keys are released (hence, all flags are turned false), the current velocity decelerates. To further clarify this snippet above, do read my post on linear kinematics to get an idea of the above code.

An example of the finished work is shown below:

Again, use the space bar and Up and Down arrow keys; this time, note how smooth the motion is.


Step 28: Preparing Assets in Flash IDE

Now that the ground work for code base is done, we shall focus on cosmetic works on our project. I'm not a very good artist so do bear with my excavator drawing.

The graphics I drew are converted into MovieClips. One thing to bear in mind is that the registration point of graphics should be positioned towards the end of segment for the arms and scoop. I've highlighted the registration points with a red circle for all my assets in the screenshots below.





I have named these MovieClip above Root, Arm1, Arm2, and Scoop respectively

Another detail is that you need to press Ctrl + L to pop the library panel and export these assets for Actionscript. Right-click, select Linkage, and fill in the details as shown in the images below.




Step 29: Exporting Assets from Flash IDE

Once all the graphical assets are done, publish them in SWC format to be properly imported to FlashDevelop. Im using Flash CS3. Press Ctrl + Shift + F12 to bring up the Publish Settings. Select the Flash tab and turn on Export SWC. Then hit Publish.



Step 30: Importing Assets into FlashDevelop

Copy the new file (.swc) into the lib folder of your project.


Now that you have it inside your project, you still need to include into library. Right click on your .swc file and select "Add To Library".



Step 31: Check the Assets

Expand the list as shown in image below to check the names of variables. I've used the following names Arm1, Arm2, Root and Scoop. These are the class names associated with those graphics. We will use them later.



Step 32: Replacing Graphics with Assets

Finally we get to the last leg of this tutorial. We shall replace the existing draw() method with this new one, drawAssets().

 
private function drawAssets():void  
{ 
	rootNode = new Root(); 
	arm1 = new Arm1(); 
	arm2 = new Arm2(); 
	scoop = new Scoop(); 
	 
	addChild(arm1); 
	addChild(rootNode); 
	addChild(scoop); 
	addChild(arm2); 
	 
	target = new Sprite(); 
	target.graphics.lineStyle(3, 0xFF0000); 
	target.graphics.moveTo( -15, -10); 
	target.graphics.lineTo( -15, -15); 
	target.graphics.lineTo( -10, -15); 
	 
	target.graphics.moveTo( 15, -10); 
	target.graphics.lineTo( 15, -15); 
	target.graphics.lineTo( 10, -15); 
	 
	target.graphics.moveTo( 15, 10); 
	target.graphics.lineTo( 15, 15); 
	target.graphics.lineTo( 10, 15); 
	 
	target.graphics.moveTo( -15, 10); 
	target.graphics.lineTo( -15, 15); 
	target.graphics.lineTo( -10, 15); 
	addChild(target); 
}

Step 33: Adjust the Asset Constraints

Right now if you launch your application, you may see a cluster of drawings in the top left corner, or the whole excavator arm and body aligned in a funny manner. Adjust the magnitude constraints and the angular limitations on joins appropriately. I've included mine as below.

 
private function initChain():void  
{ 
	c = new FKChain(rootNode); 
	c.appendNode(arm1); 
	c.alterMagnitude(260); 
	c.updateLimits(Math2.radianOf( -45), Math2.radianOf(45)); 
	 
	c.appendNode(arm2); 
	c.alterMagnitude(100); 
	c.updateLimits(Math2.radianOf( 0), Math2.radianOf(90)); 
	 
	c.appendNode(scoop); 
	c.alterMagnitude(60); 
	c.updateLimits(Math2.radianOf( 45), Math2.radianOf(135)); 
	 
	//update all node's position 
	rootNode.x = 250; 
	rootNode.y = 300; 
	c.selectNode(rootNode); 
	c.setPosition(); 
	 
	//Place target onto root node initially 
	target.x = c.currentNode.x; 
	target.y = c.currentNode.y; 
}

Step 34: Moving Horizontally

Further tweaks to keyboard control of the root node, I've eliminated the vertical movement of the whole chain so that the excavator (root) only moves in horizontal manner. The snippet below is placed into the Event.ENTER_FRAME handler

 
if (c.currentNode == rootNode) { 
	if (leftFlag) { 
		rootNode.x -= step 
	} 
	else if (rightFlag) { 
		rootNode.x += step 
	} 
}

Step 35: Play with the Arm

Every single adjustment to the code base is done. Of course, you may customise it however you want. For now, scroll through the nodes using the Space key. For each node, play with the directional keys to see the excavator moving up and down.


Conclusion

That's all for this time around. I hope the class file I've put together helps to ease your forward kinematics somewhere in your simulation and games. Thanks for reading and drop some comments if you found a flaw in anything.

Advertisement
Related Posts
  • Code
    Mobile Development
    Create a Puzzle Game for Android with the Dolby Audio APIPreview image@2x
    In this tutorial, I will show you how to use the Dolby Audio API to enhance the sound in your Android applications. To show you how to use the Dolby Audio API, we will create a simple yet fun puzzle game.Read More…
  • Code
    Mobile Development
    C++ Succinctly: Functions and ClassesPreview image@2x
    In this article, we discuss functions and classes in C++. You'll learn the difference between declaration and definition and we'll take a look at inheritance, abstract classes, and precompiled header files.Read More…
  • 3D & Motion Graphics
    Rigging
    Game Character Creation Series: Kila Chapter 6 - Basic Character RiggingKila pt6 new retina
    Adding a skeleton to a model is a great way to make a once static pile of polygons more dynamic. As the skeleton moves and rotates the model deforms with it, giving the illusion of life. This movement can then be exported and used in any number of game engines.Read More…
  • Code
    Corona SDK
    Corona SDK: Create a Shooter GameCoronasdk bullets preview@2x
    In this tutorial, I'll show you how to create a shooter game with limited bullets with the Corona SDK. The objective of the game is to shoot a high amount of targets with only five bullets. During this tutorial, you'll work with timers, touch controls, and physics. To learn more, read on!Read More…
  • Code
    Animation
    Learn About Linear KinematicsPreview
    Picturing animation in terms of vectors is intuitive, but understanding vector mathematics is a pain. In this tutorial, I hope to ease that pain and provide a solution to animation problems using a custom written Vector2D class. We will look at some fundamental concepts of linear kinematics in the Eulerian approach: displacement, velocity and acceleration. Then, we'll build a simple application with it.Read More…
  • Code
    ActionScript
    Create a Mechanical Snake With Inverse KinematicsSnake
    Imagine a chain of particles animating in symphony together: A train moving as all attached compartments follow suit; a puppet dancing as its master pulls its string; even your arms, when your parents hold your hands as they lead you in an evening walk. Movevment ripples down from the last node to the origin, abiding to constraints as it goes. This is inverse kinematics (IK), a mathematical algorithm that calculates necessary motions. Here, we'll use it to create a snake that's a little more advanced than the one from Nokia games.Read More…