Python-ONVIF PTZ control -- Absolute and Relative Move
Question: I am developing an application in Python to control ONVIF-compatible cameras.

Software: Debian Wheezy, Python 2.7, Quatanium python-onvif client

Hardware: Raspberry Pi 2 B, IP camera on local router, wifi/VNC for
development

The required PTZ functions include Absolute Move, Relative Move, Continuous
Move, Stop and using Preset positions. With the extracted test code below, I
have all of that working except Absolute and Relative Moves. All of this code
executes without any errors but the camera does not move for the Absolute or
Relative Moves. I hope someone can suggest the problem with those two
functions. The example is a bit long but I have tried to include enough code
to show the contrast between working and non-working (with upper-case
comments) portions for reference and test.

**A test sketch:**

    
    
    #!/usr/bin/python
    
    #-------------------------------------------------------------------------------
    #Test of Python and Quatanium Python-ONVIF with NETCAT camera PT-PTZ2087
    #ONVIF Client implementation is in Python
    #For IP control of PTZ, the camera should be compliant with ONVIF Profile S
    #The PTZ2087 reports it is ONVIF 2.04 but is actually 2.4 (Netcat said text not changed after upgrade)
    #------------------------------------------------------------------------------
    
    import onvifconfig
    
    if __name__ == '__main__':
    
        #Do all setup initializations
        ptz = onvifconfig.ptzcam()
    
    #*****************************************************************************
    # IP camera motion tests
    #*****************************************************************************
        print 'Starting tests...'
    
        #Set preset
        ptz.move_pan(1.0, 1)  #move to a new home position
        ptz.set_preset('home')
    
        # move right -- (velocity, duration of move)
        ptz.move_pan(1.0, 2)
    
        # move left
        ptz.move_pan(-1.0, 2)
    
        # move down
        ptz.move_tilt(-1.0, 2)
    
        # Move up
        ptz.move_tilt(1.0, 2)
    
        # zoom in
        ptz.zoom(8.0, 2)
    
        # zoom out
        ptz.zoom(-8.0, 2)
    
        #Absolute pan-tilt (pan position, tilt position, velocity)
        #DOES NOT RESULT IN CAMERA MOVEMENT
        ptz.move_abspantilt(-1.0, 1.0, 1.0)
        ptz.move_abspantilt(1.0, -1.0, 1.0)
    
        #Relative move (pan increment, tilt increment, velocity)
        #DOES NOT RESULT IN CAMERA MOVEMENT
        ptz.move_relative(0.5, 0.5, 8.0)
    
        #Get presets
        ptz.get_preset()
        #Go back to preset
        ptz.goto_preset('home')
    
        exit()
    

