czech english

Bezpečnostní kamery

AI na hlídání AI?

Bezpečnostní kamery jsou již běžné spotřební zboží — nelze je nějak využít pro mobilní robotiku? Jsou to „černé krabičky” pouze s mobilní aplikací a daty na cloudu nebo je alternativa? Blog update: 6/9/2024 — AbsoluteMove

Obsah



Blog

26. srpna 2024 — Náhodný výběr

Původně jsem to chtěl nazvat spíše „výběr kamery”, ale nabídka bezpečnostních kamer je široká a má trpělivost cokoliv nakupovat je nerozumně omezená. Asi jediný požadavek byl outdoor s tím, že bych rád nějaké otevřené API. Konzultoval jsem to s perplexity a ve zkratce mi doporučila Reolink a ke kamerám TP-Link Tapo C500 naznačovala, že existuje PyTapo Python knihovna.
Teď zpětně trošku chápu, o čem to ta AI psala. Zatím jsem vybalil tu TP-Link Tapo C500, která podporuje pouze WiFi přípojení a používá zdroj 12V/1A. Vyžadovalo to instalovat TP-Link Tapo Android applikaci, aby se kamera přepnula z access point módu na připojení na WiFi router. V rozšířených nastaveních lze pak přidat nového uživatele s heslem. A s tím je spojeno to RTSP (Real-Time Streaming Protocol):
  • rtsp://username:password@camera_ip:554/stream1
… toto fungovalo vlastně ve VLC Media Playeru hned. Časem jsem dohledal, že stream1 je s vyšším rozlišením a existuje ještě stream2 s nižším rozlišením. Stejné URL funguje i v OpenCV a tím pádem i rovnou v OSGARovi (stačí změnit v config/test-opencv-camera.json port na URL výše). Jen v tom VLC je navíc slyšet i audio.
Další krok byl s kamerou pohnout. Co fungovalo bylo posouvání v aplikaci na telefonu (a stále streamovat video). S tím je spojeno to druhé klíčové slovo ONVIF (Open Network Video Interface Forum). A jelikož o tom nic nevím, tak jsem zase zkoušel perplexity. Podle doporučení jsem nainstaloval zeep a onvif_zeep. Ale příklad použití na Tapo C500 úplně nefungoval. První zrada byla s portem — místo 80 je třeba použít 2020, jen už nevím, kde jsem tuto nápovědu sebral. Což mi ale připomíná, že jsem úplně vynechal PyTapo, které se zaseklo na problému autorizace a přes to jsem se nedostal — stáhl si github repo, jestli tam není novější verze, ale nepomohlo to. Také jsem zapomněl zmínit, že na kameře jsem po prvním připojení upgradeoval firmware — to také mohlo rozbít aktuální pytapo. A také jsem nezmínil, že při dalším připojení se změnila IP adresa kamery, ale to bych asi případně mohl změnit na routeru (aneb další možnost, jak se to mohlo pokazit).
from onvif import ONVIFCamera

# Replace with your camera's IP address, port, username, and password
camera_ip = '192.168.1.100'
camera_port = 2020
username = 'admin'
password = 'password'

# Create an ONVIF camera object
camera = ONVIFCamera(camera_ip, camera_port, username, password)

# Get device information
device_info = camera.devicemgmt.GetDeviceInformation()
print("Manufacturer:", device_info.Manufacturer)
print("Model:", device_info.Model)
print("Firmware Version:", device_info.FirmwareVersion)
print("Serial Number:", device_info.SerialNumber)
print("Hardware ID:", device_info.HardwareId)
… toto fungovalo (se správnou IP, username a password)
Manufacturer: tp-link
Model: Tapo C500
Firmware Version: 1.1.4 Build 240506 Rel.39487n
Serial Number: a19999 (upraveno)
Hardware ID: 1.0
ale při pokusech o posunutí se mi podařilo „položit server”, míněno server běžící na kameře:
capabilities = camera.devicemgmt.GetCapabilities()
skončilo s chybou:
http.client.BadStatusLine: POST /onvif/device_serviceHTTP/1.1Host 192.168.1.18:2020
User-Agent Zeep/4.2.1 (www.python-zeep.org)Accept-Encoding gzip, deflateAcHTTP/1.1
500 Internal Server Error
Pro dnešek asi stačí … časem bych doplnil i úvodní „omáčku” aneb motivace a pod.
p.s. thumb jsem převzal z https://www.railfanguides.us/oblivion/

6. září 2024 — AbsoluteMove

Toto se pokusím ještě teď v noci „odbavit” jako důkaz, že už nic nebrání nočnímu testování.
Posledně jsem psal, že dostat video z Tapo C500 bylo vlastně bez problémů (s použítím VLC). Dobrá zpráva je, že je i poměrně triviální to streamované video nahrát (pokud je kamera na síti). Co ale bylo (naštěstí už minulý čas) peklo bylo s tou kamerou programově pohnout! Jako ten ONVIF je pěkná věc, ale to by to musel každý výrobce pořádně dodržovat. A jak jsem psal, že posledně to skončilo 500 na serveru, tak s tím jsem dnes začal …
A k mému překvapení to není tak stará věc:
Jako myslím, že to novějším FW nevyřešili (mě to s 1.1.4 padá stále), ale zaujala mne tam další poznámka: the "UtcTime" element should be returned in the ISO 8601 format., která pěkně korelovala s chybou:
ERROR:zeep.xsd.types.simple:Error during xml -> python translation
Traceback (most recent call last):
  File "/home/md/.virtualenvs/tapo/lib/python3.8/site-packages/isodate/isodatetime.py", line 51, in parse_datetime
    datestring, timestring = datetimestring.split('T')
