Stutter while sending PTZ command

MWLC

n3wb
Jun 20, 2024
4
0
The Netherlands
Hi all,

I'm working on a script to move my camera using /ISAPI endpoints. I have set a ptz location for a preset with ID 1 so I can go back to it when I want. I can see the live feed of my camera to keep track of what is happening. When I send the preset command, the camera moves a little to that direction and not fully. I checked if the problem is in my connection, but sometimes it moves on one command, and sometimes not.

These are the details of my camera
"""
Model: DS-2DE2A204IW-DE3
Firmware Version:V5.6.16 build 200925
Encoding Version: V7.3 build 200907
Web Version:V4.0.1 build 191111
Plugin Version:3.0.7.25
"""


Here is the code I'm using
Python:
import logging
import xml.etree.ElementTree as ET

import requests
from requests.auth import HTTPDigestAuth

logger = logging.getLogger(__name__)


class CameraControl:
    def __init__(self, base_url, user, password):
        self.base_url = base_url
        self.user = user
        self.password = password

    def adjust_ptz(
        self,
        pan=None,
        tilt=None,
        panSpeed=100,
        tiltSpeed=100,
        duration=500,
        method="Continuous",
    ):
        try:
            # pan = tilt [-100:100]
            if method == "Momentary":
                url = f"{self.base_url}/ISAPI/PTZCtrl/channels/1/Momentary"
                payload = f"<PTZData><pan>{pan}</pan><tilt>{tilt}</tilt><panSpeed>{panSpeed}</panSpeed><tiltSpeed>{tiltSpeed}</tiltSpeed><zoom>0</zoom><Momentary><duration>{duration}</duration></Momentary></PTZData>"
            elif method == "Continuous":
                url = f"{self.base_url}/ISAPI/PTZCtrl/channels/1/Continuous"
                payload = f"<PTZData><pan>{pan}</pan><tilt>{tilt}</tilt><panSpeed>{panSpeed}</panSpeed><tiltSpeed>{tiltSpeed}</tiltSpeed><zoom>0</zoom><Continuous><duration>{duration}</duration></Continuous></PTZData>"
            elif method == "Absolute":
                # pan [0:3550], tilt [0-900]
                url = f"{self.base_url}/ISAPI/PTZCtrl/channels/1/Absolute"
                payload = (
                    "<PTZData>"
                    f"<AbsoluteHigh><azimuth>{pan}</azimuth><elevation>{tilt}</elevation><panSpeed>{panSpeed}</panSpeed><tiltSpeed>{tiltSpeed}</tiltSpeed><absoluteZoom>10</absoluteZoom></AbsoluteHigh>"
                    "</PTZData>"
                )

            response = requests.put(
                url,
                auth=HTTPDigestAuth(self.user, self.password),
                data=payload,
                headers={"Content-Type": "application/xml"},
            )
            if response.status_code == 200:
                return "Success: PTZ adjusted."
            else:
                logger.info(
                    f"Error: Unexpected response {response.status_code} - {response.text}"
                )
                return f"Error: Unexpected response {response.status_code} - {response.text}"
        except requests.exceptions.RequestException as err:
            return f"Error: {err}"

    def get_ptz_status(self):
        url = f"{self.base_url}/ISAPI/PTZCtrl/channels/1/status"
        try:
            response = requests.get(url, auth=HTTPDigestAuth(self.user, self.password))
            response.raise_for_status()
            xml_root = ET.fromstring(response.content)
            ns = {"hik": "http://www.hikvision.com/ver20/XMLSchema"}
            azimuth = xml_root.find("./hik:azimuth", ns).text
            elevation = xml_root.find("./hik:elevation", ns).text
            return (azimuth, elevation)
        except requests.exceptions.RequestException as e:
            return f"Failed to get PTZ status: {e}"
        except ET.ParseError as e:
            return f"XML parsing error: {e}"

    def set_home_position(self):
        url = f"{self.base_url}/ISAPI/PTZCtrl/channels/1/homeposition/set"
        response = requests.put(url, auth=HTTPDigestAuth(self.user, self.password))
        try:
            response.raise_for_status()
            return "Success: Home position set."
        except requests.exceptions.HTTPError as err:
            return f"Error: {err}"

    def goto_home_position(self):
        url = f"{self.base_url}/ISAPI/PTZCtrl/channels/1/homeposition/goto"
        response = requests.put(url, auth=HTTPDigestAuth(self.user, self.password))
        try:
            response.raise_for_status()
            if response.status_code == 200:
                return "Success: Camera moved to Home position."
            else:
                logger.info(
                    f"Error: Unexpected response {response.status_code} - {response.text}"
                )
                return f"Error: Unexpected response {response.status_code} - {response.text}"
        except requests.exceptions.HTTPError as err:
            return f"Error: {err}"

    def set_preset_position(self, preset_id):
        """Sets the current position of the PTZ camera as a preset position."""
        url = f"{self.base_url}/ISAPI/PTZCtrl/channels/1/presets/{preset_id}"
        payload = f"<PTZPreset><id>{preset_id}</id><presetName>Preset{preset_id}</presetName></PTZPreset>"
        response = requests.put(
            url,
            auth=HTTPDigestAuth(self.user, self.password),
            data=payload,
            headers={"Content-Type": "application/xml"},
        )
        try:
            response.raise_for_status()
            if response.status_code == 200:
                return f"Success: Preset position {preset_id} set."
            else:
                logger.info(
                    f"Error: Unexpected response {response.status_code} - {response.text}"
                )
                return f"Error: Unexpected response {response.status_code} - {response.text}"
        except requests.exceptions.HTTPError as err:
            return f"Error: {err}"

    def goto_preset_position(self, preset_id):
        """Moves the PTZ camera to a specific preset position."""
        url = f"{self.base_url}/ISAPI/PTZCtrl/channels/1/presets/{preset_id}/goto"
        response = requests.put(
            url,
            auth=HTTPDigestAuth(self.user, self.password),
            headers={"Content-Type": "application/xml"},
        )
        try:
            response.raise_for_status()
            if response.status_code == 200:
                logger.info(f"went to preset {preset_id}")
                return f"Success: Camera moved to preset position {preset_id}."
            else:
                logger.info(
                    f"Error: Unexpected response {response.status_code} - {response.text}"
                )
                return f"Error: Unexpected response {response.status_code} - {response.text}"
        except requests.exceptions.HTTPError as err:
            return f"Error: {err}"

    def get_preset_position(self, preset_id):
        """Gets the coordinates of a preset position."""
        url = f"{self.base_url}/ISAPI/PTZCtrl/channels/1/presets/{preset_id}"
        try:
            response = requests.get(url, auth=HTTPDigestAuth(self.user, self.password))
            response.raise_for_status()
            xml_root = ET.fromstring(response.content)
            ns = {"hik": "http://www.hikvision.com/ver20/XMLSchema"}
            azimuth = xml_root.find("./hik:azimuth", ns).text
            elevation = xml_root.find("./hik:elevation", ns).text
            return (azimuth, elevation)
        except requests.exceptions.RequestException as e:
            return f"Failed to get preset position: {e}"
        except ET.ParseError as e:
            return f"XML parsing error: {e}"


