GB28181平台如何接入无人机实现智能巡检?

简介: 大家都知道,无人机巡检系统,有效解决了传统巡查工作空间和时间局限问题,降低人力工作成本,有效替代人工巡检工作模式。智能巡检系统通过人工智能技术和机械智能技术完美结合,在工业等场景下,应用非常广泛。本文旨在讲如何实现无人机(如大疆无人机)数据到GB28181平台(如海康、大华、宇视等国标平台)。

大家都知道,无人机巡检系统,有效解决了传统巡查工作空间和时间局限问题,降低人力工作成本,有效替代人工巡检工作模式。智能巡检系统通过人工智能技术和机械智能技术完美结合,在工业等场景下,应用非常广泛。本文旨在讲如何实现无人机(如大疆无人机)数据到GB28181平台(如海康、大华、宇视等国标平台)。


本文以Android平台接入大疆无人机为例,首先,无人机可以通过厂商提供的接口,回调编码后的H.264/H.265数据,需要注意的是,由于GB/T28181-2016,官方规范,仅对H.264做过描述,考虑到系统通用性和尽可能的规避转码带来的性能或使用体验问题,一般建议H.264编码。


无人机的数据会上来后,可以通过编码后的数据接口,投递到JNI层,把视音频数据封装成PS包,让把PS包以负载的方式封装成RTP包,完成媒体数据的上传即可。


本文以转发的模块为例说明,无图无真相:

ad04682a388243fe8d62190b6fc4fb35.jpg

具体实现:APP启动后,我们先点击启动GB28181按钮,完成到国标平台的注册,并通过心跳机制,保持和国标平台端的通信。


当国标平台端,需要查看无人机的实时画面时,可以发送Invite,请求无人机画面,Android平台GB28181接入模块,这时启动拉取无人机回调数据,并完成数据投递,和H.264到PS到RTP的打包上传即可。

/*
* MainActivity.java
* GitHub: https://github.com/daniulive/SmarterStreaming
*/
class ButtonGB28181AgentListener implements OnClickListener {
  public void onClick(View v) {
    stopGB28181Stream();
    destoryRTPSender();
    if (null == gb28181_agent_ ) {
      if( !initGB28181Agent() )
        return;
    }
    if (gb28181_agent_.isRunning()) {
      gb28181_agent_.terminateAllPlays(true);// 目前测试下来,发送BYE之后,有些服务器会立即发送INVITE,是否发送BYE根据实际情况看
      gb28181_agent_.stop();
      btnGB28181Agent.setText("启动GB28181");
    }
    else {
      if ( gb28181_agent_.start() ) {
        btnGB28181Agent.setText("停止GB28181");
      }
    }
  }
}
//停止GB28181 媒体流
private void stopGB28181Stream() {
  if(!isGB28181StreamRunning)
    return;
  if (libPublisher != null) {
    libPublisher.StopGB28181MediaStream(publisherHandle);
  }
  if (!isRecording && !isRTSPPublisherRunning && !isPushing) {
    if (publisherHandle != 0) {
      if (libPublisher != null) {
        libPublisher.SmartPublisherClose(publisherHandle);
        publisherHandle = 0;
      }
    }
  }
  isGB28181StreamRunning = false;
}

开放的video数据投递接口如下:

/**
 * 设置编码后视频数据(H.264)
 *
 * @param codec_id, H.264对应 1
 *
 * @param data 编码后的video数据
 *
 * @param size data length
 *
 * @param is_key_frame 是否I帧, if with key frame, please set 1, otherwise, set 0.
 *
 * @param timestamp video timestamp
 *
 * @param pts Presentation Time Stamp, 显示时间戳
 *
 * @return {0} if successful
 */
public native int SmartPublisherPostVideoEncodedData(long handle, int codec_id, ByteBuffer data, int size, int is_key_frame, long timestamp, long pts);

如果还有audio的话,audio数据接口如下:

/**
 * 设置音频数据(AAC/PCMA/PCMU/SPEEX)
 *
 * @param codec_id:
 *
 *  NT_MEDIA_CODEC_ID_AUDIO_BASE = 0x10000,
 * NT_MEDIA_CODEC_ID_PCMA = NT_MEDIA_CODEC_ID_AUDIO_BASE,
 * NT_MEDIA_CODEC_ID_PCMU,
 * NT_MEDIA_CODEC_ID_AAC,
 * NT_MEDIA_CODEC_ID_SPEEX,
 * NT_MEDIA_CODEC_ID_SPEEX_NB,
 * NT_MEDIA_CODEC_ID_SPEEX_WB,
 * NT_MEDIA_CODEC_ID_SPEEX_UWB,
 *
 * @param data audio数据
 *
 * @param size data length
 *
 * @param is_key_frame 是否I帧, if with key frame, please set 1, otherwise, set 0, audio忽略
 *
 * @param timestamp video timestamp
 *
 * @param parameter_info 用于AAC special config信息填充
 *
 * @param parameter_info_size parameter info size
 *
 * @return {0} if successful
 */
public native int SmartPublisherPostAudioEncodedData(long handle, int codec_id, ByteBuffer data, int size, int is_key_frame, long timestamp,ByteBuffer parameter_info, int parameter_info_size);

其他信令交互流程前面提到很多次了,本文不再赘述,这里主要看看Invite和Ack的处理:


先看Invite处理:

@Override
  public void ntsOnInvitePlay(String deviceId, PlaySessionDescription session_des) {
    handler_.postDelayed(new Runnable() {
      @Override
      public void run() {
        MediaSessionDescription video_des = session_des_.getVideoDescription();
        SDPRtpMapAttribute ps_rtpmap_attr = video_des.getPSRtpMapAttribute();
        Log.i(TAG,"ntsInviteReceived, device_id:" +device_id_+", is_tcp:" + video_des.isRTPOverTCP()
            + " rtp_port:" + video_des.getPort() + " ssrc:" + video_des.getSSRC()
            + " address_type:" + video_des.getAddressType() + " address:" + video_des.getAddress());
        // 可以先给信令服务器发送临时振铃响应
        //sip_stack_android.respondPlayInvite(180, device_id_);
        long rtp_sender_handle = libPublisher.CreateRTPSender(0);
        if ( rtp_sender_handle == 0 ) {
          gb28181_agent_.respondPlayInvite(488, device_id_);
          Log.i(TAG, "ntsInviteReceived CreateRTPSender failed, response 488, device_id:" + device_id_);
          return;
        }
        gb28181_rtp_payload_type_  = ps_rtpmap_attr.getPayloadType();
        gb28181_rtp_encoding_name_ =  ps_rtpmap_attr.getEncodingName();
        libPublisher.SetRTPSenderTransportProtocol(rtp_sender_handle, video_des.isRTPOverUDP()?0:1);
        libPublisher.SetRTPSenderIPAddressType(rtp_sender_handle, video_des.isIPv4()?0:1);
        libPublisher.SetRTPSenderLocalPort(rtp_sender_handle, 0);
        libPublisher.SetRTPSenderSSRC(rtp_sender_handle, video_des.getSSRC());
        libPublisher.SetRTPSenderSocketSendBuffer(rtp_sender_handle, 2*1024*1024); // 设置到2M
        libPublisher.SetRTPSenderClockRate(rtp_sender_handle, ps_rtpmap_attr.getClockRate());
        libPublisher.SetRTPSenderDestination(rtp_sender_handle, video_des.getAddress(), video_des.getPort());
        if ( libPublisher.InitRTPSender(rtp_sender_handle) != 0 ) {
          gb28181_agent_.respondPlayInvite(488, device_id_);
          libPublisher.DestoryRTPSender(rtp_sender_handle);
          return;
        }
        int local_port = libPublisher.GetRTPSenderLocalPort(rtp_sender_handle);
        if (local_port == 0) {
          gb28181_agent_.respondPlayInvite(488, device_id_);
          libPublisher.DestoryRTPSender(rtp_sender_handle);
          return;
        }
        Log.i(TAG,"get local_port:" + local_port);
        String local_ip_addr = IPAddrUtils.getIpAddress(context_);
        gb28181_agent_.respondPlayInviteOK(device_id_,local_ip_addr, local_port);
        gb28181_rtp_sender_handle_ = rtp_sender_handle;
      }
      private String device_id_;
      private PlaySessionDescription session_des_;
      public Runnable set(String device_id, PlaySessionDescription session_des) {
        this.device_id_ = device_id;
        this.session_des_ = session_des;
        return this;
      }
    }.set(deviceId, session_des),0);
  }
  @Override
  public void ntsOnCancelPlay(String deviceId) {
    // 这里取消Play会话
    handler_.postDelayed(new Runnable() {
      @Override
      public void run() {
        Log.i(TAG, "ntsOnCancelPlay, deviceId=" + device_id_);
        destoryRTPSender();
      }
      private String device_id_;
      public Runnable set(String device_id) {
        this.device_id_ = device_id;
        return this;
      }
    }.set(deviceId),0);
  }