**The referenced class:**

    
    
    #*****************************************************************************
    #IP Camera control
    #Control methods:
    #   rtsp video streaming via OpenCV for frame capture
    #   ONVIF for PTZ control
    #   ONVIF for setup selections
    #
    # Starting point for this code was from:
    # https://github.com/quatanium/python-onvif
    #*****************************************************************************
    
    import sys
    sys.path.append('/usr/local/lib/python2.7/dist-packages/onvif')
    
    from onvif import ONVIFCamera
    from time import sleep
    
    class ptzcam():
        def __init__(self):
            print 'IP camera initialization'
    
        #Several cameras that have been tried  -------------------------------------
        #Netcat camera (on my local network) Port 8899
            self.mycam = ONVIFCamera('192.168.1.10', 8899, 'admin', 'admin', '/etc/onvif/wsdl/')
        #This is a demo camera that anyone can use for testing
        #Toshiba IKS-WP816R
            #self.mycam = ONVIFCamera('67.137.21.190', 80, 'toshiba', 'security', '/etc/onvif/wsdl/')
    
            print 'Connected to ONVIF camera'
            # Create media service object
            self.media = self.mycam.create_media_service()
            print 'Created media service object'
            print
            # Get target profile
            self.media_profile = self.media.GetProfiles()[0]
            # Use the first profile and Profiles have at least one
            token = self.media_profile._token
    
        #PTZ controls  -------------------------------------------------------------
            print
            # Create ptz service object
            print 'Creating PTZ object'
            self.ptz = self.mycam.create_ptz_service()
            print 'Created PTZ service object'
            print
    
            #Get available PTZ services
            request = self.ptz.create_type('GetServiceCapabilities')
            Service_Capabilities = self.ptz.GetServiceCapabilities(request)
            print 'PTZ service capabilities:'
            print Service_Capabilities
            print
    
            #Get PTZ status
            status = self.ptz.GetStatus({'ProfileToken':token})
            print 'PTZ status:'
            print status
            print 'Pan position:', status.Position.PanTilt._x
            print 'Tilt position:', status.Position.PanTilt._y
            print 'Zoom position:', status.Position.Zoom._x
            print 'Pan/Tilt Moving?:', status.MoveStatus.PanTilt
            print
    
            # Get PTZ configuration options for getting option ranges
            request = self.ptz.create_type('GetConfigurationOptions')
            request.ConfigurationToken = self.media_profile.PTZConfiguration._token
            ptz_configuration_options = self.ptz.GetConfigurationOptions(request)
            print 'PTZ configuration options:'
            print ptz_configuration_options
            print
    
            self.requestc = self.ptz.create_type('ContinuousMove')
            self.requestc.ProfileToken = self.media_profile._token
    
            self.requesta = self.ptz.create_type('AbsoluteMove')
            self.requesta.ProfileToken = self.media_profile._token
            print 'Absolute move options'
            print self.requesta
            print
    
            self.requestr = self.ptz.create_type('RelativeMove')
            self.requestr.ProfileToken = self.media_profile._token
            print 'Relative move options'
            print self.requestr
            print
    
            self.requests = self.ptz.create_type('Stop')
            self.requests.ProfileToken = self.media_profile._token
    
            self.requestp = self.ptz.create_type('SetPreset')
            self.requestp.ProfileToken = self.media_profile._token
    
            self.requestg = self.ptz.create_type('GotoPreset')
            self.requestg.ProfileToken = self.media_profile._token
    
            print 'Initial PTZ stop'
            print
            self.stop()
    
    #Stop pan, tilt and zoom
        def stop(self):
            self.requests.PanTilt = True
            self.requests.Zoom = True
            print 'Stop:'
            #print self.requests
            print
            self.ptz.Stop(self.requests)
            print 'Stopped'
    
    #Continuous move functions
        def perform_move(self, timeout):
            # Start continuous move
            ret = self.ptz.ContinuousMove(self.requestc)
            print 'Continuous move completed', ret
            # Wait a certain time
            sleep(timeout)
            # Stop continuous move
            self.stop()
            sleep(2)
            print
    
        def move_tilt(self, velocity, timeout):
            print 'Move tilt...', velocity
            self.requestc.Velocity.PanTilt._x = 0.0
            self.requestc.Velocity.PanTilt._y = velocity
            self.perform_move(timeout)
    
        def move_pan(self, velocity, timeout):
            print 'Move pan...', velocity
            self.requestc.Velocity.PanTilt._x = velocity
            self.requestc.Velocity.PanTilt._y = 0.0
            self.perform_move(timeout)
    
        def zoom(self, velocity, timeout):
            print 'Zoom...', velocity
            self.requestc.Velocity.Zoom._x = velocity
            self.perform_move(timeout)
    
    #Absolute move functions --NO ERRORS BUT CAMERA DOES NOT MOVE
        def move_abspantilt(self, pan, tilt, velocity):
            self.requesta.Position.PanTilt._x = pan
            self.requesta.Position.PanTilt._y = tilt
            self.requesta.Speed.PanTilt._x = velocity
            self.requesta.Speed.PanTilt._y = velocity
            print 'Absolute move to:', self.requesta.Position
            print 'Absolute speed:',self.requesta.Speed
            ret = self.ptz.AbsoluteMove(self.requesta)
            print 'Absolute move pan-tilt requested:', pan, tilt, velocity
            sleep(2.0)
            print 'Absolute move completed', ret
    
            print
    
    #Relative move functions --NO ERRORS BUT CAMERA DOES NOT MOVE
        def move_relative(self, pan, tilt, velocity):
            self.requestr.Translation.PanTilt._x = pan
            self.requestr.Translation.PanTilt._y = tilt
            self.requestr.Speed.PanTilt._x = velocity
            ret = self.requestr.Speed.PanTilt._y = velocity
            self.ptz.RelativeMove(self.requestr)
            print 'Relative move pan-tilt', pan, tilt, velocity
            sleep(2.0)
            print 'Relative move completed', ret
            print
    
    #Sets preset set, query and and go to
        def set_preset(self, name):
            self.requestp.PresetName = name
            self.requestp.PresetToken = '1'
            self.preset = self.ptz.SetPreset(self.requestp)  #returns the PresetToken
            print 'Set Preset:'
            print self.preset
            print
    
        def get_preset(self):
            self.ptzPresetsList = self.ptz.GetPresets(self.requestc)
            print 'Got preset:'
            print self.ptzPresetsList[0]
            print
    
        def goto_preset(self, name):
            self.requestg.PresetToken = '1'
            self.ptz.GotoPreset(self.requestg)
            print 'Going to Preset:'
            print name
            print
    

@Ottavio, Sorry that I did not make it clear that the camera I used for this
test, a Netcat PT-PTZ2084XM-A reported via ONVIF query that it did support
Absolute and Relative moves. I have subsequently verified via the onvif.org
site that this camera has not been tested and verified to meet onvif
standards. I also have verified that the above code does work correctly with a
Amcrest IP2M-841B ptz camera. The upshot of all of this is to never trust the
claim that a camera is ONVIF 2.x compatible without testing it. Even the
Amcrest has problems with both ONVIF and cgi commands for zoom. Neither Netcat
nor Amcrest have been very helpful in resolving these technical problems.


Answer: `AbsoluteMake` and `RelativeMove` in the [Profile S
specifications](http://www.onvif.org/Portals/0/documents/op/ONVIF_Profile_%20S_Specification_v1-1-1.pdf)
are `CONDITIONAL MANDATORY`, thus it is not guaranteed a-priori that they are
supported.

You need to check the camera's features.

