rs485-lpc/SUMMARY.md

141 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# RS485 → Pharos LPC 灯光控制项目总结
日期:2026-10-09(2026-10-10 更新:**LPC 自治架构**——照度采集+闭环移入 LPC Lua,Ubuntu 只做监控/记录)
状态:✅ 全部验证通过(LPC 自治闭环实测收敛 484 Lux≈目标 500;网页控制台 v3 回归全过:FX/SC/BRT 经 HTTP 触发器下发、联动开关切换 lux_link_on、历史曲线含 LPC lux)
## LPC 自治架构(2026-10-10)
- 传感器、LPC 串口、Ubuntu USB-485 同一条 RS485 总线;LPC 串口改 **RS485/4800/8N1** 后由 LPC 独占主机轮询,Ubuntu 串口命令链路废弃
- 新增触发器:**#14 LuxPoll**(Real Time 高级掩码每秒)→ Output Serial hex `02030002000265F8`(Modbus 读地址 2 输入寄存器 0x0002,32 位 lux);**#15 LuxIn**(Serial Input,Hex 匹配 `020304<4c><2c>`,`<4c>` 捕获 32 位 lux 到变量 1,`<2c>` 吞 CRC)→ `lpc_modbus_lux.lua`(`lux_value=lux` 全局暴露 + `lux_link_on` 开关 + `lux_poll_s` 调节间隔闸门 + `lux_fade` 渐变 + **自适应模型闭环**:在线辨识 k=Δlux/Δlevel(EMA、限 100~10000、限幅时不采样),步长 `err/k` 一步到位、上限 20%,误差变号减半防震荡,基准亮度 `lux_cur_level` 累加避免硬件量化吞小步长、偏差>5% 重同步;替代旧 P 调节 GAIN 0.3/最小步长 2% 的死区极限环);**#16/#17 LuxLinkOn/Off**(ASCII `LLON`/`LLOFF`);**#18 LuxFadeSet**(`FAD=<4d>`,0.1s 单位→`lux_fade`)/ **#19 LuxPeriodSet**(`PER=<4d>` 1~60→`lux_poll_s`)/ **#20 LuxDeadbandSet**(`DBD=<4d>` 1~1000→`lux_deadband`,默认 1 Lux),均可 HTTP `?num=N&var=V` 触发
- **注意**:LPC Lua 直接调 `os.time()` 会中断脚本(无报错日志),须 `pcall(function() return os.time() end)` 兜底(失败退化为计数分频)
- lux 对外暴露用 **Lua 全局变量 + `GET /api/lua?variables=lux_value`**(`set_text_slot` 在 Lua 不可用;text slot 仅 HTTP PUT/GET)
- HTTP 触发器带参:`POST /api/trigger?num=N&var=V`(实测 #9 var=600→lux_target=600)
- 网页控制台 v3:`/send` 改 `POST /api/trigger`(BRT→#2、LUX→#8、LXT→#9、LX2→#10、FX→#11、SC→#12),`/luxlink` 改 #16/#17 + 读 `lux_link_on`/`lux_value`,采样器不再碰串口;`lux_sync.py`/`poll_lux.py` 停用(避免双主机抢总线)
- 实测:上传后 T1 重启,lux 0→(升光)→饱和回落→收敛 484 Lux(死区内),level 稳定 100%;LLON/LLOFF HTTP 切换正常
## 系统架构
```
照度传感器(地址2) ─┐
发送方(PC/ESP32)─┼─ RS485 总线(4800 8N1,2026-10-10 起)──> Pharos LPC 0140650 (192.168.58.109)
Ubuntu USB-485 ────┘ (LPC 为主机每秒轮询传感器;ASCII 命令帧仍可用)
│ Serial Input 触发器 + Quick Script (Lua)
├─> get_group(0):set_master_intensity() → "All Fixtures" 组
└─> 32 × Generic Conventional 8 bit 灯具 → DMX universe 1 通道 1~32
```
## Patch(2026-10-09 新增)
- Layout 1:32 个 `Generic > Conventional 8 bit` 灯具(8 列 × 4 行),自动归入组 `All Conventional 8 bit`
- Patch:灯具 1~32 → DMX universe 1 通道 1~32(`dmx:1:N`),Controller 2 占用 32/512
- 调光粒度:Group 0 `All Fixtures` 的 master_intensity 作为总调光作用于全部 32 通道(现有 BRT/LUX/LX2 脚本无需修改)
- **eDMX Pass-Through 已改为 None**(本地模式):实测直通优先级高于本地输出(外部源发黑场时本地 255 也出不来,通道显示 `=0`),二者无法叠加,只能二选一;如需恢复直通:Patch 视图 → DMX universe 1 → eDMX Pass-Through 下拉选 `1: ArtNet In`
- **灯具强度来源**:组 master_intensity 只是缩放系数,灯具自身 intensity 须由 Timeline/Override 提供;当前用 `PUT /api/override {"target":"group","num":0,"intensity":255}` 拉满。注意 **Upload 会清空 Override**,上传后需重新下发
- 输出回读:`GET /api/output?universe=dmx:1`(`"0"`=已patch,`"=0"`=外部直通,`"!255"`=override)
- `/api/fixtures` 现返回 32 个灯具;`/api/group` 返回 `All Fixtures`(num 0) + `All Conventional 8 bit`
- GUI 操作要点:Patch 视图从左侧灯具树**拖拽**灯具到通道网格格子即完成 Patch(点击/双击格子无效)
## 通信协议(ASCII 帧,`\n` 结尾)
| 帧格式 | 含义 | LPC 触发器 |
|--------|------|-----------|
| `BRT=NNN` | 手动亮度 0~100%(3 位补零) | #2 |
| `LUX=NNNNNN` | 实测照度 0~200000 Lux(6 位补零)→ 闭环自动调光 | #8 Lux Auto Dim |
| `LXT=NNNNNN` | 设定目标照度(默认 500 Lux,全局变量 `lux_target`) | #9 Lux Target Set |
| `LX2=NNNNNN` | 照度→亮度对数映射(指示模式,灯=照度计):`level=log(lux+1)/log(200001)`,0.5s 渐变 | #10 Lux Display Map |
| `FX=NN` | 灯光效果 01~04 → 启动 Timeline N(2 位数字) | #11 FXSelect |
| `SC=NN` | 场景 01~04 → 释放全部 Timeline + 组 Override 静态亮度(2 位数字) | #12 SC Select |
## FX/SC 效果与场景(2026-10-09 新增)
- **Timeline(FX)**:T1 常亮(White 0-30s 直出 255);T2 呼吸(4 个 White 块 0-8/8-16/16-24/24-30s,Fade 4/Release 4,末块 3/3);T3 闪烁(White 0-30s,Shape=Square,Base 0 / Size 255 / Period 1s);T4 缓变(White 0-30s,Fade 15/Release 15)
- **场景(SC)**:SC=01 全亮 255 / 02 半亮 128 / 03 微亮 26 / 04 全灭 0,由 `get_group_override(0):set_intensity(level, 1.0)` 实现(0-255 整数)
- **互斥逻辑**:FX 脚本先 `get_group_override(0):clear(1.0)` 再 `get_timeline(n):start_release_others()`;SC 脚本先 `release()` 全部 4 条 Timeline 再设 Override,避免互相遮蔽
- **循环播放(2026-10-10 修复)**:Timeline 播完 Length 默认 released(灯灭)。工具栏 Loop 按钮(x=358,标尺末端出循环标记)已开启但实测对控制器不生效;实际靠触发器 **#13 FXLoop**(Timeline Ended, Any → `get_timeline(tv.integer):start()`)重启实现无限循环。SC 的 release() 属 Timeline Released,不会触发 FXLoop 误重启。实测 FX=01 两次跨过 30s 边界 pos 回绕、state 恒 running、onstage 恒 true
- **照度叠加不变**:BRT/LUX/LX2 仍写 Group 0 master_intensity,作为总调光系数乘在 Timeline/Override 基础亮度之上
- Timeline GUI 操作要点:**双击右侧 Built-in 面板效果进入放置模式(画布蓝框),再单击画布空白处提交**(拖拽不可靠;落在已有块上会被拒绝);块只在选中时渲染;后块紧邻时前块 Release 字段锁定置灰(需在放置后块之前设好 Release);Ctrl+C/V 复制块无效
LX2 映射对照:1 Lux→6% / 10→19% / 100→38% / 500→51% / 1k→57% / 10k→75% / 100k→94% / 200k→100%(与 LUX 闭环相互独立,后到的帧生效)
闭环调光参数:死区 ±50 Lux,步进 = |误差|/目标 × 0.3(限幅 2%~20%),亮度限幅 5%~100%,1 秒渐变。
## 成果文件清单
### Windows 侧 `C:\Users\tonycao\work\lpc-rs485\`
| 文件 | 说明 |
|------|------|
| `rs485_verification_plan.md` | 设计/验证文档(含实测结论、通配符语法、API 要点) |
| `SETUP.md` | 搭建步骤说明(硬件接线 / Designer 项目 / Timeline / 触发器 / Ubuntu 部署) |
| `USAGE.md` | 使用说明(协议命令表 / 发送方式 / API 查询 / 常见问题) |
| `TEST_REPORT.md` | 功能测试报告(50 项全部通过,含 FX/SC 现场回归取证) |
| `SUMMARY.md` | 本文档 |
| `lpc_serial_brt.lua` | LPC 触发器 #2 脚本:BRT 手动亮度 |
| `lpc_serial_lux.lua` | LPC 触发器 #8 脚本:LUX 闭环自动调光 |
| `lpc_serial_lxt.lua` | LPC 触发器 #9 脚本:LXT 设目标照度 |
| `lpc_serial_lx2.lua` | LPC 触发器 #10 脚本:LX2 照度→亮度对数映射 |
| `lpc_serial_fx.lua` | LPC 触发器 #11 脚本:FX 效果选择(清 Override + 启 Timeline N) |
| `lpc_serial_sc.lua` | LPC 触发器 #12 脚本:SC 场景选择(释放 Timeline + Override 亮度预设) |
| `lpc_fxloop.lua` | LPC 触发器 #13 脚本:Timeline Ended → 重启该时间线(FX 循环播放) |
| `lpc_modbus_lux.lua` | LPC 触发器 #15 脚本:Modbus 照度闭环(复用 #8 逻辑 + `lux_value` 全局暴露 + `lux_link_on` 开关) |
| `lpc_web.py` | 网页控制台 v3(部署于 Ubuntu `~/rs485-lpc/`,http://192.168.58.7:8090):实时状态 + FX/SC/BRT(HTTP 触发器)+ 联动开关(#16/#17)+ 48h 历史曲线(30s 采样 LPC API,存 `history.jsonl`) |
| `lx2_test.sh` | LX2 映射回归(8 档照度→亮度对照,PASS=8) |
| `loop_test.sh` | BRT 闭环回归(5 项,PASS=5) |
| `lux_test.sh` | 照度闭环回归(7 项:死区/降光/升光/上下限幅,PASS=7) |
| `api_smoke.sh` | LPC HTTP API 冒烟测试 |
| `test_rs485.py` | Ubuntu 串口收发工具(`-l/-p/-b/-s/-w/-i`) |
| `Test-Rs485.ps1` | Windows 串口自收发测试 |
| `Verify-Rs485.ps1` | 离线 S1~S4 验证脚本(16/16 PASS,需 UTF-8 BOM) |
| `ui.ps1` | Designer 2 GUI 自动化(点击/双击/右键/拖拽/SendKeys/截图/窗口最大化) |
| `Run-Remote.ps1` | Posh-SSH 远程执行(Ubuntu 192.168.58.7) |
| `Get-Remote.ps1` | Posh-SSH SFTP 下载 |
### Designer 2 项目
| 文件 | 说明 |
|------|------|
| `0140650.pd2`(本目录) | 项目文件(触发器 #1/#2/#8-#20 含 LuxPoll/LuxIn/LuxLinkOn/Off/LuxFadeSet/LuxPeriodSet/LuxDeadbandSet + 4 条 Loop Timeline,串口 RS485 4800 8N1),已上传 LPC(2026-10-10 19:56 归档) |
### 参考文档 `docs\`
| 文件 | 说明 |
|------|------|
| `renke-illuminance-485-200k-manual.pdf` | 仁科光照度变送器(485 型 20w)使用说明书 |
| `modbus-illuminance-packet-formats.md` | 仁科/耘农 Modbus 帧格式实测笔记 |
### Ubuntu 侧(192.168.58.7,USB-485 = `/dev/ttyUSB0`)
| 路径 | 说明 |
|------|------|
| `~/rs485-lpc/loop_test.sh` | BRT 回归(与 Windows 侧同步) |
| `~/rs485-lpc/lux_test.sh` | 照度闭环回归(已部署,7/7 PASS) |
| `~/rs485-lpc/api_smoke.sh` | LPC HTTP API 冒烟测试 |
| `~/rs485-lpc_test_rs485.py` | 串口发送工具 |
## 验证记录
- 2026-10-09 离线 S1~S4:16/16 PASS
- 2026-10-09 BRT 闭环:5/5 PASS(100/0/50/80/25)
- 2026-10-09 LUX 闭环回归:死区 / 超目标降光 / 欠目标升光 / 上下限限幅 全部通过(lux_test.sh 7/7 PASS)
- 2026-10-09 LX2 照度指示映射:8 档对数映射全过(lx2_test.sh 8/8 PASS)
- 2026-10-09 Patch 32 通道(DMX 1~32)并 Upload;回归抽测 BRT=050→level 50、BRT=000→level 0 通过
- 2026-10-09 FX/SC:FX=01 常亮 255(T1 running)/ FX=02 呼吸(255→202 渐变)/ FX=03 闪烁(255/0 交替)/ FX=04 缓变(39→75→110 缓升);SC=02→128 / SC=04→0(Override 生效、Timeline 释放);FX 与 SC 互斥切换正常
- 2026-10-10 FX 循环修复:发现 FX 播完 30s 即 released 灯灭;Loop 工具栏按钮单独开启无效,加触发器 #13 FXLoop(Timeline Ended 重启)后实测 FX=01 连续 2 个循环 pos 回绕(29500→1550→…→27980→20)输出不掉 0;FX=02 跨 30s 仍 running;SC=04 释放后 35s 保持 released 无误重启
- 2026-10-10 照度功能验证:传感器在线(Modbus 地址 2 @4800,实测 26 Lux);手动 LUX 闭环升/降/死区正常(LUX=10→level 50→70,LUX=10000→50,LUX=500 死区不动);LX2=10→level≈20;lux_sync.py 端到端 26 Lux → BRT=73 → group level=73 → 输出随动
- 2026-10-10 LPC 自治改造:触发器 #14(每秒 Modbus 轮询)+#15(hex 通配符捕获+Lua 闭环)+#16/#17(联动开关)建成并上传;LPC 日志确认 #14 Matched 207 次 / #15 Captured 2 variables 244 次;闭环实测 lux 0→饱和→收敛 484(目标 500±50 死区),`/api/lua?variables=lux_value` 实时可读;HTTP 带参触发(`?num=9&var=600`)验证通过;网页控制台 v3 回归:FX=02→T2 running、BRT=060→level 60、luxlink off/on→lux_link_on false/true、history 采样含 LPC lux=479
## 关键技术结论(官方文档未载)
1. **串行通配符语法**:`<3d>` = 恰好 3 位数字(数字在前,类型 `[cCdDxXsS]`);`?` `*` `#` `%` 均为普通字符
2. **Designer 2 触发变量**:`get_trigger_variable(1)`(v1 的 `variable[1]` 已废弃)
3. **组亮度 API**:`get_group(n):set_master_intensity(level[, fade])`,读当前值用 `grp.master_intensity_level.integer / .range`
4. **LPC HTTP API 免认证**:`GET /api/group`(`level` 字段回读)、`/api/trigger`、`/api/log`
5. **LPC 自治照度闭环(2026-10-10)**:Serial Input hex 匹配串支持通配符 `<4c>`(捕获 32 位)/`<2c>`(吞 16 位 CRC);Real Time 触发器高级掩码秒级分辨率可每秒触发 Output Serial 静态 hex 帧(Modbus 轮询);lux 经 Lua 全局变量 + `GET /api/lua?variables=` 暴露(`set_text_slot` 非 Lua 函数);HTTP 触发器带参 `POST /api/trigger?num=N&var=V`。**GUI 自动化**:Serial Input/Output 的 String 框逐键输 `<` 不进(中文布局/过滤),须 Set-Clipboard + ^v 粘贴;Upload 对话框须先点选设备行再点 Upload All
6. **Timeline 循环(2026-10-10)**:工具栏三个易混按钮——Hold At End(→|)、Loop(↻ 单环,运行时循环)、Enable Looping(↻ 两点,仅编辑器仿真,不上传)。实测 Loop 按钮开启(标尺末端有循环标记、保存、上传)后控制器仍在 Length 处 released;可靠方案是 **Timeline Ended 触发器 + `tl:start()`**(Timeline Ended/Started/Released 在 Timeline=Any 时把时间线号捕获到变量 1;Ended 不在 Hold 时触发,Released 是独立类型,SC 释放不会误触发)。本地帮助:`C:\Program Files\Pharos Controls\Designer 2\help\content\help\reference\timeline\working with timelines.htm`
## 仁科照度计参考(esp32-voice 项目)
- 大量程 0~200000 Lux 型:32 位寄存器对 0x0002(hi)/0x0003(lo),单位 1 Lux
- ESP32 侧实现:`src/config.rs`(MODBUS_ILLUMINANCE_REG=0x0002, COUNT=2)、`src/modbus/master.rs` `poll_illuminance()`
- 文档:esp32-voice `docs/` 原件;本项目 `docs/` 已归档副本(20w 说明书 PDF + 帧格式笔记)