Shutterbox

This sample project includes two 1-DOF rotors which each have five passive shutters to modulate an internal light source into light and shadow. This model is demonstrated in the shutterbox.wbt world available within the Webots.zip archive.

../_images/shutterbox.png

Screenshot of Webots model of the shutterbox robots.

Keyboard Control Code

The controller implements keyboard input to trigger motor torques. The two machines respond to different keystrokes for simultaneous control.

 1# shutterbox.py
 2#
 3# Sample Webots controller file for driving the
 4# shutterbox device.
 5#
 6# No copyright, 2022, Garth Zeglin.  This file is
 7# explicitly placed in the public domain.
 8
 9print("loading shutterbox.py...")
10
11# Import the Webots simulator API.
12from controller import Robot
13from controller import Keyboard
14
15# Import the standard Python math library.
16import math
17
18# Define the time step in milliseconds between
19# controller updates.
20EVENT_LOOP_DT = 20
21
22# Request a proxy object representing the robot to
23# control.
24robot = Robot()
25name = robot.getName()
26print(f"shutterbox.py waking up for {name}...")
27
28# Fetch handle for the 'base' joint motor.
29motor1 = robot.getDevice('motor1')
30
31# Enable computer keyboard input for user control.
32keyboard = Keyboard()
33keyboard.enable(EVENT_LOOP_DT)
34
35# Run an event loop until the simulation quits,
36# indicated by the step function returning -1.
37while robot.step(EVENT_LOOP_DT) != -1:
38
39    # Read simulator clock time.
40    t = robot.getTime()
41
42    # Read any computer keyboard keypresses.  Returns -1 or an integer keycode while a key is held down.
43    # This is debounced to detect only changes.
44    key = keyboard.getKey()
45    if key != -1:
46        # convert the integer key number to a lowercase single-character string
47        letter = chr(key).lower()
48
49        # Apply motor torques based on keypresses and robot identity.
50        if name == 'left':
51            if letter == 'a':
52                motor1.setTorque(-1.0)
53            elif letter == 'd':
54                motor1.setTorque(1.0)
55        else:
56            if letter == 'j':
57                motor1.setTorque(-1.0)
58            elif letter == 'l':
59                motor1.setTorque(1.0)
60    else:
61        motor1.setTorque(0.0)

MIDI Control Code

The controller implements MIDI input to trigger motor torques. The two machines respond to different note values for simultaneous control.

 1# shutterbox_midi.py
 2#
 3# Sample Webots controller file for driving the
 4# shutterbox device from MIDI input.
 5#
 6# No copyright, 2022, Garth Zeglin.  This file is
 7# explicitly placed in the public domain.
 8################################################################
 9print("loading shutterbox_osc.py...")