camera_control = CameraControl(f"http://192.168.1.64:80", "user1", "HaikuPlot876")

i = 1
while i < 6000:
    camera_control.adjust_ptz(pan="1800", tilt="10", duration=300, method="Absolute")
    i += 1
print(camera_control.get_ptz_status())

I made the for loop to test my theory of sending the command over time to make sure it will move, and it did move. How can i make sure that the camera will move only with one command?
 
Hi! Also new here. I don't know how much I'll be able to help, as my networking development experience is minimal but I'm very much here to learn and am interested in what you've got going. I've been working on a more DIY approach to an ALPR and want it to be more flexible than just staring down a road lane: I want it to be able to scan the surroundings periodically, and didn't want to shell out too much as I'll want multiple cameras.

I made the for loop to test my theory of sending the command over time to make sure it will move

Interesting, so if I'm not mistaken, sometimes the camera will properly return when given the preset command? If you've tested it already, does this issue occur with an arbitrary target position and what I assume is the "absolute" method being passed to adjust_ptz(), except without this method call being looped? I presume the goto_preset_position() method is what is intended to be called, and does the same issue occur with the goto_home_position() method as well?
 
Hi @blitzl3n, It's good to know that I'm not the only one new here.

Sometimes the camera will properly return when given the preset command?
yes, sometimes it would go to the preset location. I noticed that if i send it one over another, it would go after the third try, if i try again (without restarting it) it would go after the fifth try. These numbers aren't fixed, but it doesn't go to the preset on the second time after less tries from the first time. I would need to restart the whole unit in order to make it go to the preset from the first try.

