Robotics for Creative Practice - Fall 2026

Two-Link Robot Model

«  Arm Theater   ::   Contents   ::   ZYY Robot Arm Model  »

Two-Link Robot Model¶

The two-link robot model simulates a planar two-link robot with an actuator at each joint and a sensor at the end. The model is intended as a demonstration testbed for two-link kinematics.

The link geometry uses only cylinder primitives. The same geometry is referenced to use as bounding objects for collision and automatic calculation of physics parameters. The base object has a NULL Physics object so it does not move, simulating a rigid connection to the ground.

The end sensor is represented by a yellow cone. It is implemented as a DistanceSensor of limited range, pointing outward along the link axis from the end of the second link.

This model is demonstrated in the sensor-demo.wbt and controls-demo.wbt worlds.

../_images/two-link-sim.jpg

Screenshot of Webots model of fully-actuated two-link planar robot.¶

Contents

  • Two-Link Robot Model

    • System Kinematics

    • two-link.proto

    • Sample Control Code

System Kinematics¶

The bodies are as follows:

name

color

notes

base

blue

base object fixed to the ground

link1

red

proximal link, attaches to base at ‘shoulder’

link2

green

the distal link, attaches to link1 at ‘elbow’

The joints are as follows:

name

parent

child

notes

joint1

base

link1

the ‘shoulder’, includes motor1

joint2

link1

link2

the ‘elbow’, includes motor2

The axes are as follows:

name

direction

notes

joint1

along Z

located above the origin

joint2

along Z

located at the end of link1

The motors and sensors are named as follows:

name

notes

motor1

RotationalMotor on joint1

motor2

RotationalMotor on joint2

joint1

PositionSensor on joint1

joint2

PositionSensor on joint2

endRangeSensor

DistanceSensor at end of link2

two-link.proto¶