ValueError: not enough values to unpack (expected 2, got 1)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/md/.virtualenvs/tapo/lib/python3.8/site-packages/zeep/xsd/types/simple.py", line 79, in parse_xmlelement
    return self.pythonvalue(xmlelement.text)
  File "/home/md/.virtualenvs/tapo/lib/python3.8/site-packages/zeep/xsd/types/builtins.py", line 44, in _wrapper
    return func(self, re.sub(r"[\n\r\t ]", " ", value).strip())
  File "/home/md/.virtualenvs/tapo/lib/python3.8/site-packages/zeep/xsd/types/builtins.py", line 180, in pythonvalue
    return isodate.parse_datetime(value)
  File "/home/md/.virtualenvs/tapo/lib/python3.8/site-packages/isodate/isodatetime.py", line 53, in parse_datetime
    raise ISO8601Error("ISO 8601 time designator 'T' missing. Unable to"
isodate.isoerror.ISO8601Error: ISO 8601 time designator 'T' missing. Unable to parse datetime string 'Fri Sep  6 20:52:11 2024'
Teď už si nevzpomenu, co jsem z té kamery chtěl dostat, asi
media = mycam.create_media_service()

# Create ptz service object
ptz = mycam.create_ptz_service()

# Get target profile
media_profile = media.GetProfiles()[0]

# Get PTZ configuration options for getting continuous move range
request = ptz.create_type('GetConfigurationOptions')
request.ConfigurationToken = media_profile.PTZConfiguration.token
ptz_configuration_options = ptz.GetConfigurationOptions(request)
… no nevím, měl jsem si to hned psát. Sigh.
Ale je to Python, takže stačilo oeditovat /home/md/.virtualenvs/tapo/lib/python3.8/site-packages/isodate/isodatetime.py, konkrétně
try:
        datestring, timestring = datetimestring.split('T')
    except ValueError:
        return datetime.strptime(datetimestring, "%a %b %d %H:%M:%S %Y")
#        raise ISO8601Error("ISO 8601 time designator 'T' missing. Unable to"
#                           " parse datetime string %r" % datetimestring)
Další milník byl výpis ptz_configuration_options:
'Spaces': {
        'AbsolutePanTiltPositionSpace': [
            {
                'URI': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace',
                'XRange': {
                    'Min': -1.0,
                    'Max': 1.0
                },
                'YRange': {
                    'Min': -1.0,
                    'Max': 1.0
                }
            }
        ],
        'AbsoluteZoomPositionSpace': [],
        'RelativePanTiltTranslationSpace': [
            {
                'URI': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/TranslationGenericSpace',
                'XRange': {
                    'Min': -1.0,
                    'Max': 1.0
                },
                'YRange': {
                    'Min': -1.0,
                    'Max': 1.0
                }
            }
        ],
        'RelativeZoomTranslationSpace': [],
        'ContinuousPanTiltVelocitySpace': [],
        'ContinuousZoomVelocitySpace': [],
        'PanTiltSpeedSpace': [
            {
                'URI': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/GenericSpeedSpace',
                'XRange': {
                    'Min': 0.0,
                    'Max': 1.0
                }
            }
        ],
        'ZoomSpeedSpace': [],
        'Extension': None,
        '_attr_1': None,
        …
    },
    'PTZTimeout': {
        'Min': datetime.timedelta(seconds=1),
        'Max': datetime.timedelta(seconds=600)
    },
    '_value_1': [
        
    ],
    'PTControlDirection': None,
    'Extension': None,
    '_attr_1': None
}
Nyní mám zpětně teorii, že ty prázdné listy znamenají, že to ta kamera prostě neumí. Ale co by měla umět je AbsolutePanTiltPositionSpace! Jenom k tomu dohledat, co že je to klíčové slovo, které se má někde vyplnit? Kupodivu to je AbsoluteMove.
# 
    positionrequest = ptz.create_type('AbsoluteMove')
    positionrequest.ProfileToken = media_profile.token

    if positionrequest.Position is None:
        positionrequest.Position = ptz.GetStatus({'ProfileToken': media_profile.token}).Position
        positionrequest.Position.PanTilt.space = ptz_configuration_options.Spaces.AbsolutePanTiltPositionSpace[0].URI
#        positionrequest.Position.Zoom.space = ptz_configuration_options.Spaces.AbsoluteZoomPositionSpace[0].URI
    if positionrequest.Speed is None:
        positionrequest.Speed = ptz.GetStatus({'ProfileToken': media_profile.token}).Position
        positionrequest.Speed.PanTilt.space = ptz_configuration_options.Spaces.PanTiltSpeedSpace[0].URI

    request = positionrequest
    request.Position.PanTilt.x = x
    request.Position.PanTilt.y = y
    ptz.AbsoluteMove(request)
Z velké části opsáno z FalkTannhaeuser/python-onvif-zeep. Ten Zoom je třeba zakomentovat/odstranit, protože C500 žádný zoom nemá. A výsledek po mini wrapperu a volání z konzole:
(tapo) md@md-ThinkPad-P50:~/git/pytapo$ python patapo.py 0.5 -0.5
(tapo) md@md-ThinkPad-P50:~/git/pytapo$ python patapo.py -0.4 -0.5
(tapo) md@md-ThinkPad-P50:~/git/pytapo$ python patapo.py 0 -0.7
… viz noční video – aréna je přípravena!