does this issue occur with an arbitrary target?
I'm not using any of the camera functionalities to follow a person.

I presume the goto_preset_position() method is what is intended to be called?
Yes it is

Does the same issue occur with the goto_home_position() method as well?
I didn't try with it, but I assume it wouldn't move to the home position and just move a little with every command I send.

Here is a more detailed version of my case scenario:

I have a Jetson nano (ip:192.168.1.100) connected to a router (IP:192.168.1.1) and a Hikvision camera (IP:192.168.1.64) is connected to that router. My jetson nano is running a docker image that moves the camera to follow a person. This script is written in python.

The problem I have is with the camera movement. When I start my script and it sends the movement command for the camera, it is working fine. When I stop the script and run it again, the camera moves only a little bit. I thought it was a problem in the python script, but the camera doesn't take any commands at all, even from other sources. I did try to move it with the Device Network SDK from Hikvision, but it also moves for a split second then stops. However, the camera shows the black and white text on bottom left to show the current pan and tilt, which means that it received the command successfully, but didn't fully execute the command.

Steps I took to solve the problem:
  • Send a restart command for the device using /ISAPI => It didn't manage to make the camera respond back to the pan and tilt commands.
  • Unplug the power for the camera, and plug it back => it would go through the whole self-check process (pan and tilt check) but it won't respond also after.
  • Remove the internet cable and connect it back => still the same problem.
  • The only way for the camera to respond back, was to turn off power from the whole circuit (router, nano and the camera) then turn them on again.

My hunch is telling me it might be a problem with the IP of the camera? It might be blocking the IP of Nano after it sends a specific number of commands.

On a side note, I noticed that the camera restarts by itself after a while.

I'm willing to change my whole code to use different approach that /ISAPI as long as it would give me the functionality of moving the camera to a position on one command.
 
Hi @blitzl3n, It's good to know that I'm not the only one new here.


yes, sometimes it would go to the preset location. I noticed that if i send it one over another, it would go after the third try, if i try again (without restarting it) it would go after the fifth try. These numbers aren't fixed, but it doesn't go to the preset on the second time after less tries from the first time. I would need to restart the whole unit in order to make it go to the preset from the first try.

I'm not using any of the camera functionalities to follow a person.

Yes it is

I didn't try with it, but I assume it wouldn't move to the home position and just move a little with every command I send.

Here is a more detailed version of my case scenario:

I have a Jetson nano (ip:192.168.1.100) connected to a router (IP:192.168.1.1) and a Hikvision camera (IP:192.168.1.64) is connected to that router. My jetson nano is running a docker image that moves the camera to follow a person. This script is written in python.

The problem I have is with the camera movement. When I start my script and it sends the movement command for the camera, it is working fine. When I stop the script and run it again, the camera moves only a little bit. I thought it was a problem in the python script, but the camera doesn't take any commands at all, even from other sources. I did try to move it with the Device Network SDK from Hikvision, but it also moves for a split second then stops. However, the camera shows the black and white text on bottom left to show the current pan and tilt, which means that it received the command successfully, but didn't fully execute the command.

