大疆上云API示例代码部署小记
最近接手了一个项目,在不增加新的预算的前提下,实现将大疆无人机里拍摄的照片自动上传到服务器。
上其官网研究了一翻,再加上几个好友的沟通,目前已知有两种方法,一种是使用MSDK,自己开发一个APP安装到手柄上,自行实现照片下载至手柄与上传至服务器。第二种则是使用上云API,自己开发服务端,按协议要求实现指定接口实现图传。两种方案的差异可由下图所示。由于大疆已提供服务端样例,加之目前需求非常简单,故采用方案二。
一、准备上云API
首先,上大疆官网注册,先注册开发者账号,再注册APP,过程不详述,最后可得到APP的ID、KEY、License,备用。
对于我来讲,使用docker模式更容易实现产品部署。首先,在服务器上下载样例包。
axel -n 20 https://terra-sz-hc1pro-cloudapi.oss-cn-shenzhen.aliyuncs.com/c0af9fe0d7eb4f35a8fe5b695e4d0b96/docker/cloud_api_sample_docker.zip
解压至同名目录
unzip cloud_api_sample_docker.zip
二、编排Minio
目前上云API是将图片存储在对像存储器中,私有化方案目前只能选择minio。本处采用docker-compose方式布署。首先将docker源更新为国内源,加快拉取速度。打开配置文件
sudo ne /etc/docker/daemon.json
将国内常见的源地址加入
{
"data-root": "/opt/docker",
"registry-mirrors": ["https://hub-mirror.c.163.com/", "https://dockerproxy.com/", "https://quay.mirrors.ustc.edu.cn"]
}
最后,更新系统环境变量并重启
sudo systemctl daemon-reload
sudo systemctl restart docker
打开解压后根目录下现有编排文件 docker-compose.yml ,将minio段添加进入,并加入相同网桥与启动依赖
services: cloud_api_sample: depends_on: - minio minio: image: "minio/minio:latest" ports: - "9000:9000" - "9001:9001" volumes: - "/opt/minio/data:/data1" - "/opt/minio/config:/data2" environment: - MINIO_ROOT_USER=minioadmin - MINIO_ROOT_PASSWORD=minioadmin command: server --console-address ":9001" /data1 hostname: cloud_api_sample_minio networks: - cloud_service_bridge
三、部署官网组件
将根目录的镜像加载至docker中,里面包含了mysql、redis等组件
sudo docker load < cloud_api_sample_docker_v1.1.0.tar
修改后端配置文件 cloud_api_sample/source/backend_service/sample/src/main/resources/application.yml ,为mysql、redis、mqtt配置访问ip。docker-compose编排文件中使用桥接方式指定的网段为 192.168.6.0/24 ,所以宿主机分配的ip为 192.168.6.1,为cloud-api配置前面申请的id、key、license。oss启用minio,其endpoint需配成宿主机固有的ip。由于不存在直播需求,将livestream中的gb28181配置节注释,否则系统启动报错。
spring: datasource: druid: url: jdbc:mysql://192.168.6.1:3306/cloud_sample?useSSL=false&allowPublicKeyRetrieval=true redis: host: 192.168.6.1 mqtt: BASIC: host: 192.168.6.1 DRC: host: 192.168.6.1 oss: endpoint: http://192.168.192.254:9000 cloud-api: app: id: 按需 key:按需 license: 按需 livestream: url: # gb28181: # serverIP: Please enter the server ip. # serverPort: Please enter the server port. # serverID: Please enter the server id. # agentID: Please enter the agent id. # agentPassword: Please enter the agent password. # localPort: Please enter the local port. # channel: Please enter the channel. # # # Webrtc: Only supports using whip standard
为了加快编译速度,建议将maven源修改为国内。打开 cloud_api_sample/source/backend_service/pom.xml ,增加 repositories 配置节,我实际配置为阿里源,如下所示
<project> …… <repositories> <repository> <id>aliyun</id> <name>aliyun</name> <url>https://maven.aliyun.com/repository/public/</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>aliyunPlugin</id> <name>aliyunPlugin</name> <url>https://maven.aliyun.com/repository/public/</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories> </project>
修改镜像打包文件 cloud_api_sample/source/backend_service/Dockerfile ,使用2倍线程数打包jar包,实测可加快30%的打包速度。
RUN mvn clean package -T 2C -Dmaven.test.skip=true
完成上述修改后,在解压根目录下执行后台编译命令
./update_backend.sh
修改前端配置文件 cloud_api_sample/source/nginx/front_page/src/api/http/config.ts ,为license节配置前面申请的id、key、license。为http节配置客户端访问后台服务的基地址及webSocket基地址(需配成宿主机固有的ip)。目前不存在直播需求,不需配置livestreaming节。配置高德地图Web端应用key,相关知识可自行查阅。
// license appId: '按需', // You need to go to the development website to apply. appKey: '按需', // You need to go to the development website to apply. appLicense: '按需', // You need to go to the development website to apply. // http baseURL: 'http://192.168.192.254:6789/', // This url must end with "/". Example: 'http://192.168.1.1:6789/' websocketURL: 'ws://192.168.192.254:6789/api/v1/ws', // Example: 'ws://192.168.1.1:6789/api/v1/ws' // map amapKey: '按需',
为修复编译错误,打开 cloud_api_sample/source/nginx/front_page/package-lock.json ,将所有 https://registry.nlark.com/ 统一替换为 https://registry.npmmirror.com/ 。
完成上述修改后,在解压根目录下执行前台编译命令
./update_front.sh
网站默认采用8080端口访问。如服务器中已有冲突,可修改根目录下 dock-compose.yml 文件参数调整为合适端口。如下方修改为8088。
services:
nginx:
ports:
- "8088:8080"
最后,在解压根目录下执行启动命令
sudo docker-compose up -d
四、修改界面标题
在交付使用前,需要将界面上显示的如 Test 或 Demo等字样替换为客户实际名称。
打开数据库表 manage_workspace ,将workspace_name、workspace_desc、platform_name修改为实际的工作空间名、工作空间描述、平台名称,这里可影响web端的logo名称,遥控器pilot 2主界面云端名称显示,云端界面标题及界面显示。
打开前端代码文件 cloud_api_sample/source/nginx/front_page/index.html,修改第7行title标签内容,可影响web端网页标题
打开 cloud_api_sample/source/nginx/front_page/src/pages/page-web/index.html,修改第8行p标签内容,可影响web端登录界面系统名称
打开 cloud_api_sample/source/nginx/front_page/src/pages/page-pilot/pilot-index.html,修改第7行p标签内容,可影响遥控器端pilot 2登录界面系统名称。遥控器无缓存清理功能,如果使用后再更新代码,可能会出现编译后文件名依然一样,然后遥控器依然读取本地缓存的情况。这个时候可通过反复增减空格等方式修改文件然后编译,直至编译后文件名不同,并配合nginx添加请求头强制让浏览器所有缓存过期,达到更新遥控器本地缓存的目的。
五、模拟测试
首选测试minio可用性。在引入 MinIO Java SDK、Hotool工具集后,在resources/imgs下存放图片,则使用如下代码可实现文件的上传下载删除。
private static String url = "http://192.168.192.254:9000"; private static String accessKey = "minioadmin"; private static String secretKey = "minioadmin"; private static String bucketName = "cloud-bucket"; private static String regionName = "us-east-1"; try (MinioClient minioClient = MinioClient.builder() .endpoint(url).credentials(accessKey, secretKey).build(); Scanner sc = new Scanner(System.in);) { System.out.println("开始执行"); // 上传文件 for (File file : FileUtil.loopFiles("imgs")) { UploadObjectArgs upArgs = UploadObjectArgs.builder().bucket(bucketName) .object(file.getName()).filename(file.getAbsolutePath()) .build(); minioClient.uploadObject(upArgs); System.out.println("上传图片:" + file.getName()); } System.out.println("图片已上传完毕"); sc.nextLine(); // 下载文件 ListObjectsArgs listArgs = ListObjectsArgs.builder() .bucket(bucketName).recursive(true).build(); List<DeleteObject> delList = new LinkedList<>(); for (Result<Item> item : minioClient.listObjects(listArgs)) { DownloadObjectArgs downArgs = DownloadObjectArgs.builder() .bucket(bucketName).object(item.get().objectName()) .filename(item.get().objectName().replace("/", "_")) .overwrite(true).build(); minioClient.downloadObject(downArgs); delList.add(new DeleteObject(item.get().objectName())); System.out.println("下载图片:" + item.get().objectName()); } System.out.println("图片已下载完毕"); sc.nextLine(); // 删除文件 RemoveObjectsArgs reArgs = RemoveObjectsArgs.builder().bucket(bucketName).objects(delList).build(); for (Result<DeleteError> error : minioClient.removeObjects(reArgs)) { System.out.println(error.get().message()); } System.out.println("图片已删除完毕"); sc.nextLine(); System.out.println("结束执行"); }
在上云方面,使用工具连上mysql数据库,在media_file表中插入对应图片数据。以1.jpg为例,插入语句如下
INSERT INTO `cloud_sample`.`media_file` (`id`, `file_id`, `file_name`, `file_path`, `workspace_id`, `fingerprint`, `tinny_fingerprint`, `object_key`, `sub_file_type`, `is_original`, `drone`, `payload`, `job_id`, `create_time`, `update_time`) VALUES (1, '7858e36e-d62d-41dd-bee9-824521e6c810', '1.jpg', 'b', 'e3dea0f5-37f2-4d79-ae58-490af3228069', '', '', '1.jpg', NULL, 1, '3', '4', '5', 1719893248236, 1719893248236);
使用上述代码上传图片1.jpg,并在库中插入上述数据后,登录上云API,进入 Workspace -> Media Files页面,即可看到1.jpg图片数据。点击Action下的按钮如下载成功,则说明上云API与minio连通成功。
六、域名访问
上述描述均为直接使用ip访问的场景,即无人机遥控器、上云API、minio、管理端电脑均在内网。可通过如下修改,使其支持外网域名访问。
首先,修改 cloud_api_sample/source/nginx/default.conf 文件,建议所有外网请求均通过相同端口接收,然后通过不同地址转发至不同服务。如下示例中,在原有基础上, sample管理后台接口请求(注意其 proxy_pass 必须已斜杠结尾,使转发后的地址将不附带sample段), /api/v1/ws 管理ws请求,如下所示:
server { listen 8080 default_server; server_name ""; error_log /var/log/nginx/error.log; access_log /var/log/nginx/access.log main; location /sample/ { proxy_pass http://192.168.6.1:6789/; #proxy_set_header Host $host; #proxy_set_header X-Real-IP $remote_addr; #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /api/v1/ws { proxy_pass http://192.168.6.1:6789/api/v1/ws; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; #proxy_set_header Host $host; #proxy_set_header X-Real-IP $remote_addr; #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } #location /minio/ { #cloud-bucket # proxy_pass http://192.168.6.1:9000/; # proxy_set_header Host $http_host; # #proxy_set_header X-Real-IP $remote_addr; # #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #} location / { #expires -1; #add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate"; proxy_set_header X-Forwarded-For $remote_addr; root /home/vue_server/; try_files $uri $uri/ /index.html; } }
与之对应的,前端配置文件 config.ts 中,其 baseURL 与 websocketURL 需修改为域名,注意 baseURL需增加/sample/地址段用于nginx转发识别,所下所示:
baseURL: 'http://域名:端口/sample/', // This url must end with "/". Example: 'http://192.168.1.1:6789/'
websocketURL: 'ws://域名:端口/api/v1/ws', // Example: 'ws://192.168.1.1:6789/api/v1/ws'
后端配置文件 application.yml 中,将对像存储oss的 endPoint 修改为域名,将mqtt的 host 与 port修改为域名。注意,此域名需保证后台服务也能访问。由于nginx是将根目录转发到前端,非根目录的http或ws请求转发到后台,mqtt是处理tcp请求,oss(minio)不允许在endpoint中配置路径,导致上传文件的地址是在根目录与nginx有冲突,这三者需要使用不同的端口号。
mqtt:
BASIC:
host: 域名
port: 端口
oss:
endpoint: http://域名:端口
七、无人机连接
完成上述准备后,打开无人机及遥控器,使两者之间正常连接,然后打开遥控器飞控软件 DJI Pilot 2, 上云功能区默认显示Not Logged In即未登录,点击后界面将跳转至地址页,如下图所示:
在URL输入框中输入上云后台地址,格式为http://地址:端口/pilot-login,基于配置地址为域名或ip均可,然后点击右上角的connection按钮。如下图所示
界面将跳到至登录页,输入用户名与密码后点击蓝色Login按钮,如下图所示
登录成功后,遥控器会发送mqtt消息,服务端收到后会回复mqtt消息,遥控器收到后即完成连接,自动读取并显示遥控器云端SN号与无人机SN号,如下图所示:
点击Device Binding按钮,系统跳转至绑定界面,点击Bind按钮后实现无人机与云端的绑定,方便后续操作。
点击左上角的 Test Group One 按钮,可以查看各个模块的连接信息,显示绿色的 Connected 则表示已完成连接初始化。
八、无人机传图
在主界面点击Media File Upload按钮,进入图像传输配置页面,如下图所示:
打开 Auto Photo Upload开关,选择默认的Original Photo选择,实现原始图像的自动上传。(默认已打开)
当拍摄后,图库里的照片将自动传输至云端,如拍摄界面的左上侧红框显示,上下小箭头消失后即上传完成。
可在云端查看上传的照片,如照片所示
如果不想自动传输,则关闭选项后,进入图库后,点击打开需要上传的照片,将自动实现上传,右下角显示上传进度,当到达100%时,完成上传。
依然可在云端查看上传的照片,如照片所示
点击明细后可在云端查看图片
参考
centos 7 端口占用查看进程、根据进程名称查询进程信息、根据进程编号查询进程信息、杀掉进程操作
linux rhel虚拟机中如何给磁盘动态扩容(硬盘直接扩展)
Linux - 通过LVM对磁盘进行动态扩容 (Linux的逻辑卷)
docker-compose 搭建 minio 分布式对象存储 最新版(使用教程)
[docker] 浅谈Docker:网络模式及从容器内部访问宿主机的IP地址
minio报错SignatureDoesNotMatch解决方案
docker安装 MinIO对象存储(已解决 走nginx代理情况下, SignatureDesNotMatch签名不一致问题)
Nginx配置域名反向代理MQTT 配置mqtt /ws /wss访问域名连接。