.. _moving_head: Moving Head ===================== This component offers low-level control over the pan-tilt projector. **Please read this entire introduction before using the projector!** .. warning:: Seriously, read this introduction! This is not a case of figuring stuff out while you go! The component doesn't manage the lamp state directly; it just passes your commands through to the DMX interface. There is no backchannel at all, so all the component can do in the end is guess what state the projector is in. When the lamp is switched off, it will take 8 seconds for the lamp to *actually* switch off (during which the lamp state will already self-report as "off" via this interface). The lamp will then have to cool down before you can switch it on again. This component will have no knowledge about the temperature and whether it is possible to switch on the lamp. If the lamp is set to "on" but doesn't actually turn on, the component will report a running lamp without any physical manifestation of this fact whatsoever. The same is true for all movements that are being carried out but haven't finished yet. The component will always report the target angle, not the current angle. Basically, this component will lie to you every chance it gets. This isn't due to malice but the lack of a backchannel though. You can use the shutter to disable the projector image without switching off the lamp. However, the lamp's lifetime is limited to 1000 hours (a bit more than 40 days) so don't leave the lamp running unnecessarily. .. warning:: After switching off the lamp, the projector needs to cool down for at least five minutes before switching off its mains supply. The stop script should do this automatically which means that stopping this script might take an extremely long time! If anything went wrong and the projector is in an undefined state, starting will take equally long because the script will go through the whole on-off cycle just to be sure. Angles in the RSB interface use floating point values which is converted internally into two bytes of precision for the DMX communication. This conversion is not standardized which means that the actual angle might differ slightly from the exact floating point value given. We simply map the range :math:`[\text{minimum angle}, \text{maximum angle}]` to :math:`[0, 65535]` in a linear fashion without regard for what the hi and lo byte actually mean in terms of physical angle. However, this means that by sticking to the range from minimum to maximum, you will always be able to use the full range of the moving head. The component currently supports two display modes: *color* and *picture*. In color mode, the whole projection area will be filled by a uniform color that can be freely chosen using a 24-bit RGB value. In picture mode, a 720×480 color JPEG is read from the SD card in the projector. See below for details on how to determine the ID of a picture to send and some currently installed pictures. The mode is changed automatically when you call ``setColorRGB()``/``setColorHSV()`` or ``setPicture()`` respectively. While the projector can display a VGA signal, this is not currently supported using this API. .. figure:: /images/movinghead/statemachine.png :scale: 50 % :alt: state machine of shutdown cycle (in uppercase): off <-> lamp off -> lamp on -> 8 second shutdown -> 5 minute cooldown -> lamp off; 5 minute cooldown -> lamp wait -> lamp on State machine of the moving head control. After starting the vdemo component, the projector should be in a state called "OFF" where the power plug is set to off so it will be cut from mains electricity. By sending the RPC command ``startProjector()``, the power plug will be switched on and the projector will go into a self-test mode that will take about 37 seconds (it will become ready after exactly 45 seconds). This state is called "WARMUP". After it is finished, it will automatically switch into the state "LAMP OFF". You can now send the RPC ``setLampState(true)`` that will put the projector into the state "LAMP ON" at which point it is fully ready to function. Most calls are not available before this point and will be ignored. Sending ``setLampState(false)`` will cause the projector to go into a shutdown mode. There will first be a visual countdown of 8 seconds on the projected image during which the state is "8S SHUTDOWN". After that, the lamp will be off but the state is "5MIN COOLDOWN" which unsurprisingly takes 5 minutes before going into "LAMP OFF" again. Only then can you send ``killProjector()`` to cut mains electricity again. You can interrupt the cooldown by setting the lamp to on again. This will not be an immediate process, however, and a state "LAMP WAIT" will be active for 90 seconds before "LAMP ON" is reached again. Notice that there is not necessarily a guarantee that the lamp will actually be available after 90 seconds. This will depend on the ambient temperature and there is no way of querying if the lamp is actually on again and the projector has completed a short procedure that goes along with screen changes and an unresponsiveness to external commands. Related resources ----------------- Component repository: - `Browse component repository `_. - ``git clone https://projects.cit-ec.uni-bielefeld.de/git/lsp-csra.pantiltprojector.git`` System startup: - switch on the projector using the hardware switch depicted below - ``movinghead`` component in the ``tools`` tab of the ``supplementary`` VDemo - alternatively, call ``start_movinghead.sh`` which itself calls ``rsbpantilt.py`` .. figure:: /images/hardware/movinghead_off_switch.jpeg :scale: 50 % :alt: power switch of the pan-tilt projector The power switch. You should never need to use it but if you do, make sure this is *on* before you try to use the software and make sure the lamp has been off for at least five minutes before you turn this *off* again. Handbook: The :download:`manufacturer documentation ` for the projector is available in PDF format. Interfaces ----------------- ======================================================= ====================================== ===================================== ========================================================================================= Scope (Remote Server) + Method Argument Type Return Type Description ======================================================= ====================================== ===================================== ========================================================================================= ``/citec/csra/home/living/movinghead/setPanTilt()`` rst.geometry.SphericalDirectionFloat bool set pan and tilt angle; returns false if value out of range (check stderr for details) ``/citec/csra/home/living/movinghead/lookAt()`` rst.geometry.Translation bool set coordinates to look at in living room coordinates ``/citec/csra/home/living/movinghead/getProjectorPosition()`` void rst.geometry.Translation get projector position ``/citec/csra/home/living/movinghead/getPanTilt()`` void rst.geometry.SphericalDirectionFloat get pan and tilt angle ``/citec/csra/home/living/movinghead/getPanMax()`` void float get max pan angle (should be 540) ``/citec/csra/home/living/movinghead/getPanMin()`` void float get min pan angle (should be 0) ``/citec/csra/home/living/movinghead/getTiltMax()`` void float get max tilt angle (should be 270) ``/citec/csra/home/living/movinghead/getTiltMin()`` void float get min tilt angle (should be 0) ``/citec/csra/home/living/movinghead/getPan()`` void float get current target pan angle (doesn't equal current angle if movement ongoing) ``/citec/csra/home/living/movinghead/getTilt()`` void float get current target tilt angle (doesn't equal current angle if movement ongoing) ``/citec/csra/home/living/movinghead/getState()`` void rst.devices.PanTiltProjector get complete device state ``/citec/csra/home/living/movinghead/setFocus()`` int bool set focus distance (0–255); returns success state ``/citec/csra/home/living/movinghead/getFocus()`` void int get target focus distance ``/citec/csra/home/living/movinghead/setLampState()`` bool bool switch lamp on or off; returns success state ``/citec/csra/home/living/movinghead/getLampState()`` void bool get target lamp state (true for on, false for off) ``/citec/csra/home/living/movinghead/setShutterOpen()`` bool bool open (true) or close (false) physical shutter in front of lamp ``/citec/csra/home/living/movinghead/isShutterOpen()`` void bool get shutter state ``/citec/csra/home/living/movinghead/setColor()`` rst.vision.Color bool set color as RGB or HSB tuple ``/citec/csra/home/living/movinghead/getColor()`` void rst.vision.RGBColor get color as RGB tuple ``/citec/csra/home/living/movinghead/setPicture()`` int bool set picture ID on SD card, range [0, 1023] (see above) ``/citec/csra/home/living/movinghead/getPicture()`` void int get picture ID on SD card ``/citec/csra/home/living/movinghead/startProjector()`` void bool set projector from ``off`` to ``lamp off`` mode, switching the power plug on ``/citec/csra/home/living/movinghead/killProjector()`` void bool set projector from ``lamp off`` to ``off`` mode, switching the power plug off ``/citec/csra/home/living/movinghead/isOff()`` void bool return true ⇔ projector is ``off`` ``/citec/csra/home/living/movinghead/getFSMState()`` void str get string describing state of the state machine (see figure) ======================================================= ====================================== ===================================== ========================================================================================= =========================================== ======================================== =============================================================================================================================== Scope (Informer) Type =========================================== ======================================== =============================================================================================================================== ``/citec/csra/home/living/movinghead/stateannounce`` str posts the state name of the new state each time a state change of the state machine occurs (see figure) =========================================== ======================================== =============================================================================================================================== Picture Interface ------------------- The projector can display a maximum of 1024 pictures stored on the SD card. These are stored in 32 blocks at 32 image files each. Each block has its own directory. The naming scheme is as follows: image 17 in block 3 is called ``/DVS-2500/Image/Image003/I003_017.jpg``. Notice the block number is repeated in the image file name. Block and image numbers start at 0. For convenience, this block structure is hidden in handling the images in this component. Therefore, block 0–31 and image 0–31 are mapped to one range 0–1023. There is a further complication, though, in that the first block is inaccessible for layers 1 and 2 and only usable in layer 3. Since layer 1 is currently used, block 0 is removed from the numbering (or more precisely, moved to the end). Therefore, the aforementioned image 17 in block 3 would get the ID :math:`((\text{block}-1) \mod 32) \cdot 32 + \text{image} = (3-1) \cdot 32 + 17 = 81`. Some potentially useful images for pointing purposes have the following IDs: ==== ======================================= =================== ID Description Image ==== ======================================= =================== 1 large circle |img1| 7 medium-sized circle |img7| 8 small circle |img8| 96 large, fuzzy circle |img96| 97 small circle, slightly fuzzy |img97| 60 color test |img60| ==== ======================================= =================== .. |img1| image:: /images/movinghead/Image001/I001_001.jpg :width: 100% .. |img7| image:: /images/movinghead/Image001/I001_007.jpg :width: 100% .. |img8| image:: /images/movinghead/Image001/I001_008.jpg :width: 100% .. |img96| image:: /images/movinghead/Image004/I004_000.jpg :width: 100% .. |img97| image:: /images/movinghead/Image004/I004_001.jpg :width: 100% .. |img60| image:: /images/movinghead/Image002/I002_028.jpg :width: 100% .. todo:: An automatically generated gallery of all pictures would be nice. All images after number 97 are currently empty, so custom images can be added very easily as long as image format and naming scheme are followed. They are then instantly accessible. Angle Orientations ------------------- For an elevation/tilt of 45 degrees, the following azimuth/pan angles point into these directions: ======== ================== Angle Points to ======== ================== 0 4K screen 90 Kitchen/hallway 180 Couch wall 270 Window ======== ================== Obviously, an angle of more than 90 degrees will invert these directions. Code Examples -------------- The following code assumes the projector is in the `LAMP OFF` state which is the case after the vdemo component has executed fully. .. code-block:: python :linenos: import rsb import time import rst import rstsandbox import rstexperimental import logging from rst.geometry.Translation_pb2 import Translation logging.basicConfig() registerGlobalConverter(ProtocolBufferConverter(messageClass = Translation)) rsb.__defaultParticipantConfig = rsb.ParticipantConfig.fromDefaultSources() with rsb.createRemoteServer('/home/living/movinghead/') as server: # switch on lamp server.setLampState(True) # create 3D point with x/y/z values in meters from living room coordinate system t = Translation() t.x = 2.5 # window axis t.y = 4.0 # wall axis t.z = 3.0 # ceiling axis # move projector server.lookAt(t) # open shutter for 3 seconds and switch off lamp server.setShutterState(True) time.sleep(3) server.setLampAndShutterState(False) # wait for projector to be in off state while server.getFSMState() != "LAMP OFF": time.sleep(1) # switch projector off completely server.killProjector() # switch projector on again server.startProjector() while server.getFSMState() != "LAMP OFF": time.sleep(1) .. todo:: Specify which methods can be called in which states and what the error return value is.