10
11# Import the Webots simulator API.
12from controller import Robot
13
14# Import standard Python libraries.
15import math, queue, platform
16
17# Import the MIDI interface.
18# See https://spotlightkid.github.io/python-rtmidi/
19# and https://pypi.org/project/python-rtmidi/
20import rtmidi
21
22# this should be customized for your particular MIDI controllers
23preferred_MIDI_device = 'IAC'
24# preferred_MIDI_device = 'MPD218'
25
26################################################################
27# Define the time step in milliseconds between
28# controller updates.
29EVENT_LOOP_DT = 20
30
31# Request a proxy object representing the robot to
32# control.
33robot = Robot()
34name = robot.getName()
35print(f"shutterbox_midi.py waking up for {name}...")
36
37# Fetch handle for the 'base' joint motor.
38motor1 = robot.getDevice('motor1')
39
40################################################################
41# Create a thread-safe queue to communicate data to the robot thread.
42messages = queue.Queue()
43
44# Callback function to receive MIDI messages.
45def midi_received(data, unused):
46    msg, delta_time = data
47    print("MIDI message: ", msg)
48    if len(msg) == 3:
49        # process NoteOn and NoteOff on channel 9
50        if msg[0] == 0x99:  # decimal 153, NoteOn for channel 9
51            pad = msg[1] - 35   # pad 1 on the MPD218 is MIDI 36, pad 2 is 37, etc.
52            messages.put((pad, 1))
53        elif msg[0] == 0x89:  # decimal 137, NoteOff for channel 9
54            pad = msg[1] - 35   # pad 1 on the MPD218 is MIDI 36, pad 2 is 37, etc.
55            messages.put((pad, 0))
56
57# Initialize the MIDI input system and read the currently available ports.
58midi_in = rtmidi.MidiIn()
59for idx, midi_name in enumerate(midi_in.get_ports()):
60    if preferred_MIDI_device in midi_name:
61        print("Found preferred MIDI input device %d: %s" % (idx, midi_name))
62        midi_in.open_port(idx)
63        midi_in.set_callback(midi_received)
64        break
65    else:
66        print("Ignoring unselected MIDI device: ", midi_name)
67
68if not midi_in.is_port_open():
69    if platform.system() == 'Windows':
70        print("Virtual MIDI inputs are not currently supported on Windows, see python-rtmidi documentation.")
71    else:
72        print("Creating virtual MIDI input.")
73        midi_in.open_virtual_port(preferred_MIDI_device)
74        if not midi_in.is_port_open():
75            print("Unable to open MIDI device.")
76
77################################################################
78# Run an event loop until the simulation quits,
79# indicated by the step function returning -1.
80while robot.step(EVENT_LOOP_DT) != -1:
81
82    # Read simulator clock time.
83    t = robot.getTime()
84
85    # Check for MIDI messages.
86    if not messages.empty():
87        # each message is a tuple of the form (button index, status)
88        # e.g. (1,1) is button 1 pressed, (1,0) is button 1 released
89        msg = messages.get()
90        print("Main loop received", msg)
91        if ((name == 'left') and (msg[0] == 1)) or ((name == 'right') and (msg[0] == 3)):
92            motor1.setTorque( -1 if msg[1] == 1 else 0)
93                
94        elif (name == 'left' and msg[0] == 2) or (name == 'right' and msg[0] == 4):
95            motor1.setTorque( 1 if msg[1] == 1 else 0)

OSC Control Code

The controller implements OSC input to trigger motor torques. Each machine runs a separate OSC server.

 1# shutterbox_osc.py
 2#
 3# Sample Webots controller file for driving the
 4# shutterbox device from OSC network input.
 5#
 6# No copyright, 2022, Garth Zeglin.  This file is
 7# explicitly placed in the public domain.
 8################################################################
 9print("loading shutterbox_osc.py...")