The robot model has been encapsulated in a .proto file for easy reuse. The model includes user-accessible link length parameters to demonstrate procedural scaling.

  1#VRML_SIM R2023b utf8
  2# documentation url: https://courses.ideate.cmu.edu/16-375
  3# Planar two-link actuated arm for course exercises.  The graphics use only
  4# primitives for clarity of the source. The base has NULL physics so it will be
  5# fixed in place. The two link lengths are adjustable parameters to demonstrate
  6# using procedural elements in the prototype.  The link physics properties are
  7# specified using density so the dynamics will also scale, but the motor
  8# parameters are constant.  The end includes a distance sensor pointed along the
  9# axis.
 10# license: No copyright, 2020-2026 Garth Zeglin.  This file is explicitly placed in the public domain.
 11# template language: javascript
 12
 13PROTO two-link [
 14  field SFVec3f    translation  0 0 0
 15  field SFRotation rotation     0 1 0 0
 16  field SFFloat    link1Length  0.5
 17  field SFFloat    link2Length  0.5
 18  field SFString   controller   "two_link"
 19  field SFString   name         ""
 20  field SFString   customData   ""
 21]
 22{
 23  Robot {
 24    # connect properties to user-visible data fields
 25    translation IS translation
 26    rotation IS rotation
 27    controller IS controller
 28    name IS name
 29    customData IS customData
 30
 31    # calculate derived parameters
 32    %<
 33      let halfLink1Len = fields.link1Length.value / 2;
 34      let halfLink2Len = fields.link2Length.value / 2;
 35    >%
 36
 37    # define the kinematic tree
 38    children [
 39      # add a default radio receiver and transmitter
 40      Receiver {
 41      }
 42      Emitter {
 43      }
 44
 45      # the cylindrical base shape is wrapped in a Transform
 46      # to position it within the robot body coordinates
 47      DEF baseObject Transform {
 48        translation 0 0 0.1
 49        children [
 50          Shape {
 51            appearance PBRAppearance {
 52              baseColor 0.21529 0.543008 0.99855
 53              metalness 0
 54            }
 55            geometry Cylinder {
 56	      height 0.2
 57	      radius 0.2
 58            }
 59          }
 60        ]
 61      }
 62      # define the base pivot joint connecting the base
 63      # and the first link
 64      HingeJoint {
 65        jointParameters HingeJointParameters {
 66          axis 0 0 1
 67        }
 68        device [
 69          PositionSensor {
 70            name "joint1"
 71          }
 72          RotationalMotor {
 73            name "motor1"
 74            acceleration 2
 75            maxVelocity 3.14
 76	    # maxTorque 2
 77	    maxTorque 20
 78          }
 79        ]
 80        # start definition of the first link
 81        endPoint Solid {
 82          # place the shape origin halfway along the first link;
 83          # this vector is in body coordinates, X points along
 84          # the link in the neutral pose
 85          translation %<=halfLink1Len>% 0 0.25
 86          children [
 87            # define the 'elbow' pivot connecting the links
 88            HingeJoint {
 89              jointParameters HingeJointParameters {
 90                axis 0 0 1
 91                # place the elbow joint axis at the end of the first
 92                # link; position is relative to link1 origin
 93                anchor %<= halfLink1Len >% 0 0
 94                dampingConstant 0.1
 95              }
 96              device [
 97                PositionSensor {
 98                  name "joint2"
 99                }
100                RotationalMotor {
101                name "motor2"
102                acceleration 2
103                maxVelocity 6.28
104                # maxTorque 1.5
105		maxTorque 15
106                }
107              ]
108              # define the second link
109              endPoint Solid {
110                # place the link2 origin halfway along the second link
111                translation %<= halfLink1Len+halfLink2Len>% 0 0.1
112                children [
113                  # the cylindrical link shape is wrapped in a Transform
114                  # to position it within the link2 coordinates
115                  DEF link2Shape Transform {
116                    # the Cylinder shape coordinates use Z as the
117                    # long axis; this 90 deg rotation around Y
118                    # places the lengthwise Z axis along the link.
119                    rotation 0 1 0 1.5708
120                    children [
121                      Shape {
122                        appearance DEF greenAppearance PBRAppearance {
123                          baseColor 0.413001 1 0.33489
124                          metalness 0
125                        }
126                        geometry Cylinder {
127                          height IS link2Length
128                          radius 0.05
129                        }
130                      }
131                   ]
132                 } # end link2 Shape
133	     	 # add a visual hub to the base of link2, not part of the bounding object
134	     	 Transform {
135             	    rotation 0 1 0 0
136             	    translation %<= -halfLink2Len>% 0 0
137  	     	 	children [
138	     	 	  Shape {
139             	        appearance USE greenAppearance
140             	        geometry Cylinder {
141	     	 	      height 0.1
142	     	 	      radius 0.05
143	     	 	    }
144	     	 	  }
145	     	 	]
146             	 } # end Transform around link2 base hub
147		 # define a DistanceSensor attached to the second link Solid node
148                 DistanceSensor {
149                   translation %<= halfLink2Len>% 0 0
150                   name "endRangeSensor"
151
152		   # the sensor lookup table implicitly defines the maximum range and the units, each
153		   # entry is [distance, value, noise]
154                   lookupTable [
155                     0 0 0
156                     0.9 0.9 0    # 0.9 meters reads as 0.9 meters
157                   ]
158		   resolution 0.001 # assume millimeter resolution
159                   numberOfRays 5
160                   aperture 0.1
161                   children [
162                     Transform {
163                       rotation 0 -1 0 1.5708
164                       children [
165                         Shape {
166                           appearance PBRAppearance {
167                             baseColor 1 0.99028 0.0584421
168                             roughness 0.5
169                             metalness 0.5
170                             emissiveColor 1 0.99028 0.0584421
171                             emissiveIntensity 0.2
172                           }
173                           geometry Cone {
174                             bottomRadius 0.02
175                             height 0.1
176                           }
177                         }
178                       ]
179                     }
180                   ]
181                 } # end DistanceSensor
182                ] # end link2 Solid children
183                # top-level properties of link2
184                name "link2"
185                boundingObject USE link2Shape
186                physics Physics {
187                  # Assume the link is a thin-walled aluminum tube with 50 mm
188		  # radius and 2 mm wall thickness.  Aluminum has a density of
189		  # 2700 kg/m^3, but this will be scaled by the ratio of the
190		  # tube cross-section to the solid cylinder cross-section
191		  # assumed by the simulator.  Note that the moment of inertia
192		  # around the long axis will be underestimated.
193		  # density = 2700 * (R_outer**2 - R_inner**2) / R_outer**2
194                  density 211.7
195                  mass -1
196                }
197              }
198            }
199            # finish the definition of link1 with a shape
200            # node in the 'children' list
201            DEF link1Shape Transform {
202              rotation 0 1 0 1.5708
203              children [
204                Shape {
205                  appearance DEF redAppearance PBRAppearance {
206                     baseColor 00.990494 0.516915 0.468254
207                     metalness 0
208                  }
209                  geometry Cylinder {
210                    height IS link1Length
211                    radius 0.05
212                  }
213                }
214               ]
215             }
216	     # add a visual hub to the base of link1, not part of the bounding object
217	     Transform {
218                rotation 1 0 0 0
219                translation %<= -halfLink1Len>% 0 0
220  		children [
221		  Shape {
222                    appearance USE redAppearance
223                    geometry Cylinder {
224		      height 0.1
225		      radius 0.05
226		    }
227		  }
228		]
229             } # end Transform around link1 base hub
230	     # add a visual hub to the end of link1, not part of the bounding object
231	     Transform {
232                rotation 1 0 0 0
233                translation %<= halfLink1Len>% 0 0
234  		children [
235		  Shape {
236                    appearance USE redAppearance
237                    geometry Cylinder {
238		      height 0.1
239		      radius 0.05
240		    }
241		  }
242		]
243             } # end Transform around link1 end hub
244          ] # close the children list of the link1 node
245          # top-level properties of link1
246          name "link1"
247          boundingObject USE link1Shape
248          physics Physics {
249	    # See notes for link2 density; this assumes the same geometry.
250            density 211.7
251            mass -1
252          }
253        }
254      }
255    ] # close the children list of the base node
256    # define top-level properties of the base
257    boundingObject USE baseObject
258
259    # the base of the robot itself has NULL physics to simulate being fixed to the ground
260    # physics Physics { density -1 mass 10  }
261
262  } # close the Robot definition
263}

