项目背景与阶段
本项目基于 OpenHarmony 物联网芯片平台 Hi3863 为核心控制单元,设计了一款面向宠物户外追踪与健康监测的低功耗可穿戴设备。硬件端集成了独立 GNSS 定位模组、六轴惯性测量单元(IMU)、接触式高精度 NTC 体温传感器以及 4G 蜂窝通信模块。系统完整覆盖了从传感器端到底层固化、从通信链路到云端解析、从数据聚合到 App 展示的全链路。
项目于 2026 Q2 参加广东省大学生计算机设计大赛——基于开源鸿蒙的智能宠物关怀产品开发挑战赛。同全省 172 支队伍角逐赛场,因非自然不可抗力最终仅差一名未能入围一等奖(5%),在172支队伍中排名第 10。
一、硬件平台与传感器选型
1.1 主控芯片:Hi3863
海思 Hi3863 是一款面向物联网中低端应用的 RISC-V 内核无线 SoC,集成 2.4GHz Wi-Fi(802.11 b/g/n)与 BLE 5.2,芯片内置 PA 与 RF 匹配网络。Hi3863 的 RAM 上限大约在 200KB 量级,不足以运行完整的 libmosquitto 或 paho-embedded-c 的运行时栈——这直接决定了下文通信方案的选择方向。
1.2 传感器组合方案
定位传感器选用了独立 GNSS 模组,而非蜂窝基站定位或 Wi-Fi 指纹定位。蜂窝基站定位在城市楼群间的精度大约在 50-300 米范围,Wi-Fi 指纹定位在室内尚可但进入无 Wi-Fi 覆盖的户外区域就完全失效——而本项目覆盖的核心场景恰恰包括猫在开阔户外活动时的轨迹精度。独立 GNSS 的冷启动首次定位时间约 30-45 秒,热启动约 2-5 秒,考虑到设备并非实时连续定位(每次上报是间歇性的),冷启动的延迟是可以接受的。
运动感知采用六轴 IMU(三轴加速度 + 三轴角速度陀螺仪)。IMU 的工作分成两个独立用途:一是作为计步和运动状态识别的主要数据源,二是作为低功耗唤醒的触发源——设备处于深睡状态时,CPU 休眠、4G 断电,IMU 保持微安级电流持续检测加速度变化,当 delta_g 超出阈值(约 1.2 m/s²)时通过硬件中断唤醒主控。体温传感器采用 NTC 接触式方案,通过热敏电阻的分压值计算体温。NTC 的精度约 ±0.3°C,响应速度取决于与被测表面的接触紧密度。作为参考,环境温度传感器独立于 NTC,用于计算温湿度和辅助修正 NTC 的接触误差。
通信采用了 4G Cat.1 模块,而非 NB-IoT 或 Cat.M——因为设备需要支持实时双向通信(设备上报 + 云端下发配置),NB-IoT 的下行延迟通常在 5-15 秒范围,不满足配置修改的低延迟需求。Cat.1 的下行延迟在 1-2 秒内,但峰值功耗需要通过整机功耗管理策略来约束。
1.3 pet_status 表:硬件数据的直接映射
pet_status 表是硬件端传感器数据在数据库中的直接镜像。经度 latitude 字段使用 decimal(10,7) 精度——小数点后 7 位对应约 0.01 米的平面定位精度,足以涵盖 GPS C/A 码的民用精度上限。运动状态 motion_status 字段用 tinyint 编码:2 表示静止/睡眠,1 表示行走/轻度运动,3 表示跳跃或剧烈运动——这个编码由 IMU 的 delta_g 和 gyro_energy 共同决定。NTC 体温与各字段具有独立的 decimal(4,1) 精度,小数点后一位对应 0.1°C 的显示精度。设备的唯一标识 device_id 作为主键,在硬件出厂时写入模块的 NVRAM,首次上报时由 Omega 端自动建档(_init_device_if_not_exists)。
二、设备通信协议设计
2.1 自定义轻量化 MQTT
Hi3863 的 RAM 不足以运行完整的 MQTT 客户端库——仅 paho-embedded-c 的 MQTT 协议编码表就超过 15KB 的固件空间占用,加上 TCP/IP 协议栈和 Wi-Fi 驱动,留给应用层的堆栈空间十分紧张。因此通信实现直接在芯片的 TCP Socket 接口上构造了 MQTT 3.1.1 的最小可行实现。设备端只实现三个报文类型:CONNECT(固定头 0x10,用于首次连接时声明 Client ID 和 Clean Session 标志)、PUBLISH(固定头 0x30,用于上传传感器数据)、PINGREQ(固定头 0xC0 00,用于维持心跳连接)。不实现 SUBSCRIBE——设备不需要订阅任何下行主题,所有配置指令由 Alpha 进程的 3 秒轮询触发,设备层面的配置更新通过设备端的拨轮物理切换后上报,由云端确认。
心跳维持使用 30 秒间隔的 PINGREQ——这个值记录在 GlobalDataManager.ts 的 keepAlive 字段中。如果云端在 30 秒内未收到设备的心跳或数据报文,将判定设备离线。连接重建的逻辑在设备端固件中实现:检测到 TCP 断开后重置 socket 句柄、重新发起 DNS 解析和 CONNECT 握手序列,间隔 3 秒重试。
2.2 数据上报报文结构
每次上报的数据报文是一个 JSON 对象,包含最多三个子结构。base 字段携带基础传感器数据:电池百分比(0-100)、环境温度(°C)、NTC 体温(°C)、环境湿度(%)。imu 字段根据当前上传模式包含不同的运动数据——Mode 0 下是三轴加速度和三轴角速度的瞬时值(各 1 个浮点数),Mode 1 下是上一个通信周期内的运动特征均值。gps 字段仅在有效定位时存在,包含经度和纬度两个浮点数。device_id 字段在所有报文中必须存在,作为 Omega 端数据路由和 pet_status 表主键写入的依据。报文经 Omega 校验和写入后,beta_status 表的各字段更新完,全量同步 payload 会经 testtopic/1 主题推送给 App。
设备上报的 JSON 报文不包含嵌套级——Omage 端的解析只有一层,不递归。这不是性能层面的考虑,而是为了在设备端代码中简化拼接:Hi3863 上用逐行 printf 拼接 JSON 字符串,嵌套意味着在循环中插入逗号和换行控制,出错概率高于单层。
2.3 设备端配置同步方式
设备端的配置项只有两个:upload_interval(上传间隔,默认 30000ms)和 upload_mode(上传模式,默认 0)。配置修改有三条路径:硬件拨轮——用户直接在设备上拨动物理开关,设备重新设置本地参数后将新的 interval 和 mode 通过 MQTT 上报,Alpha 收到后写入 devices 表并标记影子缓存;App 端设置——用户通过 App 的配置文件页面修改参数,PHP 接口写入 devices 表后 Alpha 的 3 秒轮询侦测到变更后下发;设备上线自动同步——设备首次连接或重连上线时,Alpha 的 /online handler 检测到设备上线事件后直接从 devices 表拉取当前配置并立即下发一帧。三条路径最终都回到同一张 devices 表,写入优先级由数据库的最后写入时间决定。
三、IMU 数据采集策略:双模式并行
3.1 Mode 0:瞬时值上传
Mode 0 下,IMU 在每个 3 秒周期结束时采集一次三轴加速度(ax, ay, az)和三轴角速度的值,原样编码后上传。Omage 端收到后执行完整的 PetAlgorithms.analyze_motion_latest()。该函数先计算 a = sqrt(ax²+ay²+az²) 即加速度矢量模长,再计算 delta_g = |a - 9.80665| 即与重力加速度的偏差。delta_g 大于 4.5 m/s² 或陀螺仪能量总和大于 150 时判定为跳跃或剧烈运动(status=3),delta_g 在 1.2-4.5 或陀螺仪能量在 25-150 之间判定为行走(status=1),delta_g 低于 1.2 且陀螺仪能量低于 25 判定为静止(status=2)。从 data['imu'] 字段解析到 status 赋值,全部处理在 Omage 的 on_message 回调中完成,不经过消息队列。
3.2 Mode 1:特征均值上传
Mode 1 的数据流与 Mode 0 完全不同。设备端在一个上传周期内持续采集 IMU 数据(频率高于上传间隔),在周期结束时计算五个特征值:平均 delta_g(ax 字段传输)、峰值 delta_g(ay 字段传输)、活跃占比(活跃采样点数/总采样点数,az 字段传输)、平均陀螺仪能量(gx 字段传输)和峰值陀螺仪能量(gy 字段传输)。Omage 端收到后依次完成三类计算:用 max_delta_g 和 max_gyro 做阈值判定分类运动状态;根据活跃占比和运动状态推算当前周期的活跃和休息持续时间;用 undiluted 陀螺仪能量和 delta_g 的线性组合计算动态步频,乘以活跃时长后得出周期步数。
从代码中的注释来看,Mode 1 的设计比 Mode 0 晚一个迭代版本才加入——项目初期先以 Mode 0 跑通了全链路,发现上传带宽压力超过预期后才增加了设备端预运算的逻辑。两个模式并行存在的真正原因不是技术上的取舍,而是覆盖不同的用户场景:有人愿意牺牲电量换取更精确的运动识别(Mode 0),有人希望设备充一次电用更长时间而不在乎运动细节的准确度(Mode 1)。
3.3 计步算法:IMU 推算与 GPS 距离互验
每次数据上报处理完后,Omage 会计算两个步数:IMU 推算步数和 GPS 距离步数,最终步数取较大值。IMU 侧的公式是动态步频 = (陀螺仪能量 × 0.5) + (delta_g × 10.0),结果钳制在 15-180 步/分钟区间内。系数 0.5 和 10.0 来自猫的运动数据采集和参数搜索。GPS 侧的公式是 Haversine(两次定位的距离) ÷ CAT_STRIDE(0.25m/步)。两层漂移过滤——距离小于 2 米视为噪声丢弃,大于 500 米视为定位跳点丢弃——在 Haversine 计算之前执行。这个双通道设计的隐含假设是 IMU 和 GPS 的误差来源不相关:IMU 误差来自传感器噪声和身体姿态变化,GPS 误差来自卫星信号反射和大气延迟,两者不会同时出现相同方向的偏差,取较大值比取平均值更能反映真实步数。
四、坐标处理链路:WGS-84 到 GCJ-02
硬件端上报的 GPS 坐标是 WGS-84 原值——这是 GNSS 模组直接输出的国际通用坐标系。Omage 端接收后将其原样写入 pet_status 表的 pet_lng/pet_lat 字段和 pet_data_history 轨迹表。App 端从 testtopic/1 主题收到全量同步 payload 后,在 processRawData 中进行坐标转换:调用 CoordTransform.wgs84ToGcj02(lng, lat) 将 WGS-84 转换为高德地图要求的 GCJ-02。
转换函数首先检查经纬度是否在中国大陆区域外(经度 73.66-135.05,纬度 3.86-53.55),境外时不做任何修正直接返回原值。境内坐标的计算分三部分:先计算相对于中央子午线(105°E, 35°N)的偏移量,然后通过 transformLat 和 transformLng 这两个包含正弦级数展开的非线性函数计算纬度偏移和经度偏移,最后将偏移量乘以基于当前纬度的缩放因子后叠加到原始坐标上。正弦级数的周期项分成短周期(2°、1° 周期)和长周期(20°、12° 周期)两部分——短周期模拟火星坐标系的局部偏转波动,长周期模拟中国大陆整体的大地水准面模型偏差。
将转换放在 App 端而非 Omage 端的原因是:数据库中保留 WGS-84 原值可以保留未来换地图厂商的可能性——如果后期从高德切换到 Apple Map 或 Google Map(假设它们在中国大陆可用),GCJ-02 坐标在高精度地图上的偏移量不再适用。数据库里存原值,接口层转换,是在不改动云端数据链路的前提下切换地图厂商的前提。
五、低功耗全链路设计
5.1 三级休眠递进
功耗策略分成三个递进层级。第一层是 IMU 始终运行在低功耗模式,加速度阈值检测在 IMU 内部完成。当加速度变化低于设定的 delta_g 阈值且持续超过约 2 个通信周期(6 秒),系统判定宠物处于静止状态,将上传间隔从 3 秒逐步拉长至 10 秒、30 秒甚至更长。长间隔期间 CPU 大部分时间处于 WFI(Wait For Interrupt)状态。第二层:如果静止状态持续超过系统设定的深睡门限,系统主动挂断 4G 射频和 GNSS 模组的供电——这两个模块在空闲时依然是整机功耗的主要来源(4G 模块在 RRC IDLE 状态下约 20-50mA 维持电流,GNSS 模组约 10-20mA)。断供电后只有 IMU 的硬件中断通道维持活动,IMU 本身的微安级电流是深睡模式下唯一持续的消耗。第三层:当 IMU 检测到加速度或角速度超出阈值时,产生外部中断唤醒主控,主控恢复 4G 模块供电、发起 TCP 连接、发送上线通知。从 IMU 中断产生到设备恢复在线,目标延迟控制在 3 秒以内。
5.2 Wi-Fi 嗅探与 4G 降维切换
在浅睡或活跃状态下,设备端 4G 射频处于开启状态。每次上报前,设备会执行一次 Wi-Fi 网络扫描——如果识别到已知的可信 SSID(家庭 Wi-Fi 或预设热点),立即关断 4G 模块,改用 Wi-Fi 传输。这条策略的逻辑是:Wi-Fi 的传输功耗远低于 4G Cat.1,且宠物在家庭环境中停留的时间通常占一天的大部分比例。如果在室内能用 Wi-Fi 替代 4G,一天的累计传输功耗可以下降约 60%。Wi-Fi 热点列表通过 App 端的配置页面写入设备预存。
六、设备全生命周期管理
6.1 设备绑定与认证
每台设备在出厂时在 NVRAM 中烧录了唯一的 device_id 和 product_key(PIN 码防伪验证码,存储于 devices 表)。用户首次使用 App 时扫描设备上的二维码或输入 PIN 码,调用 PHP 接口 bind_device.php 将 device_id 与用户账号关联。App 端在建立 MQTT 连接后立即发送一条 sync_request 到 app/sync/request 主题,Omega 的 sync handler 响应后从数据库拉取完整的设备状态和围栏配置,通过 testtopic/1 下发给 App。
6.2 轨迹回放实现
定位数据在 Omage 的 on_message 中写入 pet_data_history 表。该表每条记录包含设备 ID、经纬度(decimal(10,6) 精度)、电量和打点时间戳。为支持 App 端的历史轨迹回放,表上建立了 (device_id, created_at) 联合索引,PHPCI 口的 get_track.php 以 device_id 为条件、创建时间为排序字段返回定位历史点,App 端在高德地图 WebView 上按时间顺序绘制轨迹线。
6.3 电子围栏配置体系
围栏配置存储在 pet_config 表,包含围栏中心坐标(decimal(10,7) 精度)和半径(米)。每次 Omage 收到有效 GPS 数据后,立即计算当前位置到围栏中心的 Haversine 距离,大于围栏半径时将 is_inside 字段置 0。围栏修改有三条路径:App 端在地图上长按设定点后调用 PHP 接口写入数据库,Alpha 的 3 秒轮询侦测变更后下发更新;Omega 的 app/fence/update handler 接收 App 端直接通过 MQTT 发送的围栏坐标并立即更新;设备首次上线时 Omega 从数据库拉取围栏配置作为默认值。围栏触发事件不会直接产生推送——推送层由 NotificationUtils.ts 在前面判断 isInside 状态的变化来实现。
七、用户系统与设备绑定认证
7.1 注册与登录流程
users 表存储用户身份信息,字段包括 id、username、email、password_hash、token、nickname、avatar_url 和 created_at。注册流程要求同时提供用户名和邮箱:前端发起注册请求时附带 email 验证码(由 send_code.php 生成发送,存储于 verification_codes 表,记录包括 account、code、expires_at),后端校验通过后将 password 以 password_hash 算法(PHP 内置的 bcrypt 封装)加密存储后再写入 users 表。注册成功后直接生成 token 并返回——不要求用户注册后再次登录。
登录逻辑在 login.php 中完成。后端同时支持邮箱和用户名两种输入形式:通过 filter_var 检测输入格式后自动判断走 email 查询还是 username 查询。查到用户后提取其绑定的邮箱地址,校验该邮箱对应的验证码是否正确且未过期,最后用 password_verify() 校验密码哈希。三层验证全部通过后生成 16 字节随机 token(bin2hex(random_bytes(16)))写入 users 表并返回给客户端。后端不维护 session,每次接口调用由 PHP 端通过 SQL 查询 users.token 字段验证登录状态——这个 token 在绑设备和切换设备等操作中作为身份凭证。
7.2 设备绑定与权限模型
每台硬件设备在出厂时在 devices 表中预置了 device_id(唯一的硬件 ID)和 product_key(安全 PIN 码),设备外壳或说明书上印有对应的二维码。bind_device.php 的核心逻辑分四步:验证传人的 token 是否有效;用 device_id + product_key 的双重条件查询 devices 表确认设备存在于出厂库且 PIN 码正确;查询 user_devices 表确认该设备尚未被任何用户绑定(如果已绑定但属于当前用户则返回"您已经绑定过",如果属于其他用户则用脱敏后的用户名提示"请先让原拥有者解绑");验证通过后向 user_devices 表插入一条新记录并将设备绑定到该用户账户下。
user_devices 表是用户与设备之间的多对多映射,字段包括 user_id、device_id、pet_name、is_active 和 bind_time。一个用户可以绑定多台设备,同一台设备只能绑定一个用户。is_active 字段标记用户当前选中的活跃设备,用于 App 端默认打开时直接展示该设备的数据。每次切换设备时通过 set_active_device.php 将该设备的 is_active 置 1,同时将当前用户的其余设备 is_active 置 0。get_devices.php 返回该用户下所有绑定设备的名称、在线状态和基本信息。unbind_device.php 根据 token 和 device_id 删除 user_devices 表中的对应记录。
7.3 用户配置管理
update_profile.php 支持修改用户昵称(nickname)、头像地址(avatar_url)和个人简介(bio),所有修改都以 token 为身份标识。get_profile.php 通过当前 token 返回完整的用户信息。重置密码流程分两步:先由 send_code.php 发送验证码到绑定邮箱,再由 reset_password.php 验证验证码后更新 users 表的 password_hash 字段。三步接口(验证码发送 → 验证码校验 → 密码更新)各自独立,调用顺序由前端控制。
八、智能家居联动
智能家居模块独立于设备数据的主链路运行,使用 MQTT 端口 1884(设备数据主端口为 1883),两个端口间的 I/O 互不干扰。smart_home_devices 表存储所有已接入的智能设备,字段包括 device_id(标识符,如 light、fan、ac)、name(显示名称)、type(设备类型)、state(ON/OFF)和 updated_at。ha_api.php 提供四个接口:list(列举所有已注册设备)、add(新增设备)、delete(删除设备及其关联的自动化规则)、set(控制设备开关)。
控制指令的实际执行路径是 PHP 端通过 exec() 系统函数调用 /usr/bin/mosquitto_pub 向 MQTT 主机的 1884 端口发送 JSON 载荷,payload 格式为 {"state": "ON"} 或 {"state": "OFF"}。PHP 发送 MQTT 指令后直接写数据库和响应 App——MQTT 下发失败不会阻塞 App 端操作,采用 fire-and-forget 模式。智能设备与宠物数据的联动由自动化条件引擎驱动,定义在 Omega 的 evaluate_automations() 方法中。引擎以 is_active = 1 的条件扫描 automations 表,逐条判断 trigger_type(ntc_temp / temp / humidity)、operator(> / <)和 threshold 组成的触发表达式。条件满足时做去重判定——查询 smart_home_devices 表确认设备当前状态和目标状态是否一致,不一致才执行 MQTT 下发。触发后更新 automations 表的 last_triggered 时间戳,防止同一触发源在短时间内重复下发。
自动化规则通过 App 端的 AutomationPage.ets 创建和管理,App 端请求 PHP 接口写入 automations 表。规则的生命周期由 is_active 字段控制,用户可以在 App 端随时激活或停用任意一条规则而不影响其他规则运行。删除规则时 ha_api.php 会同步删除 smart_home_devices 表中对应的设备记录,保持数据一致性。每次触发的完整时序是:外设上报 → Omega 传感器解析 → evaluate_automations() 扫描 → 条件匹配 → 去重判断 → mosquitto_pub 指令 → 写入数据库 → App 收到全量同步后更新 UI。
九、AI 助手集成
AI 助理部分通过 DeepSeek V4 Pro 大模型的 API 桥接实现。App 端在 PetAiChat.ets 页面中获取用户输入的自然语言消息,通过 HTTP POST 将消息转发至 DeepSeek SDK 接口,并将当前设备的最新状态数据作为上下文附加在请求中——包括实时体温、步数、活跃时长和围栏进出状态。DeepSeek 接口返回的流式响应在 App 端逐块拼接展示,形成聊天式交互界面。
将 AI 桥接放在 App 端而非云端的原因是降低延迟——如果每一条消息都要经 HTTP → 云端 → DeepSeek → 云端 → App 的完整链路,往返时间会额外增加 500-800ms。App 端直连 DeepSeek 的 API 可以将第一次 token 返回的时间控制在 1-2 秒以内。云端不缓存任何 AI 对话记录。
