在使用 Elixir 进行 IPdome 控制时,可以使用 ExOnvif 库。
ExOnvif官方文档中未给出摄像机连续运动命令,自己按照onvif协议 Onvif协议 扩展的此项功能;
连续移动的命令 ContinuousMove,属于PTZ服务的一部分,允许客户端向网络摄像机或其他视频监控设备发送指令,以使云台持续平移(Pan)、倾斜(Tilt)、变焦(Zoom)。 这个操作让监控人员能够灵活地追踪移动的目标或调整视角,而不仅仅是跳转到预设的位置。
由以上协议可以看出,控制摄像机连续移动,需要参数2个
- ProfileToken:ProfileToken指定执行PTZ操作的配置文件标识符,必须先从设备获取有效的ProfileToken。
- Velocity:PTZ参数,PanTilt定义了平移和倾斜的速度,x轴通常对应水平方向(Pan),y轴对应垂直方向(Tilt)。正值表示正方向移动,负值反之。Zoom定义了变焦速度,值为0通常意味着不改变当前变焦状态。
程序实现步骤如下
- 初始化Device设备
- 获取Device设备的ProfileToken
- 控制设备
完整的文件:
defmodule MvOnvif.Action do
use GenServer
@moduledoc """
自定义的Onvif的部分协议
获取当前状态(exonvif)
absolute move调用摄像头到指定位置,
continuous move摄像头连续移动
调用指定预置位
停止运动
"""
import ExOnvif.Utils.XmlBuilder
import SweetXml
alias ExOnvif.Device
alias ExOnvif.Media2
import ExOnvif.Utils.ApiClient, only: [ptz_request: 4]
# 初始化device设备
defp get_device(uri) when not is_nil(uri) do
%{host: host, userinfo: userinfo} = URI.parse(uri)
[user, pw] = String.split(userinfo, ":")
Device.new("http://" <> host, user, pw);
end
defp get_device(uri) do
:error
end
# 获取profiletoken标识符
defp get_main_stream_profile_token(device) do
profiles = Media2.get_profiles(device)
case profiles do
{:ok, list} -> {:ok, hd(list).reference_token}
_ -> "something went wrong"
end
end
@spec mv_continuous_move(ExOnvif.Device.t(), ExOnvif.PTZ.AbsoluteMove.t()) :: {:ok, String.t()} | {:error, any()}
def mv_continuous_move(device, ab_m) do
body = encode(ab_m)
ptz_request(device, "ContinuousMove", body, fn resp -> :ok end )
end
# 按照xml格式编码
def encode(absolute_move) do
element(
"tptz:ContinuousMove",
[element("tptz:ProfileToken", absolute_move.profile_token),
element("tptz:Velocity", Vector.encode(absolute_move.speed))]
)
end
# 摄像头连续移动
def continuous_move_(uri, speed \\ {0.5, 0.5, 0.1}) do
with {:ok, device} <- get_device(uri),
{:ok, profile_token} <- get_main_stream_profile_token(device)
do
{x, y, z} = speed
speed = %ExOnvif.PTZ.Vector{
pan_tilt: %ExOnvif.PTZ.Vector.PanTilt{
x: x,
y: y},
zoom: z
}
abm = ExOnvif.PTZ.AbsoluteMove.new(profile_token, nil, speed)
mv_continuous_move(device, abm)
end
end
end
停止移动 参考连接
以下为连续运动请求的标准xml文件示例
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope"
xmlns:wsdl="http://www.onvif.org/ver20/ptz/wsdl"
xmlns:tt="http://www.onvif.org/ver10/schema">
<SOAP-ENV:Header>
<!-- 安全认证头 -->
<Security SOAP-ENV:mustUnderstand="true"
xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<UsernameToken>
<Username>admin</Username>
<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">
<!-- 加密密码(示例值) -->
dGhpcyBpcyBub3QgYSByZWFsIHBhc3N3b3JkIEJhc2U2NA==
</Password>
<Nonce>eUYbbbyFbcLbcByKUvbcsA==</Nonce>
<Created>2025-08-14T10:30:00.000Z</Created>
</UsernameToken>
</Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<wsdl:ContinuousMove>
<!-- 必填参数:配置集Token -->
<wsdl:ProfileToken>Profile_1</wsdl:ProfileToken>
<!-- 必填参数:运动速度向量 -->
<wsdl:Velocity>
<tt:PanTilt x="0.5" y="0.0"
space="http://www.onvif.org/ver10/tptz/PanTiltSpaces/VelocityGenericSpace"/>
<tt:Zoom x="0.0"
space="http://www.onvif.org/ver10/tptz/ZoomSpaces/VelocityGenericSpace"/>
</wsdl:Velocity>
<!-- 可选参数:超时时间(单位:秒) -->
<!-- <wsdl:Timeout>PT30S</wsdl:Timeout> -->
</wsdl:ContinuousMove>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>