10
11# Import the Webots simulator API.
12from controller import Robot
13
14# Import standard Python libraries.
15import math, threading, queue
16
17# This uses python-osc to communicate with a Max/MSP patch.
18#   installation:      pip3 install python-osc
19#   source code:       https://github.com/attwad/python-osc
20#   pypi description:  https://pypi.org/project/python-osc/
21from pythonosc import udp_client
22from pythonosc import dispatcher
23from pythonosc import osc_server
24
25################################################################
26# Define the time step in milliseconds between
27# controller updates.
28EVENT_LOOP_DT = 20
29
30# Request a proxy object representing the robot to
31# control.
32robot = Robot()
33name = robot.getName()
34print(f"shutterbox_osc.py waking up for {name}...")
35
36# Fetch handle for the 'base' joint motor.
37motor1 = robot.getDevice('motor1')
38
39################################################################
40# Start a background thread running an OSC server listening for messages on an UDP socket.
41
42# Create a thread-safe queue to communicate data to the robot thread.
43messages = queue.Queue()
44
45def message(msgaddr, *args):
46    """Process messages received via OSC over UDP."""
47    print("Controller received message %s: %s" % (msgaddr, args))
48    if msgaddr == '/buttonbox/button':
49        messages.put(args)
50
51def unknown_message(msgaddr, *args):
52    """Default handler for unrecognized OSC messages."""
53    print("Simulator received unmapped message %s: %s" % (msgaddr, args))
54    
55# Initialize the OSC message dispatch system.
56dispatch = dispatcher.Dispatcher()
57dispatch.map("/buttonbox/*", message)
58dispatch.set_default_handler(unknown_message)
59
60# Start and run the server.
61simport = 16375 if name == 'left' else 54375
62server = osc_server.ThreadingOSCUDPServer(('127.0.0.1', simport), dispatch)
63server_thread = threading.Thread(target=server.serve_forever)
64server_thread.daemon = True
65server_thread.start()
66print(f"shutterbox_osc {name} started OSC server on port {simport}")
67
68################################################################
69# Run an event loop until the simulation quits,
70# indicated by the step function returning -1.
71while robot.step(EVENT_LOOP_DT) != -1:
72
73    # Read simulator clock time.
74    t = robot.getTime()
75
76    # Check for network messages.
77    if not messages.empty():
78        # each message is a tuple of the form (button index, status)
79        # e.g. (1,1) is button 1 pressed, (1,0) is button 1 released
80        msg = messages.get()
81        if len(msg) == 2:
82            if msg[0] == 1:
83                if msg[1] == 1:                
84                    motor1.setTorque(-1)
85                else:
86                    motor1.setTorque(0)
87            elif msg[0] == 1:
88                if msg[1] == 1:                
89                    motor1.setTorque(1)
90                else:
91                    motor1.setTorque(0)

