基于MQTT的Web?獲取火柴人(骨架數據)的實時數據并播放開發指南
今天我們來探討一下獲取火柴人(骨架數據)的實時數據并播放的方法。由于筆者水平有限,難免會有出入之處,請大家批評指正。
獲取火柴人(骨架數據)的實時數據并播放的通常涉及以下步驟:
獲取MQTT oaunth token;
獲取mqttAccount相關信息;
獲取streamtoken;
獲取組ID;
建立MQTT連接;
訂閱骨架數據流:
接收骨架數據;
解析骨架數據;
渲染骨架數據;
保持連接活躍.
下面我們展開講解,每個步聚。
獲取MQTT oaunth token:
- 美國(默認)
OAuth API URL:https://oauth.altumview.com/v1.0 AltumView API URL:https://api.altumview.com/v1.0 MQTT 主機 URL:prod.altumview.com - 中國(我們應該用下面URL)
OAuth API URL:https://oauth.ailecare.cn/v1.0 AltumView API URL:https://api.ailecare.cn/v1.0 MQTT 主機 URL:beijing.altumview.com.cn - 加拿大
OAuth API URL:https://oauth.altumview.ca/v1.0 AltumView API URL:https://api.altumview.ca/v1.0 MQTT 主機 URL:prodca.altumview.ca client_id: 聯系我們獲取
grant_type:client_credentials
scope:camera:write camera:read //請求攝像頭的讀寫權限
client_secret: ********** //聯系我們獲取
Success-Response 200:
獲取mqttAccount信息:
依oauth接口文檔
調用接口
https://oauth.ailecare.cn/v1.0/token
方法:post
請求頭:
字段 | 類型 | 描述 |
---|---|---|
Content-Type | String | application/x-www-form-urlencoded |
示例:content-type:application/x-www-form-urlencoded
請求體:
字段 | 類型 | 描述 |
---|---|---|
client_id | String | Your application client id |
grant_type | String | access token grant type. authorization_code, refresh_token, or client_credentials |
client_secret可選 | String | Your application's client secret. Required if grant_type is client_credentials or refresh_token. NOTE: if using the authorization code flow, refresh token will only be returned if client secret is provided. However, refrain from storing the client_secret in public apps where the code can be exposed. |
scope可選 | String | Scopes of this request. Default: [user:read]. Options: camera:write person:write alert:write user:write group:write invitation:write room:write camera:read person:read alert:read user:read group:read invitation:read room:read person_info:write |
redirect_uri可選 | String | Required for grant_type of authorization_code. Redirect uri value, previously used when receiving authorization code |
code可選 | String | The authorization code. Required for grant_type of authorization_code. |
code_verifier可選 | String | Required for grant_type of authorization_code. Code verifier for the proivded challenge from GET login endpoint. |
refresh_token可選 | String | Required for grant_type of refresh_token. This is the refresh token retreived from the previous request. NOTE: if using the authorization code flow, refresh token will only be returned if client secret is provided. |
state可選 | String | Application state, which will return the same value in the same field during return |
device_desctiption可選 | String | Desciption of the device making the request |
必填項:
非必填項我們也填上,因為我們的grant_type是 client_credentials,所以我們client_secret也需要填上,稍后用到
響應
如何返回200,恭喜你,第一步成功了
請求成功(200)
字段 | 類型 | 描述 |
---|---|---|
status_code | Number | HTTP response code. |
success | Boolean | The status of the operation. |
message | String | The message of the operation. |
token_type | String | token type; default is "bearer" |
access_token | String | authrization code for obtain access token. |
refresh_token可選 | String | Refresh token is not included if using the client credential grant, or if client_secret is not provided in the authorization code flow |
data | Object | The data of the operation. |
is_group_owner | String | Is this user a group owner |
expires_in | Number | the token expiration time in seconds |
state | Number | The state from the request |
HTTP/1.1 200 OK{ "status_code": 200, "message": "The request has succeeded.", "success": true, "token_type": "bearer", "access_token": "12346e3babcd21c1bef3f2f12342d64087a3abcd", "refresh_token": "cfab8df1234380abcd378123412aabcdd2c41234", "expires_in": 3600, "state": "", "data": { "is_group_owner": true, "email": "de@sunsili.com", "user_id": 123 }}
上面有一個重要東西,就是
access_token
也就是我們費盡心思,寫接口要獲取的東西,有了它才進行下一步
如果請求失敗呢,請依如下說明,排除
請求失敗(4xx)
名稱 | 類型 | 描述 |
---|---|---|
InvalidRequestFieldError | The parameter is provided in invalid format. | |
error_code | Number | The error response code. |
message | String | The message of the operation. |
success | Boolean | The status of the operation. |
status_code | Number | HTTP response code. |
FeatureNotSupportedError | This feature is not supported on this Camera. |
示例:
HTTP/1.1 400 Bad Request
{
"status_code": 400,
"error_code": 6,
"message": "Invalid neccessary fields'",
"success": false
}
出錯最多的可能是用錯URL。
獲取MQTT用戶名、密碼和WSS URL。注意,MQTT會話有時間限制,需要定期更新。
依接口文檔
調用接口:
https://api.altumview.com/v1.0/mqttAccount
示例如下:
此接口需要權限: camera:write camera:read,上一個步一定申請這個權限。
請求頭
字段 | 類型 | 描述 |
---|---|---|
Authorization必需 | String | Bearer access token |
請求成功返回示例Success-Response 200:
如果返回碼為200,恭喜你又成功了一步
請求成功(200)
字段 | 類型 | 描述 |
---|---|---|
status_code必需 | Number | HTTP response code. |
success必需 | Boolean | The status of the operation. |
message必需 | String | The message of the operation. |
data必需 | Object | The data of the operation. |
wss_url必需 | String | The WSS URL. |
mqtt_account必需 | Object | The MQTT account object result. |
username必需 | String | The MQTT username connect to MQTT server |
passcode必需 | String | The MQTT passcode connect to MQTT server |
expires_at必需 | Number | The MQTT account expires epoch time in second |
legacy_subscribe_topics必需 | String[] | The legacy MQTT subscribe topics that are allowed for the account, this will be removed in the future |
subscribe_topics必需 | String[] | The MQTT subscribe topics that are allowed for the account, not in use yet |
legacy_publish_topics必需 | String[] | The legacy MQTT publish topics that are allowed for the account, this will be removed in the future |
publish_topics必需 | String[] | The MQTT publish topics that are allowed for the account, not in use yet |
HTTP/1.1 200 OK { "data":{ "mqtt_account":{ "username": "someusername", "passcode": "somepasscode", "expires_at": 1594432207, "legacy_subscribe_topics": [ "mobileClient/43A726FEE257AAAA/#", "mobileClient/200776FFFF70E05F/#", "mobileClient/2B9FBBBBDD6DAB73/#", ], "subscribe_topics": [mobileClient/160/#], "legacy_publish_topics": [ "mobile/43A726FEE2576342/#", "mobile/200776214270E05F/#", }, "publish_topics": ["mobile/160/#"] }, "wss_url": "wss://beijing.altumview.com:8084/mqtt" }, "message":"The request has succeeded.", "success":true, "status_code":200 }
此接口返回數據會告訴你:
MQTT用戶名、密碼和WSS URL, 可以訂閱的主題等信息
拿到上面信息,我們來測試一下MQTT數據流
測試MQTT數據流
首先建立MQTT連接
要用上面接口獲取的MQTT用戶名、密碼和WSS URL
我測試用的MQTTX windows客戶端(可聯系我們獲取),其他平臺測試的客戶端(需要的朋友聯系我們獲取幫助)
然后訂閱主題
使用流令牌訂閱MQTT主題,以接收骨架數據。主題格式為
mobileClient/${groupId}/camera/${serialNumber}/skeleton/${streamToken}
火柴人檢測到有人時,才推送火柴人數據到MQTT
<html> <title>Skeleton Stream Demo</title> <script src="https://docs.altumview.com/resources/js_libs/jquery.min.js"></script> <script src="https://docs.altumview.com/resources/js_libs/mqttws31.min.js" type="text/javascript"></script> <script src="https://docs.altumview.com/resources/js_libs/polyfill.min.js"></script> <script type="text/javascript" language="javascript"> /***************************************************************************************** * You must replace parameters with your own. Refer to the FAQ for more detail on how to configure them: * https://docs.altumview.com/FAQ.pdf * For demo, these settings are configured to an AltumView account on the Canadian server. * If you do not see any skeleton rendering, the sensor is no longer available. * * Last updated: March 18, 2022 by Andrew A. ******************************************************************************************/ const oauthUrl = "https://oauth.ailecare.cn/v1.0"; const apiUrl = "https://api.ailecare.cn/v1.0"; const mqttUrl = "beijing.altumview.com.cn"; const clientId = "HkJMDXEe6G1tJ66s"; const clientSecret = "zFAl2CSkB6hGdzcIwfMMRbFErh8ValC7CS9ISsbnYZyH6xZdXbltoKrVAD7lQ4Xm"; const serialNumber = "23E94A5DACD323EE"; // Use the mobile app to get the serial number const streamToken = "701406606"; // Call GET '/cameras/:id/streamtoken' endpoint to get Stream Token const groupId = 72; // Call GET '/info' endpoint to get Group ID const getCredentials = () => { $.ajax({ "type": "POST", "url": `${oauthUrl}/token`, "headers": { "Content-Type": "application/x-www-form-urlencoded" }, "data": { "client_id": clientId, "client_secret": clientSecret, "grant_type": "client_credentials", "scope": "camera:write camera:read", }, "success": function(response) { token = response.access_token; console.log("token", token) var url = `${apiUrl}/mqttAccount`; var xhr = new XMLHttpRequest(); xhr.open("GET", url); xhr.setRequestHeader("Authorization", "Bearer " + token); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { console.log(xhr.responseText) const response = JSON.parse(xhr.responseText); username = response.data.mqtt_account.username; password = response.data.mqtt_account.passcode; const canvasWidth = 960; const canvasHeight = 540; const onFailure = () => { const reconnectTimeout = 2000; console.log("Connect failed. Trying to reconnect after 2 sec"); setTimeout(MQTTConnect, reconnectTimeout); } const onMessageArrived = (message) => { const byteList = message.payloadBytes const frameNum = parseStringInt32(byteList, 0) const numPeople = parseStringInt32(byteList, 4) const people = [] for (let i = 0; i < numPeople; i++) { const pos = 8 + 152 * i; const personId = parseStringInt32(byteList, pos); const person = {}; for (let j = 0; j < 18; j++) { const x = parseStringFloat(byteList, pos + 8 + j * 4); const y = parseStringFloat(byteList, pos + 80 + j * 4); if (x && y) person[j] = new Point(x, y); } person.name = personId; people.push(person); } const canvas = document.getElementById('canvas'); if (canvas && people) { const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvasWidth, canvasHeight); people.forEach(person => { drawSkeleton(ctx, 4, person); }) } } const drawSkeleton = (ctx, lineWidth, points) => { ctx.lineWidth = lineWidth; ctx.lineCap = 'round'; let minX = 1; let minY = 1; pointPairs.forEach(pair => { const startPoint = points[pair.start]; const endPoint = points[pair.end]; if (startPoint !== undefined && endPoint !== undefined) { if (endPoint.x < minX) minX = endPoint.x; if (endPoint.y < minY) minY = endPoint.y; ctx.strokeStyle = pair.color; drawLine(ctx, startPoint.x * canvasWidth, startPoint.y * canvasHeight, endPoint.x * canvasWidth, endPoint.y * canvasHeight); } }) } function Point(x, y) { this.x = x; this.y = y; } const drawLine = (ctx, x0, y0, x1, y1) => { ctx.beginPath(); ctx.moveTo(x0, y0); ctx.lineTo(x1, y1); ctx.stroke(); } const pointPairs = [ { start: 0, end: 1, color: 'pink' }, { start: 1, end: 2, color: 'orange' }, { start: 2, end: 3, color: 'yellow' }, { start: 3, end: 4, color: 'lightYellow' }, { start: 1, end: 5, color: 'darkSalmon' }, { start: 5, end: 6, color: 'salmon' }, { start: 6, end: 7, color: 'lightSalmon' }, { start: 1, end: 8, color: 'darkTurquoise' }, { start: 8, end: 9, color: 'turquoise' }, { start: 9, end: 10, color: 'paleTurquoise' }, { start: 1, end: 11, color: 'darkRed' }, { start: 11, end: 12, color: 'red' }, { start: 12, end: 13, color: 'orange' }, { start: 0, end: 14, color: 'purple' }, { start: 14, end: 16, color: 'purple' }, { start: 0, end: 15, color: 'violet' }, { start: 15, end: 17, color: 'violet' } ] const parseStringInt32 = (stringData, startIndex) => { const t = stringData.slice(startIndex, startIndex + 4); return new DataView(t.buffer).getInt32(0, true); } const parseStringFloat = (stringData, startIndex) => { const t = stringData.slice(startIndex, startIndex + 4); return new DataView(t.buffer).getFloat32(0, true); } const onConnect = () => { console.log('connect success'); var soptions = { qos: 0 }; // Next, subscribe to this topic with the aforementioned stream token appended const subscribeTopic = `mobileClient/${groupId}/camera/${serialNumber}/skeleton/${streamToken}`; mqtt.subscribe(subscribeTopic, soptions); console.log(`subscribe to ${subscribeTopic}`); // Finally, publish the same stream token as a message to the camera in order to start streaming. You must publish this message every 45 seconds to keep streaming going. const publishTopic = `mobile/${groupId}/camera/${serialNumber}/token/mobileStreamToken`; message = new Paho.MQTT.Message(streamToken); message.destinationName = publishTopic; message.qos = 2; message.retained = false; mqtt.send(message); console.log("Connected"); const reconnectTimeout = 44000; setTimeout(MQTTConnect, reconnectTimeout); } const MQTTConnect = async (id) => { const port = 8084; console.log(`connecting to ${mqttUrl}:${port}`); mqtt = new Paho.MQTT.Client(mqttUrl, port, username); const options = { timeout: 3, onSuccess: onConnect, onFailure: onFailure, useSSL: true, userName: username, password: password }; mqtt.onMessageArrived = onMessageArrived; mqtt.connect(options); } MQTTConnect(1); } }; xhr.send(); }, "error": function(errorThrown) { alert(JSON.stringify(errorThrown.error())); } }); }</script> <body> <p>This is a demo of the Skeleton Streaming</p> <canvas id="canvas" width="960" height="540" style="background-color: black; transform: scaleX(-1)"></canvas> <script> getCredentials();</script> </body></html>
代碼概述
HTML結構:頁面包含一個<canvas>元素,用于繪制動畫。
JavaScript邏輯:
引入了jQuery、MQTT WebSocket客戶端庫和Polyfill庫。
獲取URL參數,如客戶名稱和應用類型。
動態設置畫布尺寸以適應不同屏幕。
使用Ajax請求獲取訪問令牌和MQTT賬戶信息。
連接到MQTT服務器,并訂閱特定主題以接收動畫數據。
接收到數據后,解析并在畫布上繪制火柴人動畫。
還包含了一些輔助函數,如繪制線條、解析數據等。
詳細解析
HTML頭部:
引入了必要的JavaScript庫。
設置了頁面標題。
HTML主體:
一個<div>容器包裹了一個<canvas>元素,用于顯示動畫。
JavaScript代碼:
drawSkeleton:繪制火柴人的骨架。
Point:表示點的類。
drawLine:繪制線條。
parseStringInt32和parseStringFloat:解析二進制數據。
解析接收到的數據,提取出火柴人的位置信息。
在畫布上繪制火柴人動畫。
使用獲取的用戶名和密碼連接到MQTT服務器。
訂閱特定主題以接收火柴人動畫數據。
定期發布消息以保持連接。
參數獲取:從URL中獲取客戶名稱和應用類型。
畫布尺寸設置:根據窗口大小動態調整畫布尺寸。
獲取憑證:通過Ajax請求獲取OAuth令牌和MQTT賬戶信息。
MQTT連接:
數據處理:
輔助函數:
錯誤處理
請注意,根據文檔說明,您需要每15分鐘獲取一次流令牌以保持數據流的活躍狀態。
總結
這段代碼是一個完整的在線直播演示頁面,展示了如何使用MQTT協議和Web技術(HTML、JavaScript)來實現實時動畫的展示。它涵蓋了從前端界面設計,后端數據處理的完整流程,包括網絡通信、數據解析和圖形繪制等關鍵技術。
這個過程需要您的應用程序能夠處理網絡請求、WebSocket連接、二進制數據處理和圖形渲染。您可能需要根據您應用程序的具體技術棧選擇合適的庫和工具來實現上述功能。
如果您遇到任何問題,可以參考我們API文檔或聯系技術支持獲取幫助。
了解火柴人攝像頭,請參考:【推薦好物】火柴人隱私攝像頭 AI智能行為檢測跌倒報警
請注意,這個過程需要您的應用程序能夠處理網絡請求、WebSocket連接、二進制數據處理和圖形渲染。您可能需要根據您應用程序的具體技術棧選擇合適的庫和工具來實現上述功能。如果您遇到任何問題,可以參考我們API文檔或聯系技術支持獲取幫助。
API參考文檔
火柴人攝像頭Skeleton在線OAuthAPI文檔_SUNSHINE SILICON (http://www.ssjjxcjy.cn/doc_6.html)
火柴人攝像頭Skeleton在線API文檔_SUNSHINE SILICON (http://www.ssjjxcjy.cn/doc_3.html)
開源地址
https://gitee.com/lojam/mqtt-web-stream-demo/tree/master/streamDemo
火柴人隱私保護攝像頭 AI智能行為檢測跌倒報警簡介



立即購買
在線演示:
火柴人攝像頭Skeleton在線直播演示_在線工具_光明谷科技 (sunsili.com)
這款火柴人隱私保護攝像頭內置NPU(人工智能神經網絡處理器),運行多種深度學習算法,可以檢測測人員的活動,并應用大數平臺對各種行為(躺、站、坐、彎腰)進入統計分析,從而實現跌倒風險評估。當發生緊急情況時(例如跌倒),傳感器會立即向家人或護 理人員發送報警信息。為保護隱私,傳感器通過AI算法將原始圖像計算成火柴人動畫數據,只上傳火柴人動畫數據到云平臺(APP和后臺只能查看火柴人動畫),絕不上傳原始視頻,因此火柴人傳感器可以安裝在家里的任何房間,包括臥室和浴室。火柴人動畫還是極有價值的醫療數據,可以有多種用途,如可以分析老人的健康狀況,協助事故調查和分析,改進養老機構的服務質量,幫助醫生提前發現一些疾病,例如帕金森癥、阿茲海默癥、抑郁癥等,并幫助醫生和病人進行康復治療。