ThingsBoard开源物联网平台实践:从环境搭建到数据可视化
ThingsBoard是一个开源物联网平台,用于设备管理、数据收集和可视化。本文介绍了从环境搭建到数据可视化的完整实践过程:首先通过Docker快速部署ThingsBoard服务,配置Web管理界面并注册设备;然后安装Python客户端SDK,编写程序模拟设备上报数据(包括位置信息和车辆状态);最后在ThingsBoard仪表盘中实现数据可视化展示。整个过程涵盖了MQTT通信、设备认证、数据上报和
ThingsBoard开源物联网平台实践:从环境搭建到数据可视化
一、背景介绍
1、什么是ThingsBoard?
ThingsBoard是一个开源的物联网平台,它专门用于设备管理、数据收集、处理和可视化。想象一下,你有很多物联网设备(比如智能家居设备、车辆传感器、工业机器等),这些设备会产生大量数据,ThingsBoard就是那个帮你统一管理这些设备,并让你能够查看和分析数据的"大脑"。
2、为什么要使用ThingsBoard?
- 开源免费:不需要支付昂贵的许可费用
- 易于扩展:支持从几个设备到数百万个设备的规模
- 功能完整:提供设备管理、数据监控、告警、可视化等全套功能
- 支持多种协议:MQTT、CoAP、HTTP等,适应不同设备需求
3、本文目的
本文将搭建ThingsBoard环境,注册物联网设备,并通过Python程序上报设备数据和位置信息,最后在Web界面上查看这些数据。
二、参考链接
三、效果展示

四、详细操作步骤
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 math
x_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.0
ret += (20.0 * math.sin(lat * pi) + 40.0 *
math.sin(lat / 3.0 * pi)) * 2.0 / 3.0
ret += (160.0 * math.sin(lat / 12.0 * pi) + 320 *
math.sin(lat * pi / 30.0)) * 2.0 / 3.0
return ret
def 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.0
ret += (20.0 * math.sin(lng * pi) + 40.0 *math.sin(lng / 3.0 * pi)) * 2.0 / 3.0
ret += (150.0 * math.sin(lng / 12.0 * pi) + 300.0 *math.sin(lng / 30.0 * pi)) * 2.0 / 3.0
return ret
def 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 * pi
magic = math.sin(radlat)
magic = 1 - ee * magic * magic
sqrtmagic = 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 + dlat
mglng = lng + dlng
return [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_status
g_status.engine_started=msg.engine_started
g_status.speed_mps=msg.speed_mps
g_status.odometer_m=msg.odometer_m
g_status.throttle_percentage=msg.throttle_percentage
g_status.brake_percentage=msg.brake_percentage
g_status.steering_percentage=msg.steering_percentage
g_status.parking_brake=msg.parking_brake
g_status.low_beam_signal=msg.low_beam_signal
g_status.left_turn_signal=msg.left_turn_signal
g_status.right_turn_signal=msg.right_turn_signal
g_status.driving_mode=msg.driving_mode
g_status.error_code=msg.error_code
g_status.battery_soc_percentage=msg.battery_soc_percentage
g_status.horn=msg.horn
def pos_callback(msg):
global g_status
x=msg.pose.position.x
y=msg.pose.position.y
heading=msg.pose.heading
projector2 = pyproj.Proj(proj='utm', zone=50, ellps='WGS84')
lon, lat = projector2(x, y, inverse=True)
x,y=wgs84togcj02(lon, lat)
g_status.x=x
g_status.y=y
g_status.heading=heading
client=None
def monitor_function():
global g_status
while True:
global client
telemetry = {"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 client
print(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 client
client = 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的仪表板菜单
- 点击左侧菜单的"仪表板"
- 点击"+"创建新仪表板
-
添加数据可视化组件
- 点击"编辑仪表板"
- 添加各种部件:
- 数字/图表:显示速度、电量等数据
- 地图:显示设备位置
- 开关:远程控制设备
-
配置数据源
- 选择对应的设备和遥测数据
- 设置刷新间隔
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)