Steps I took to solve the problem:
  • Send a restart command for the device using /ISAPI => It didn't manage to make the camera respond back to the pan and tilt commands.
  • Unplug the power for the camera, and plug it back => it would go through the whole self-check process (pan and tilt check) but it won't respond also after.
  • Remove the internet cable and connect it back => still the same problem.
  • The only way for the camera to respond back, was to turn off power from the whole circuit (router, nano and the camera) then turn them on again.

My hunch is telling me it might be a problem with the IP of the camera? It might be blocking the IP of Nano after it sends a specific number of commands.

On a side note, I noticed that the camera restarts by itself after a while.

I'm willing to change my whole code to use different approach that /ISAPI as long as it would give me the functionality of moving the camera to a position on one command.

Apologies for the late response, I actually just just got in my Jetson more recently and spent a bit of time updating its firmware and updating Jet Pack. I had my eyes set on a camera and second guessed my choice, and in part what might inform my decision is what might be more conducive to supporting features like what you're working with: custom control over PTZ.

I'd have a difficult time debugging through the address information in your code but nothing immediately sticks out and the behavior is consistent with suggesting a different problem besides the software side of things. Given that the camera is linked to the router directly, I'm assuming it's not running on PoE, is it? And if so, the camera must be getting power from another source. You very well may have already comprehensively checked connections and power supplies or have no real reason to worry due to how you've set it up, but is the camera getting consistent power? The symptoms you're seeing probably don't quite match that possibility, though.

Like you've hypothesized on the camera blocking the IP of the camera, I think it sounds reasonable but would have trouble suggesting how to investigate. These cameras aren't cheap, so if you don't have another one with PTZ capabilities it's tough luck.

Again, you very well may have already tried everything I'll bring up: the IP address on the Nano is staying static, is it? Have you been able to check? If not, I'd wonder about configuring a static IP. Was DHCP enabled when activating the camera, if it would affect anything? No port address conflicts either? I've taken a peek through the DS-2DE2A204IW-DE3 user manual but it really wasn't much of a help as it doesn't really cover this use case.

On trying a different approach: maybe it could be worth reaching out to Hikvision? Their integration center might help, perhaps contact them with a burner account and/or fake identity if you feel it would be better to: you will need to make an account to even access their ISAPI dev guide. I'd imagine many of their solutions might have a catch on these pages, whether it be payment or a requirement to keep your equipment coupled to their servers, but I feel it's probably worth a look before throwing in the towel on ISAPI.
 
Apologies for the late response, I actually just just got in my Jetson more recently and spent a bit of time updating its firmware and updating Jet Pack. I had my eyes set on a camera and second guessed my choice, and in part what might inform my decision is what might be more conducive to supporting features like what you're working with: custom control over PTZ.

I'd have a difficult time debugging through the address information in your code but nothing immediately sticks out and the behavior is consistent with suggesting a different problem besides the software side of things. Given that the camera is linked to the router directly, I'm assuming it's not running on PoE, is it? And if so, the camera must be getting power from another source. You very well may have already comprehensively checked connections and power supplies or have no real reason to worry due to how you've set it up, but is the camera getting consistent power? The symptoms you're seeing probably don't quite match that possibility, though.

Like you've hypothesized on the camera blocking the IP of the camera, I think it sounds reasonable but would have trouble suggesting how to investigate. These cameras aren't cheap, so if you don't have another one with PTZ capabilities it's tough luck.

Again, you very well may have already tried everything I'll bring up: the IP address on the Nano is staying static, is it? Have you been able to check? If not, I'd wonder about configuring a static IP. Was DHCP enabled when activating the camera, if it would affect anything? No port address conflicts either? I've taken a peek through the DS-2DE2A204IW-DE3 user manual but it really wasn't much of a help as it doesn't really cover this use case.

On trying a different approach: maybe it could be worth reaching out to Hikvision? Their integration center might help, perhaps contact them with a burner account and/or fake identity if you feel it would be better to: you will need to make an account to even access their ISAPI dev guide. I'd imagine many of their solutions might have a catch on these pages, whether it be payment or a requirement to keep your equipment coupled to their servers, but I feel it's probably worth a look before throwing in the towel on ISAPI.