Ack后调用StartGB28181MediaStream(),开始发送大疆无人机编码后的数据到国标平台端。

@Override
public void ntsOnAckPlay(String deviceId) {
  handler_.postDelayed(new Runnable() {
    @Override
    public void run() {
      Log.i(TAG,"ntsOnACKPlay, device_id:" +device_id_);
      if (!isRecording && !isRTSPPublisherRunning && !isPushing) {
        OpenPushHandle();
      }
      libPublisher.SetGB28181RTPSender(publisherHandle, gb28181_rtp_sender_handle_, gb28181_rtp_payload_type_, gb28181_rtp_encoding_name_);
      int startRet = libPublisher.StartGB28181MediaStream(publisherHandle);
      if (startRet != 0) {
        if (!isRecording && !isRTSPPublisherRunning && !isPushing) {
          if (publisherHandle != 0) {
            libPublisher.SmartPublisherClose(publisherHandle);
            publisherHandle = 0;
          }
        }
        destoryRTPSender();
        Log.e(TAG, "Failed to start GB28181 service..");
        return;
      }
      isGB28181StreamRunning = true;
    }
    private String device_id_;
    public Runnable set(String device_id) {
      this.device_id_ = device_id;
      return this;
    }
  }.set(deviceId),0);
}

需要注意的是,可以在国标平台端发起Invite请求,到Ack完成后,才开始调用大疆无人机的接口回调H.264数据,有些型号的无人机,也可以回调编码前的yuv/nv12等格式数据,这种我们也可以处理,自己编码即可。


由于无人机的特殊性,携带经纬度信息,也可以通过GB28181位置订阅(MobilePosition)实现无人机实时位置的更新。

