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