The pain is real to set up the nano, not to mention if you would need to utilize the GPU power with TensorRt and CUDA. It took me quite a while (1.5 month) to get it done.

The camera is connected directly to a constant power source. I'm pretty sure that my power source is working consistently because my router (and the nano) are connected to the same source. Both Nano and the router, are working without going down.

I did try to contact "Hikvision Technical Support - USA" and they told me it didn't show up on their system. I would have to dig into the receipts from the company.

The ip for all attached device to the router, is static. The nano: 192.168.1.100 and the camera: 192.168.1.64.

Thanks for mentioning the integration center. I will check it later. My steps from now on will be to just diagnose with different cameras and see if the problem persists. I hope it is only in this unit.

Feel free to reach if you need help with the nano or have more to share.

Cheers
 
The pain is real to set up the nano, not to mention if you would need to utilize the GPU power with TensorRt and CUDA. It took me quite a while (1.5 month) to get it done.

Cheers

Again, sorry for the delay in responding. You were right: the last week has just been me playing with setting up the Nano with CUDA support and I've currently spent multiple days just trying to run Nvidia's pre-built NGC models. Yes, I'm a bit of a Linux noob and it slows things down, but the amount of work that needs to be done is certainly a lot for just one person. These have distracted me so much so I've really just forgotten about the camera altogether: I haven't bought a Hikvision and I hear that they're remarkably difficult to get ahold of these days in the US due to more recent regulations, and that sellers are inundated with fakes. Are things any different where you live, where you bought it, or just at the time you bought your camera? Your experiences with Hikvision tech support do make me a bit more leery too, but I feel as though I don't have the skills or background to develop my own PTZ algorithms without a lot of documentation holding my hand either.

Also noted on the power source: I imagine the Nano would be even more sensitive to power fluctuations after all and that can probably be struck out. Also noted on the IP addresses.

I apologize again that I've not been able to be of much help. Chances are I'll just need to work around with the Jetson and might need to drop down the scope of my work to a fixed camera. I'm not a fan of that, though, and would still like to target something similar to what you're attempting in the long-run.
 
Again, sorry for the delay in responding. You were right: the last week has just been me playing with setting up the Nano with CUDA support and I've currently spent multiple days just trying to run Nvidia's pre-built NGC models. Yes, I'm a bit of a Linux noob and it slows things down, but the amount of work that needs to be done is certainly a lot for just one person. These have distracted me so much so I've really just forgotten about the camera altogether: I haven't bought a Hikvision and I hear that they're remarkably difficult to get ahold of these days in the US due to more recent regulations, and that sellers are inundated with fakes. Are things any different where you live, where you bought it, or just at the time you bought your camera? Your experiences with Hikvision tech support do make me a bit more leery too, but I feel as though I don't have the skills or background to develop my own PTZ algorithms without a lot of documentation holding my hand either.

Also noted on the power source: I imagine the Nano would be even more sensitive to power fluctuations after all and that can probably be struck out. Also noted on the IP addresses.

I apologize again that I've not been able to be of much help. Chances are I'll just need to work around with the Jetson and might need to drop down the scope of my work to a fixed camera. I'm not a fan of that, though, and would still like to target something similar to what you're attempting in the long-run.

There is a lot that would be done for the Jetson nano to get CUDA support. I have made a docker image that would utilize TensorRt with UltraLytics which should get you fully up with the GPU support. TensorRt will allow much faster inference speed with a .engine model you would run, over a .pt. You can follow this guide instead Install PyTorch on Jetson Nano - Q-engineering (qengineering.eu) if you want to have ubuntu GUI working.

I live in the Netherlands, and the cameras were bought by the company. We bought them in bulk, so I assume they were cheap.

As an update on my camera issue. I realized it isn't bound by how long the camera has been up for. I would restart the camera and leave it for 5,10 and 15 min, and it would still be able to move. I noticed that the camera would restart by itself without me interacting with it. I don't think it is a power problem at this point