ThingsBoard开源物联网平台实践:从环境搭建到数据可视化
ThingsBoard开源物联网平台实践:从环境搭建到数据可视化
- 一、背景介绍
- 1、什么是ThingsBoard?
- 2、为什么要使用ThingsBoard?
- 3、本文目的
- 二、参考链接
- 三、效果展示
- 四、详细操作步骤
- 1、安装ThingsBoard
- 2、Web登录并配置设备
- 3、安装Python客户端SDK
- 4、编写并运行设备客户端程序
- 五、数据可视化配置
一、背景介绍
1、什么是ThingsBoard?
ThingsBoard是一个开源的物联网平台,它专门用于设备管理、数据收集、处理和可视化。想象一下,你有很多物联网设备(比如智能家居设备、车辆传感器、工业机器等),这些设备会产生大量数据,ThingsBoard就是那个帮你统一管理这些设备,并让你能够查看和分析数据的"大脑"。
2、为什么要使用ThingsBoard?
- 开源免费:不需要支付昂贵的许可费用
- 易于扩展:支持从几个设备到数百万个设备的规模
- 功能完整:提供设备管理、数据监控、告警、可视化等全套功能
- 支持多种协议:MQTT、CoAP、HTTP等,适应不同设备需求
3、本文目的
本文将搭建ThingsBoard环境,注册物联网设备,并通过Python程序上报设备数据和位置信息,最后在Web界面上查看这些数据。
二、参考链接
- 官方安装指南
- Python客户端SDK文档
三、效果展示
四、详细操作步骤
1、安装ThingsBoard
为什么选择Docker安装?
Docker可以让我们快速部署应用,避免复杂的依赖和环境配置问题,特别适合开发和测试环境。
mkdir -p ~/.mytb-data && sudo chown -R 799:799 ~/.mytb-data
mkdir -p ~/.mytb-logs && sudo chown -R 799:799 ~/.mytb-logs
docker run -d -p 9090:9090 -p 7070:7070 -p 1883:1883 \-p 5683-5688:5683-5688/udp \-v ~/.mytb-data:/data \-v ~/.mytb-logs:/var/log/thingsboard \--name mytb --restart always thingsboard/tb-postgres
端口说明:
9090
:Web管理界面端口1883
:MQTT协议端口,设备通过此端口连接7070
:HTTP API端口5683-5688
:CoAP协议端口,用于低功耗设备
2、Web登录并配置设备
访问ThingsBoard管理界面:
# 在浏览器中访问(将<IP>替换为你的服务器IP)
http://<你的服务器IP>:9090# 默认登录账号
用户名:tenant@thingsboard.org
密码:tenant
设备注册流程:
- 登录后进入"设备"菜单
- 点击"+"添加新设备
- 填写设备名称和描述
- 创建设备后,点击设备详情,复制"访问令牌"
- 这个令牌相当于设备的身份证,用于验证设备身份
3、安装Python客户端SDK
什么是MQTT客户端?
MQTT是一种轻量级的物联网通信协议,我们的Python程序需要通过这个客户端库与ThingsBoard服务器通信。
pip3 install tb-mqtt-client
4、编写并运行设备客户端程序
程序功能详解:
这个Python程序主要完成以下任务:
cat > tb_client.py << 'EOF'
import sys
import os
import pyproj
sys.path.insert(0,"/opt/apollo/neo/python/cyber/python")
sys.path.insert(0,"/opt/apollo/neo/python")
import ctypes
import socket
from cyber_py3 import cyber
from modules.common_msgs.chassis_msgs import chassis_pb2
from modules.common_msgs.localization_msgs import localization_pb2
from modules.common_msgs.control_msgs import control_cmd_pb2
from tb_device_mqtt import TBDeviceMqttClient, TBPublishInfo
import argparse
import time
import numpy as np
import threading
import mathx_pi = 3.14159265358979324 * 3000.0 / 180.0
pi = 3.1415926535897932384626 # π
a = 6378245.0 # 长半轴
ee = 0.00669342162296594323 # 扁率def transformlat(lng, lat):ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * math.sqrt(math.fabs(lng))ret += (20.0 * math.sin(6.0 * lng * pi) + 20.0 *math.sin(2.0 * lng * pi)) * 2.0 / 3.0ret += (20.0 * math.sin(lat * pi) + 40.0 *math.sin(lat / 3.0 * pi)) * 2.0 / 3.0ret += (160.0 * math.sin(lat / 12.0 * pi) + 320 *math.sin(lat * pi / 30.0)) * 2.0 / 3.0return retdef transformlng(lng, lat):ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * math.sqrt(math.fabs(lng))ret += (20.0 * math.sin(6.0 * lng * pi) + 20.0 *math.sin(2.0 * lng * pi)) * 2.0 / 3.0ret += (20.0 * math.sin(lng * pi) + 40.0 *math.sin(lng / 3.0 * pi)) * 2.0 / 3.0ret += (150.0 * math.sin(lng / 12.0 * pi) + 300.0 *math.sin(lng / 30.0 * pi)) * 2.0 / 3.0return retdef wgs84togcj02(lng,lat):"""WGS84转GCJ02(火星坐标系):param lng:WGS84坐标系的经度:param lat:WGS84坐标系的纬度:return:列表"""dlat = transformlat(lng - 105.0, lat - 35.0)dlng = transformlng(lng - 105.0, lat - 35.0)radlat = lat / 180.0 * pimagic = math.sin(radlat)magic = 1 - ee * magic * magicsqrtmagic = math.sqrt(magic)dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi)dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * pi)mglat = lat + dlatmglng = lng + dlngreturn [mglng, mglat]class ApolloStatus(ctypes.Structure):_pack_ = 4 # 4字节对齐_fields_ = [("speed_mps", ctypes.c_double),("odometer_m", ctypes.c_double),("throttle_percentage", ctypes.c_double),("brake_percentage", ctypes.c_double),("steering_percentage", ctypes.c_double),("x", ctypes.c_double),("y", ctypes.c_double),("heading", ctypes.c_double),("engine_started", ctypes.c_int),("parking_brake", ctypes.c_int),("low_beam_signal", ctypes.c_int),("left_turn_signal", ctypes.c_int),("right_turn_signal", ctypes.c_int),("driving_mode", ctypes.c_int),("error_code", ctypes.c_int),("battery_soc_percentage", ctypes.c_int),("horn", ctypes.c_int)]g_status = ApolloStatus()def chassis_callback(msg):global g_statusg_status.engine_started=msg.engine_startedg_status.speed_mps=msg.speed_mpsg_status.odometer_m=msg.odometer_mg_status.throttle_percentage=msg.throttle_percentageg_status.brake_percentage=msg.brake_percentageg_status.steering_percentage=msg.steering_percentageg_status.parking_brake=msg.parking_brakeg_status.low_beam_signal=msg.low_beam_signalg_status.left_turn_signal=msg.left_turn_signalg_status.right_turn_signal=msg.right_turn_signalg_status.driving_mode=msg.driving_modeg_status.error_code=msg.error_codeg_status.battery_soc_percentage=msg.battery_soc_percentageg_status.horn=msg.horndef pos_callback(msg):global g_statusx=msg.pose.position.xy=msg.pose.position.yheading=msg.pose.headingprojector2 = pyproj.Proj(proj='utm', zone=50, ellps='WGS84')lon, lat = projector2(x, y, inverse=True)x,y=wgs84togcj02(lon, lat)g_status.x=xg_status.y=yg_status.heading=headingclient=None
def monitor_function():global g_statuswhile True:global clienttelemetry = {"engine_started": g_status.engine_started,"speed_mps": g_status.speed_mps,"throttle_percentage": g_status.throttle_percentage,"brake_percentage":g_status.brake_percentage,"steering_percentage":g_status.steering_percentage,"latitude":g_status.y,"longitude":g_status.x}telemetry_with_ts = {"ts": int(round(time.time() * 1000)), "values": {"battery_soc_percentage": g_status.battery_soc_percentage}}client.send_telemetry(telemetry_with_ts)client.send_telemetry(telemetry)time.sleep(5)def on_server_side_rpc_request(request_id, request_body):global clientprint(request_id, request_body)if request_body["method"] == "getValue":client.send_rpc_reply(request_id, {"value": True})elif request_body["method"] == "setValue":value=request_body['params']print(value)client.send_rpc_reply(request_id, {"value": True})def main():parser = argparse.ArgumentParser(description='网络延迟测试客户端')parser.add_argument('--host', default='localhost', help='服务器地址 (默认: localhost)') parser.add_argument('--token', default='', help='')args = parser.parse_args()cyber.init()node = cyber.Node("tb_client")node.create_reader("/apollo/canbus/chassis",chassis_pb2.Chassis,chassis_callback)node.create_reader("/apollo/localization/pose",localization_pb2.LocalizationEstimate,pos_callback)global clientclient = TBDeviceMqttClient(args.host, username=args.token)client.set_server_side_rpc_request_handler(on_server_side_rpc_request)client.connect()thread = threading.Thread(target=monitor_function)thread.start()node.spin()cyber.shutdown()if __name__ == "__main__":main()
EOF
python3 tb_client.py --host <服务器IP> --token=<获取的令牌>
代码说明:
- 订阅Apollo消息: 获取车辆信息
- 设备连接:使用令牌连接到ThingsBoard
- 数据上报:定期发送设备状态数据
- RPC处理:响应服务器的远程控制命令
- 多线程:使用独立线程处理数据上报
五、数据可视化配置
-
进入ThingsBoard的仪表板菜单
- 点击左侧菜单的"仪表板"
- 点击"+"创建新仪表板
-
添加数据可视化组件
- 点击"编辑仪表板"
- 添加各种部件:
- 数字/图表:显示速度、电量等数据
- 地图:显示设备位置
- 开关:远程控制设备
-
配置数据源
- 选择对应的设备和遥测数据
- 设置刷新间隔