Sample Control Code¶

 1# two_link.py
 2#
 3# Sample Webots controller file for driving the two-link arm
 4# with two driven joints.  This example simulates a passive
 5# distal link by applying zero torque, then moves the
 6# base joint in a periodic excitation.
 7
 8# No copyright, 2020-2021, Garth Zeglin.  This file is
 9# explicitly placed in the public domain.
10
11print("loading two_link.py...")
12
13# Import the Webots simulator API.
14from controller import Robot
15
16# Define the time step in milliseconds between controller updates.
17EVENT_LOOP_DT = 200
18
19# Request a proxy object representing the robot to control.
20robot = Robot()
21robot_name = robot.getName()
22print("%s: controller connected." % (robot_name))
23
24# Fetch handle for the 'base' and 'elbow' joint motors.
25j1 = robot.getDevice('motor1')
26j2 = robot.getDevice('motor2')
27
28# Configure the motor for velocity control by setting
29# the position targets to infinity.
30j1.setPosition(float('inf'))
31
32# Start out with a 3 radian/second target rotational
33# velocity (roughly 180 deg/sec).
34j1.setVelocity(3)
35
36# Configure the second motor to freewheel.  Please note
37# this does not turn off the hinge friction.  For reference see:
38#  https://cyberbotics.com/doc/reference/motor
39#  https://cyberbotics.com/doc/reference/rotationalmotor
40j2.setTorque(0.0)
41
42# Run loop to execute a periodic script until the simulation quits.
43# If the controller returns -1, the simulator is quitting.
44while robot.step(EVENT_LOOP_DT) != -1:
45    # Read simulator clock time.
46    t = robot.getTime()
47
48    # Change the target velocity in a cycle with a two-second period.
49    if int(t) % 2 == 0:
50        j1.setVelocity(0)
51    else:
52        j1.setVelocity(3)
53        

«  Arm Theater   ::   Contents   ::   ZYY Robot Arm Model  »

This is a work of satirical fiction. Any resemblance to actual art or technology is purely coincidental.

© Copyright 2026, Garth Zeglin. Licensed under CC-BY-4.0. Last updated on 2026-09-17. Created using Sphinx 8.2.3. University legal notice.