title: Python-ONVIF PTZ control -- Absolute and Relative Move
body: <p>I am developing an application in Python to control ONVIF-compatible cameras.</p>
  
  <p>Software: Debian Wheezy, Python 2.7, Quatanium python-onvif client</p>
  
  <p>Hardware: Raspberry Pi 2 B, IP camera on local router, wifi/VNC for development</p>
  
  <p>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.</p>
  
  <p><strong>A test sketch:</strong></p>
  
  <pre><code>#!/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()
  </code></pre>
  
  <p><strong>The referenced class:</strong></p>
  
  <pre><code>#*****************************************************************************
  #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
  </code></pre>
  
  <p>@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. </p>