World File

  1#VRML_SIM R2022a utf8
  2WorldInfo {
  3  basicTimeStep 5
  4}
  5Viewpoint {
  6  orientation -0.26570435851864105 0.22355324061151227 0.9377767018199423 1.7385757199994738
  7  position 0.32559893826458497 -2.6292060353822615 2.7294458883735686
  8}
  9Background {
 10  skyColor [
 11    0.1 0.1 0.1
 12  ]
 13}
 14DirectionalLight {
 15  direction -0.4 -0.5 -1
 16  intensity 0.25
 17  castShadows TRUE
 18}
 19RectangleArena {
 20  floorSize 5 2
 21}
 22Wall {
 23  translation 0 1 0
 24  rotation 0 0 1 1.5708
 25  size 0.2 5 2.4
 26}
 27pedestal {
 28  translation 0.75 0 0
 29  width 0.5
 30  depth 0.5
 31  height 1
 32}
 33Robot {
 34  translation 0.75 0 1
 35  children [
 36    DEF base Transform {
 37      translation 0 0 0.05
 38      children [
 39        Shape {
 40          appearance GlossyPaint {
 41            baseColor 0.180392 0 1
 42          }
 43          geometry Cylinder {
 44            height 0.1
 45            radius 0.2
 46          }
 47        }
 48      ]
 49    }
 50    PointLight {
 51      attenuation 0 0 1
 52      location 0 0 0.3
 53      castShadows TRUE
 54    }
 55    Transform {
 56      translation 0 0 0.2
 57      children [
 58        Shape {
 59          appearance GlossyPaint {
 60          }
 61          geometry Sphere {
 62            radius 0.01
 63          }
 64          castShadows FALSE
 65        }
 66      ]
 67    }
 68    HingeJoint {
 69      jointParameters HingeJointParameters {
 70        axis 0 0 1
 71        dampingConstant 0.1
 72      }
 73      device [
 74        RotationalMotor {
 75          name "motor1"
 76          maxVelocity 3.14
 77        }
 78      ]
 79      endPoint Solid {
 80        translation 0 0 0.11000000000000003
 81        children [
 82          DEF disc1 Transform {
 83            translation 0 0 0.005
 84            children [
 85              Shape {
 86                appearance GlossyPaint {
 87                  baseColor 0.772549 0.2 0.192157
 88                }
 89                geometry Cylinder {
 90                  height 0.01
 91                  radius 0.16
 92                }
 93              }
 94            ]
 95          }
 96          HingeJoint {
 97            jointParameters HingeJointParameters {
 98              axis 0 0 1
 99              anchor 0.15 0 0
100              minStop -0.01
101              maxStop 2
102              springConstant 0.05
103              dampingConstant 0.01
104            }
105            endPoint Solid {
106              translation 0 0 0.01
107              children [
108                Shape {
109                  appearance GlossyPaint {
110                    baseColor 0.192157 0.772549 0.589319
111                  }
112                  geometry Mesh {
113                    url [
114                      "../stl/shutterbox-vane.stl"
115                    ]
116                  }
117                }
118              ]
119              physics Physics {
120                density -1
121                mass 0.5
122                centerOfMass [
123                  0.1 -0.1 0.1
124                ]
125                inertiaMatrix [
126                  0.03 0.03 0.06
127                  0 0 0
128                ]
129              }
130            }
131          }
132          HingeJoint {
133            jointParameters HingeJointParameters {
134              axis 0 0 1
135              anchor 0.0463525 0.142658 0
136              minStop -0.01
137              maxStop 2
138              springConstant 0.05
139              dampingConstant 0.01
140            }
141            endPoint Solid {
142              translation 0 0 0.01
143              rotation 0 0 1 1.2566370614359172
144              children [
145                Shape {
146                  appearance GlossyPaint {
147                    baseColor 0.192157 0.772549 0.589319
148                  }
149                  geometry Mesh {
150                    url [
151                      "../stl/shutterbox-vane.stl"
152                    ]
153                  }
154                }
155              ]
156              name "solid(1)"
157              physics Physics {
158                density -1
159                mass 0.5
160                centerOfMass [
161                  0.1 -0.1 0.1
162                ]
163                inertiaMatrix [
164                  0.03 0.03 0.06
165                  0 0 0
166                ]
167              }
168            }
169          }
170          HingeJoint {
171            jointParameters HingeJointParameters {
172              axis 0 0 1
173              anchor -0.121353 0.0881678 0
174              minStop -0.01
175              maxStop 2
176              springConstant 0.05
177              dampingConstant 0.01
178            }
179            endPoint Solid {
180              translation 0 0 0.01
181              rotation 0 0 0.9999999999999999 2.5132741228718345
182              children [
183                Shape {
184                  appearance GlossyPaint {
185                    baseColor 0.192157 0.772549 0.589319
186                  }
187                  geometry Mesh {
188                    url [
189                      "../stl/shutterbox-vane.stl"
190                    ]
191                  }
192                }
193              ]
194              name "solid(2)"
195              physics Physics {
196                density -1
197                mass 0.5
198                centerOfMass [
199                  0.1 -0.1 0.1
200                ]
201                inertiaMatrix [
202                  0.03 0.03 0.06
203                  0 0 0
204                ]
205              }
206            }
207          }
208          HingeJoint {
209            jointParameters HingeJointParameters {
210              axis 0 0 1
211              anchor -0.121353 -0.0881678 0
212              minStop -0.01
213              maxStop 2
214              springConstant 0.05
215              dampingConstant 0.01
216            }
217            endPoint Solid {
218              translation 0 0 0.01
219              rotation 0 0 -0.9999999999999999 2.5132741228718345
220              children [
221                Shape {
222                  appearance GlossyPaint {
223                    baseColor 0.192157 0.772549 0.589319
224                  }
225                  geometry Mesh {
226                    url [
227                      "../stl/shutterbox-vane.stl"
228                    ]
229                  }
230                }
231              ]
232              name "solid(3)"
233              physics Physics {
234                density -1
235                mass 0.5
236                centerOfMass [
237                  0.1 -0.1 0.1
238                ]
239                inertiaMatrix [
240                  0.03 0.03 0.06
241                  0 0 0
242                ]
243              }
244            }
245          }
246          HingeJoint {
247            jointParameters HingeJointParameters {
248              axis 0 0 1
249              anchor 0.0463525 -0.142658 0
250              minStop -0.01
251              maxStop 2
252              springConstant 0.05
253              dampingConstant 0.01
254            }
255            endPoint Solid {
256              translation 0 0 0.01
257              rotation 0 0 -1 1.2566370614359172
258              children [
259                Shape {
260                  appearance GlossyPaint {
261                    baseColor 0.192157 0.772549 0.589319
262                  }
263                  geometry Mesh {
264                    url [
265                      "../stl/shutterbox-vane.stl"
266                    ]
267                  }
268                }
269              ]
270              name "solid(4)"
271              physics Physics {
272                density -1
273                mass 0.5
274                centerOfMass [
275                  0.1 -0.1 0.1
276                ]
277                inertiaMatrix [
278                  0.03 0.03 0.06
279                  0 0 0
280                ]
281              }
282            }
283          }
284        ]
285        physics Physics {
286          density -1
287          mass 2
288          centerOfMass [
289            0 0 0
290          ]
291          inertiaMatrix [
292            0.03 0.03 0.06
293            0 0 0
294          ]
295        }
296      }
297    }
298  ]
299  name "right"
300  controller "shutterbox"
301}
302pedestal {
303  translation -0.75 0 0
304  width 0.5
305  depth 0.5
306  height 1
307}
308Robot {
309  translation -0.75 0 1
310  children [
311    DEF base Transform {
312      translation 0 0 0.05
313      children [
314        Shape {
315          appearance GlossyPaint {
316            baseColor 0.180392 0 1
317          }
318          geometry Cylinder {
319            height 0.1
320            radius 0.2
321          }
322        }
323      ]
324    }
325    PointLight {
326      attenuation 0 0 1
327      location 0 0 0.3
328      castShadows TRUE
329    }
330    Transform {
331      translation 0 0 0.2
332      children [
333        Shape {
334          appearance GlossyPaint {
335          }
336          geometry Sphere {
337            radius 0.01
338          }
339          castShadows FALSE
340        }
341      ]
342    }
343    HingeJoint {
344      jointParameters HingeJointParameters {
345        axis 0 0 1
346        dampingConstant 0.1
347      }
348      device [
349        RotationalMotor {
350          name "motor1"
351          maxVelocity 3.14
352        }
353      ]
354      endPoint Solid {
355        translation 0 0 0.11000000000000003
356        children [
357          DEF disc1 Transform {
358            translation 0 0 0.005
359            children [
360              Shape {
361                appearance GlossyPaint {
362                  baseColor 0.772549 0.2 0.192157
363                }
364                geometry Cylinder {
365                  height 0.01
366                  radius 0.16
367                }
368              }
369            ]
370          }
371          HingeJoint {
372            jointParameters HingeJointParameters {
373              axis 0 0 1
374              anchor 0.15 0 0
375              minStop -0.01
376              maxStop 2
377              springConstant 0.05
378              dampingConstant 0.01
379            }
380            endPoint Solid {
381              translation 0 0 0.01
382              children [
383                Shape {
384                  appearance GlossyPaint {
385                    baseColor 0.192157 0.772549 0.589319
386                  }
387                  geometry Mesh {
388                    url [
389                      "../stl/shutterbox-vane.stl"
390                    ]
391                  }
392                }
393              ]
394              physics Physics {
395                density -1
396                mass 0.5
397                centerOfMass [
398                  0.1 -0.1 0.1
399                ]
400                inertiaMatrix [
401                  0.03 0.03 0.06
402                  0 0 0
403                ]
404              }
405            }
406          }
407          HingeJoint {
408            jointParameters HingeJointParameters {
409              axis 0 0 1
410              anchor 0.0463525 0.142658 0
411              minStop -0.01
412              maxStop 2
413              springConstant 0.05
414              dampingConstant 0.01
415            }
416            endPoint Solid {
417              translation 0 0 0.01
418              rotation 0 0 1 1.2566370614359172
419              children [
420                Shape {
421                  appearance GlossyPaint {
422                    baseColor 0.192157 0.772549 0.589319
423                  }
424                  geometry Mesh {
425                    url [
426                      "../stl/shutterbox-vane.stl"
427                    ]
428                  }
429                }
430              ]
431              name "solid(1)"
432              physics Physics {
433                density -1
434                mass 0.5
435                centerOfMass [
436                  0.1 -0.1 0.1
437                ]
438                inertiaMatrix [
439                  0.03 0.03 0.06
440                  0 0 0
441                ]
442              }
443            }
444          }
445          HingeJoint {
446            jointParameters HingeJointParameters {
447              axis 0 0 1
448              anchor -0.121353 0.0881678 0
449              minStop -0.01
450              maxStop 2
451              springConstant 0.05
452              dampingConstant 0.01
453            }
454            endPoint Solid {
455              translation 0 0 0.01
456              rotation 0 0 0.9999999999999999 2.5132741228718345
457              children [
458                Shape {
459                  appearance GlossyPaint {
460                    baseColor 0.192157 0.772549 0.589319
461                  }
462                  geometry Mesh {
463                    url [
464                      "../stl/shutterbox-vane.stl"
465                    ]
466                  }
467                }
468              ]
469              name "solid(2)"
470              physics Physics {
471                density -1
472                mass 0.5
473                centerOfMass [
474                  0.1 -0.1 0.1
475                ]
476                inertiaMatrix [
477                  0.03 0.03 0.06
478                  0 0 0
479                ]
480              }
481            }
482          }
483          HingeJoint {
484            jointParameters HingeJointParameters {
485              axis 0 0 1
486              anchor -0.121353 -0.0881678 0
487              minStop -0.01
488              maxStop 2
489              springConstant 0.05
490              dampingConstant 0.01
491            }
492            endPoint Solid {
493              translation 0 0 0.01
494              rotation 0 0 -0.9999999999999999 2.5132741228718345
495              children [
496                Shape {
497                  appearance GlossyPaint {
498                    baseColor 0.192157 0.772549 0.589319
499                  }
500                  geometry Mesh {
501                    url [
502                      "../stl/shutterbox-vane.stl"
503                    ]
504                  }
505                }
506              ]
507              name "solid(3)"
508              physics Physics {
509                density -1
510                mass 0.5
511                centerOfMass [
512                  0.1 -0.1 0.1
513                ]
514                inertiaMatrix [
515                  0.03 0.03 0.06
516                  0 0 0
517                ]
518              }
519            }
520          }
521          HingeJoint {
522            jointParameters HingeJointParameters {
523              axis 0 0 1
524              anchor 0.0463525 -0.142658 0
525              minStop -0.01
526              maxStop 2
527              springConstant 0.05
528              dampingConstant 0.01
529            }
530            endPoint Solid {
531              translation 0 0 0.01
532              rotation 0 0 -1 1.2566370614359172
533              children [
534                Shape {
535                  appearance GlossyPaint {
536                    baseColor 0.192157 0.772549 0.589319
537                  }
538                  geometry Mesh {
539                    url [
540                      "../stl/shutterbox-vane.stl"
541                    ]
542                  }
543                }
544              ]
545              name "solid(4)"
546              physics Physics {
547                density -1
548                mass 0.5
549                centerOfMass [
550                  0.1 -0.1 0.1
551                ]
552                inertiaMatrix [
553                  0.03 0.03 0.06
554                  0 0 0
555                ]
556              }
557            }
558          }
559        ]
560        physics Physics {
561          density -1
562          mass 2
563          centerOfMass [
564            0 0 0
565          ]
566          inertiaMatrix [
567            0.03 0.03 0.06
568            0 0 0
569          ]
570        }
571      }
572    }
573  ]
574  name "left"
575  controller "shutterbox"
576}