相关文章
|
移动开发 Java API
大疆无人机对接
本文介绍了大疆无人机对接第三方云平台的方案,包括设备对接和CloudAPI对接两种方式,重点讨论了CloudAPI对接。CloudAPI对接方案通过DJI Pilot 2或大疆机场将无人机与第三方云平台连接,实现低门槛接入,无需重复开发APP。方案优势在于让开发者更专注于业务开发,而非无人机功能适配。文章详细阐述了对接流程,包括环境准备、申请APPKey、对接流程、直播功能及获取无人机实时数据等内容,并提供了丰富的接口说明和技术支持资源。
11355 4
大疆无人机对接
|
8月前
|
机器学习/深度学习 PyTorch TensorFlow
卷积神经网络深度解析:从基础原理到实战应用的完整指南
蒋星熠Jaxonic,深度学习探索者。深耕TensorFlow与PyTorch,分享框架对比、性能优化与实战经验,助力技术进阶。
|
6月前
|
监控 Java Sentinel
Sentinel安装与集成
介绍如何切换hmall-micro项目至dev_02分支并提交代码,强调多分支并行开发模式。随后讲解Sentinel服务保护框架的安装与配置,包括控制台部署、本地运行及项目集成,实现微服务熔断降级,并通过簇点链路监控接口。
Sentinel安装与集成
|
5月前
|
安全 API 数据库
Dify 开源 LLM 应用开发平台企业级 Docker Compose 部署手册
本文为企业级 Dify 生产部署指南,聚焦 Docker Compose 方案,涵盖环境准备、安全安装、双模式部署、前后端配置及加固优化,适用于私有化与生产场景,不涉及 Kubernetes。
3655 7
|
8月前
|
人工智能 监控 并行计算
厨房食品卫生与安全检测14类数据集(18万张图片,已划分、已标注)——AI智能检测的行业实践基石
本数据集包含18万张标注图像,覆盖蟑螂、老鼠、口罩佩戴等14类厨房安全目标,专为YOLO等目标检测模型设计,助力AI实现厨房卫生智能监控,推动食品安全数字化升级。
厨房食品卫生与安全检测14类数据集(18万张图片,已划分、已标注)——AI智能检测的行业实践基石
|
存储 监控 关系型数据库
ARM 架构上基于 ProxySQL 部署 PolarDB-X 高可用集群实战指南
本指南详解在ARM64架构上基于ProxySQL部署PolarDB-X高可用集群的完整流程,涵盖环境准备、RPM安装、三节点集群配置、ProxySQL读写分离与自动故障转移设置,实现金融级高可用与读写分离,适用于分布式数据库实战部署。
572 0
|
API 开发工具 Android开发
简述大疆无人机对接
【2月更文挑战第7天】本文介绍了对接大疆无人机的主要目的,包括实时画面获取、飞行数据监测、操控飞行、媒体管理和业务功能开发等,并列举了多种开发接口如MobileSDK、UXSDK、云开发API等。重点讨论了MobileSDK在Android平台的应用,包括SDK集成步骤、直播推流和获取飞机实时数据的细节。另外,UXSDK用于加速应用开发,提供预设UI组件。上云API则简化了无人机与第三方云平台的集成,支持MQTT、HTTPS和WebSocket协议,适用于行业级无人机。对接流程涉及Pilot2和Dock的配置,以及数据传输和业务功能处理。文章还提及了如何对接多个飞机的方法。
14294 0
简述大疆无人机对接
|
传感器 人工智能 边缘计算
AI赋能油田巡检——无人机视频监控系统的技术解析
无人机油田巡检系统融合无人机硬件与AI视频监控技术,实现全域覆盖、智能分析和高效管理。通过多旋翼/固定翼无人机搭载高分辨率摄像头及传感器,采集多维数据;结合YOLOv9等算法进行异常检测,准确率高达98%。系统支持5G实时传输、边缘计算及集中化管理平台,提供可视化监控与预测性维护。基于开源框架设计,灵活扩展且成本低,大幅提升油田巡检效率与安全性。
1244 0
|
Linux
Linux 服务器下载百度网盘文件
本教程指导如何使用 `bypy` 库从百度网盘下载文件。首先通过 `pip install bypy` 安装库,接着运行 `bypy info` 获取登录链接并完成授权,最后将文件置于指定目录并通过 `bypy downdir /Ziya-13b-v1` 命令下载至本地。
1864 1
Linux 服务器下载百度网盘文件
|
网络协议 物联网 数据安全/隐私保护
物联网卡:什么是NB卡
对于物联网NB卡的操作,我们可以从多个方面进行详细阐述,包括其基本概念、选购注意事项、配置过程以及使用注意事项等。以下是对物联网NB卡操作的全面指导: