基于Node-RED与reTerminal DM的工业CAN总线数据采集与边缘处理实战

发布时间:2026/8/2 13:01:37
基于Node-RED与reTerminal DM的工业CAN总线数据采集与边缘处理实战 1. 项目缘起当工业边缘计算遇上图形化编程最近在折腾一个工业数据采集与边缘控制的项目核心硬件是一块来自Seeed Studio的reTerminal DM。这玩意儿本质上是一个基于树莓派CM4计算模块的工业级边缘计算终端自带一块5英寸的触摸屏接口丰富最吸引我的是它板载了一个隔离的CAN FD接口。我的任务是把产线上几台老设备的运行状态数据通过CAN总线抓上来进行简单的逻辑处理后再通过MQTT推送到云平台。传统的做法免不了要写一堆C或者Python代码去解析CAN报文、处理数据、实现通信协议调试起来周期长界面也不友好。就在我琢磨着怎么优雅地“偷懒”时Node-RED进入了视野。Node-RED是一个基于流的低代码编程工具用拖拽节点、连线的方式就能构建应用特别适合做物联网相关的数据集成和逻辑编排。它内置了处理JSON、HTTP、MQTT等常见协议的节点社区也有大量贡献的节点包。最关键的是它能在树莓派这类资源受限的设备上原生运行通过浏览器就能进行可视化开发和监控。一个大胆的想法冒了出来能不能直接在reTerminal DM上跑Node-RED然后用它来对接CAN总线完成从数据采集、处理到上报的全流程这样一来复杂的底层通信和逻辑都交给了图形化流程我只需要关注业务规则本身。这个组合听起来很美——reTerminal DM提供坚固的硬件和关键的CAN接口Node-RED提供敏捷的软件开发和集成能力。但实际操作起来从系统准备、环境配置、CAN驱动调试到Node-RED流程设计每一步都有不少细节需要注意。网上关于这个特定组合的完整指南并不多我踩了不少坑也总结出了一套可行的方案。如果你手头也有类似的边缘设备想快速实现CAN总线数据的可视化采集与处理那么我接下来的分享或许能帮你省下不少时间。2. 硬件与系统准备为reTerminal DM打好基础工欲善其事必先利其器。在开始玩转Node-RED和CAN之前我们需要确保reTerminal DM的硬件识别和操作系统环境是就绪的。这一步看似基础却决定了后续所有操作能否顺利进行。2.1 reTerminal DM的硬件接口确认与系统选择reTerminal DM的核心是树莓派Compute Module 4CM4它通过一个定制的载板引出了丰富的工业接口。对于我们这个项目最关键的是那个绿色的3针端子它对应的是隔离的CAN FD接口CAN_H, CAN_L, GND。在动手接线前务必确认你的设备型号支持CAN有些版本可能没有并准备好对应的CAN总线连接线或适配器。操作系统方面官方为reTerminal DM提供了基于Raspberry Pi OS原Raspbian的定制镜像。我强烈建议从这里开始因为它已经包含了针对这块板子的所有硬件驱动、屏幕旋转设置和基础优化。你可以从Seeed Studio的Wiki页面找到最新的镜像文件。使用Raspberry Pi Imager工具将其烧录到一张高速的MicroSD卡建议32GB或以上Class 10或A1/A2级别中。首次启动后记得通过sudo raspi-config完成本地化设置时区、键盘布局、扩展文件系统并务必启用SSH和VNC这样后续就可以在电脑上远程操作比在小屏幕上戳来戳去方便多了。注意烧录系统后第一次启动可能比较慢系统在进行初始配置。如果屏幕长时间黑屏或卡在彩虹屏检查一下电源。reTerminal DM需要稳定的5V/3A供电供电不足会导致各种奇怪的问题。2.2 系统更新与基础依赖安装系统启动并完成初步配置后第一件事就是更新软件源和升级所有包。打开终端依次执行sudo apt update sudo apt full-upgrade -y sudo apt autoremove -y这个过程可能会花费一些时间取决于网络速度和需要更新的包数量。升级完成后建议重启一次sudo reboot以确保所有更新生效。接下来安装一些我们后续肯定会用到的工具和依赖sudo apt install -y git curl wget build-essential python3-pip net-toolsgit,curl,wget用于从网络获取代码和工具。build-essential包含GCC、make等编译工具后续安装某些Node.js模块或驱动时需要。python3-pipPython3的包管理器虽然我们主要用Node.js但有些系统工具或脚本可能需要Python。net-tools包含ifconfig等老牌网络工具调试时可能用到。2.3 CAN总线驱动与工具链安装这是核心的一步。树莓派内核通常已经包含了CAN子系统SocketCAN的支持但我们需要安装用户空间的配置和测试工具。SocketCAN是Linux内核提供的CAN协议栈它将CAN设备抽象为网络接口我们可以像操作网卡一样用ip命令来配置CAN接口用candump、cansend等工具来收发数据。安装CAN工具包sudo apt install -y can-utils安装完成后你可以通过candump --help等命令验证是否安装成功。现在我们需要让系统识别并启用reTerminal DM上的CAN控制器。这块板子使用的通常是Microchip的MCP2518FD CAN FD控制器通过SPI与CM4通信。驱动通常已经包含在树莓派内核中但需要手动配置设备树Device Tree来启用它。首先检查当前系统是否已经加载了相关驱动模块lsmod | grep mcp25 lsmod | grep can如果能看到mcp251xfd、can、can_raw等模块说明驱动已就绪。接下来需要编辑/boot/config.txt文件来启用SPI并覆盖设备树配置。使用sudo nano /boot/config.txt打开文件在文件末尾添加以下几行# Enable SPI dtparamspion # Overlay for MCP2518FD CAN FD controller on SPI0 dtoverlaymcp2518fd,spi0-0,interrupt25这里的关键是dtoverlaymcp2518fd它告诉内核在SPI0的第一个片选CS0上挂载一个MCP2518FD控制器并使用GPIO25作为中断引脚。这个引脚定义是reTerminal DM载板设计好的不要随意更改。保存文件CtrlO然后回车退出编辑器CtrlX然后重启系统sudo reboot。重启后再次登录系统。现在检查CAN接口是否已经出现ip link show你应该能看到一个名为can0的网络接口可能状态是DOWN。如果没看到检查/boot/config.txt的配置是否有误或者通过dmesg | grep -i can查看内核启动日志寻找关于CAN控制器初始化成功或失败的信息。3. Node-RED的部署与基础配置硬件和驱动搞定后我们就可以来部署本次项目的“大脑”——Node-RED了。在树莓派生态中安装Node-RED有非常便捷的方式。3.1 安装Node.js与Node-RED虽然树莓派OS可能自带了Node.js但版本可能较旧。Node-RED官方推荐使用其提供的安装脚本它会自动处理Node.js版本和Node-RED的安装。这是最稳妥的方法。在终端中执行以下命令bash (curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)这个脚本会做几件事添加NodeSource的仓库提供较新的Node.js LTS版本、安装或更新Node.js和npm、然后全局安装Node-RED。安装过程可能需要几分钟期间会询问你是否要安装为系统服务以及一些额外工具我建议都选择“是”按Y键。安装完成后Node-RED会作为系统服务nodered自动运行。你可以用以下命令管理它sudo systemctl status nodered # 查看状态 sudo systemctl stop nodered # 停止服务 sudo systemctl start nodered # 启动服务 sudo systemctl enable nodered # 设置开机自启3.2 访问与初识Node-RED界面Node-RED默认运行在1880端口。你可以在reTerminal DM本机的浏览器中访问http://localhost:1880或者从同一网络下的其他电脑访问http://reTerminal DM的IP地址:1880。第一次打开你会看到一个清爽的流编辑器界面。左侧是节点面板Palette分类摆放着各种功能的节点中间是工作区Workspace用于拖放和连接节点右侧是信息面板Sidebar用于查看节点信息、调试消息和配置。提示为了开发方便我强烈建议在电脑上通过浏览器访问reTerminal DM的Node-RED界面。reTerminal DM的5寸屏幕虽然能操作但空间有限在电脑大屏幕上拖拽节点、查看调试信息会高效得多。确保你的电脑和reTerminal DM在同一个局域网内。3.3 安装与管理节点Node-RED的强大之处在于其丰富的节点库。默认安装包含了一些基础节点但我们需要专门处理CAN总线的节点。点击右上角的菜单三条横线 - “节点管理”Manage Palette。这里有两个标签页“用户节点”User Settings和“安装”Install。在“安装”页面的搜索框中输入“can”。你会看到几个相关的节点包最常见的是node-red-contrib-can或node-red-contrib-socketcan。我使用的是node-red-contrib-socketcan因为它直接基于我们之前安装的SocketCAN驱动更底层性能也更好。找到node-red-contrib-socketcan点击旁边的“安装”按钮。安装完成后关闭对话框。回到节点面板你应该能在左侧看到一个新的分类比如“network”里面包含了can in和can out两个节点。这就是我们连接CAN总线的桥梁。4. 打通CAN总线配置与基础测试有了can in和can out节点我们终于可以让Node-RED和真实的CAN网络对话了。但在构建复杂流程之前必须确保基础的通信是通的。4.1 配置CAN接口参数在将CAN设备接入网络前我们需要先配置can0接口的参数主要是比特率波特率。CAN总线的通信速率必须与总线上其他所有节点保持一致常见的速率有125kbps、250kbps、500kbps、1Mbps等。你需要根据你的设备手册来确定正确的速率。假设我们的总线速率是500kbps。在reTerminal DM的终端中执行以下命令来配置并启动can0接口sudo ip link set can0 type can bitrate 500000 sudo ip link set can0 up第一行设置can0接口类型为can比特率为500000 bps即500kbps。第二行启动can0接口。你可以用ip -details link show can0来查看接口的详细状态确认其已UP并且比特率等参数设置正确。为了让配置在每次启动时自动生效我们可以创建一个systemd服务。但更简单的方法是将其添加到/etc/rc.local文件在exit 0之前sudo nano /etc/rc.local添加/sbin/ip link set can0 type can bitrate 500000 /sbin/ip link set can0 up保存并退出。这样系统启动后CAN接口就会自动配置好。4.2 使用can-utils进行硬件层测试在引入Node-RED之前强烈建议先用can-utils工具包进行一轮硬件层测试这能有效隔离问题到底是硬件/驱动问题还是Node-RED配置问题。打开两个终端窗口。 在第一个终端启动一个CAN报文监听器candump can0这个命令会持续监听can0接口上收到的所有CAN报文并实时打印出来。在第二个终端尝试发送一条测试报文。例如发送一个标准帧ID: 0x123数据为11 22 33 44cansend can0 123#11223344如果一切正常你在第一个终端的candump输出中应该立即看到这条报文。这证明了1CAN驱动工作正常2can0接口配置正确3物理连接接线很可能没问题。如果cansend失败或candump收不到你需要按以下顺序排查接口状态ip link show can0确认状态是UP。物理连接检查CAN_H和CAN_L是否接反是否接了终端电阻120欧姆对于总线两端的设备是必须的。比特率确认ip -details link show can0显示的比特率与总线其他设备完全一致。驱动与配置检查dmesg | grep -i can和/boot/config.txt中的配置。4.3 在Node-RED中创建第一个CAN流程硬件测试通过后我们就可以在Node-RED中构建流程了。拖拽一个can in节点到工作区双击它进行配置。Interface选择can0。Frame type根据你的设备选择Standard标准帧11位ID或Extended扩展帧29位ID。如果不确定可以先选Standard。ID (hex)这里可以填写一个具体的CAN ID来过滤接收如果留空或填*则接收所有ID的报文。初次测试建议留空。RTR远程传输请求帧通常不勾选。Mode选择Raw原始模式这样节点输出的msg.payload就是一个Buffer对象包含了完整的CAN帧数据我们需要自己解析。再拖拽一个debug节点到工作区将can in节点的输出连接到debug节点的输入。点击右上角的红色“部署”Deploy按钮。然后打开右侧的调试Debug标签页。如果总线上有数据你应该能在调试窗口看到一条条消息。每条消息的msg.payload是一个类似Buffer aa bb cc dd ...的对象。这就是原始的CAN数据。虽然能看到数据流动但还是一堆十六进制数可读性很差。接下来我们就需要学习如何解析它们。5. 解析CAN数据从原始报文到有意义的信息工业设备通过CAN总线发送的数据往往是按照特定协议封装的。常见的如CANopen、J1939或者厂家自定义的协议。解析这些数据是将原始字节转化为温度、压力、转速等工程值的关键。5.1 理解CAN报文结构一个CAN数据帧主要包含ID报文标识符决定了报文的优先级和含义。DLC数据长度码表示后面数据域有多少个字节0-8CAN FD可以更长。Data数据域最多8个字节CAN FD更多承载实际信息。假设我们收到一条ID为0x18FEF100数据为Buffer 10 20 30 40的报文。这4个字节可能代表一个32位的整数也可能是两个16位的整数或者4个独立的8位状态值。如何解释完全取决于协议定义。5.2 使用function节点进行手动解析Node-RED的function节点允许我们编写JavaScript代码来处理消息。这是最灵活的数据解析方式。假设协议规定ID0x18FEF100的报文前两个字节10 20是一个大端序Big Endian的16位无符号整数代表转速RPM。我们可以这样配置一个function节点// 将msg.payload (Buffer) 转换为可操作的字节数组 let data msg.payload; if (data.length 2) { // 读取大端序的16位无符号整数 // data[0]是高字节data[1]是低字节 let rpm (data[0] 8) | data[1]; msg.payload { canId: msg.topic, // can in节点会将ID放在msg.topic中 rawData: msg.payload.toString(hex), parsedValue: { rpm: rpm } }; } return msg;这段代码做了几件事检查数据长度是否足够。将第一个字节左移8位与第二个字节进行按位或操作得到16位整数。因为是大端序所以索引0是最高有效字节。构造一个新的msg.payload对象包含原始ID、原始数据的十六进制字符串和解析后的转速值。将can in节点连接到这个function节点再将function节点连接到debug节点。部署后调试输出就会从原始的Buffer变成结构清晰的JSON对象例如{ canId: 18FEF100, rawData: 10203040, parsedValue: { rpm: 4128 } }这样可读性就大大增强了。5.3 处理多信号与复杂协议一条CAN报文的数据域里经常打包了多个信号。例如上述报文的后面两个字节30 40可能代表温度单位是0.1°C。扩展上面的function节点代码let data msg.payload; if (data.length 4) { let rpm (data[0] 8) | data[1]; // 假设温度也是大端序16位整数单位0.1°C let tempRaw (data[2] 8) | data[3]; let temperature tempRaw / 10.0; // 转换为摄氏度 msg.payload { canId: msg.topic, rawData: msg.payload.toString(hex), parsedValue: { rpm: rpm, temperature_C: temperature } }; } return msg;对于更复杂的协议比如某个信号可能只占几个比特bit或者是有符号数、浮点数解析逻辑会更复杂。你需要仔细查阅设备的数据字典DBC文件是汽车行业描述CAN信号的标准格式。在function节点中你可以使用JavaScript的DataViewAPI或者buffer-reader这类npm模块来更精确地处理这些数据。实操心得为每个重要的CAN ID创建一个独立的function节点进行解析并在节点名称上做好标注如“解析_电机转速_0x18FEF100”。这样流程图会非常清晰后期维护和调试时一眼就能找到对应的处理逻辑。不要试图在一个巨大的function节点里处理所有ID那会变成难以维护的“屎山代码”。6. 构建数据处理与转发流程解析出有意义的数据只是第一步。在一个典型的物联网边缘应用中我们还需要对数据进行加工如过滤、聚合、计算并转发到更上层的系统如本地数据库、MQTT Broker、Web界面。6.1 数据过滤与条件触发Node-RED提供了switch节点可以根据消息的属性如msg.topic即CAN ID或msg.payload.parsedValue.rpm进行路由。例如我们只想在电机转速超过3000 RPM时触发一个报警。可以这样操作将解析转速的function节点的输出连接到一个switch节点。配置switch节点规则选择msg.payload.parsedValue.rpm条件选择gt(大于)值填写3000。switch节点会有两个输出第一个输出对应“条件为真”转速3000第二个输出对应“其他情况”。将第一个输出连接到一个function或change节点来设置报警信息然后再连接到一个用于通知的节点比如发邮件、发MQTT消息到手机App。6.2 数据聚合与定时上报设备数据往往是高频发送的如每秒100条直接全部上报到云端会造成带宽浪费和服务器压力。常见的做法是在边缘进行聚合。我们可以使用function节点配合上下文Context来求平均值。Node-RED的上下文有节点级、流程级、全局级用于存储临时数据。例如每10秒计算一次平均转速并上报拖入一个inject节点配置它每10秒触发一次。拖入一个function节点编写代码来读取、计算并清除累积的转速数据。// 初始化上下文变量 let sum context.get(rpmSum) || 0; let count context.get(rpmCount) || 0; // 当收到来自inject节点的触发时msg.payload可能是时间戳 if (msg.topic trigger) { let avgRpm count 0 ? sum / count : 0; // 准备上报的消息 msg.payload { timestamp: new Date().toISOString(), average_rpm: avgRpm }; // 重置累加器 context.set(rpmSum, 0); context.set(rpmCount, 0); return msg; } // 当收到来自CAN解析节点的数据时 else { let rpm msg.payload.parsedValue.rpm; context.set(rpmSum, sum rpm); context.set(rpmCount, count 1); // 丢弃此消息不向下传递等待inject节点触发汇总 return null; }将这个function节点的输出连接到MQTT发布节点即可实现定时聚合上报。6.3 通过MQTT上报到云端或本地服务器Node-RED内置了强大的mqtt out节点。假设我们有一个本地的Mosquitto MQTT Broker或者一个云服务商如EMQX Cloud、阿里云物联网平台的MQTT接入点。在右侧配置面板Configuration中添加一个新的MQTT Broker配置。填写服务器地址、端口、以及可选的用户名、密码。拖入一个mqtt out节点双击它选择刚才配置的Broker。在Topic字段填写发布主题例如reterminal/dm/motor/status。你可以将CAN ID或设备名编入主题方便订阅。将之前处理好的数据流比如聚合后的平均数据连接到这个mqtt out节点。在msg.payload中我们已经构造好了JSON格式的数据。mqtt out节点会将其作为消息负载发送出去。这样任何订阅了reterminal/dm/motor/status主题的MQTT客户端比如另一个Node-RED实例、手机App、数据可视化平台Grafana等都能实时收到来自reTerminal DM的边缘处理数据。6.4 创建简单的本地UI仪表盘Node-RED的另一个亮点是node-red-dashboard节点包它可以让你快速构建一个本地监控UI。安装后你会获得一系列UI节点仪表盘、图表、开关、文本框等。安装node-red-dashboard节点包。拖拽一个gauge仪表节点到工作区配置其Group和Tab用于组织UI布局设置标签为“电机转速”数值范围0-5000。将解析转速的function节点的输出连接到这个gauge节点。部署后访问http://reTerminal DM的IP地址:1880/ui你就能看到一个实时显示转速的仪表盘。这对于在现场快速验证数据非常有用。7. 进阶应用与故障排查当基础流程跑通后我们可以探索一些更进阶的应用并了解如何应对可能出现的棘手问题。7.1 双向通信通过Node-RED发送CAN指令除了监听控制设备也是常见需求。我们可以用can out节点来发送CAN指令。拖入一个inject节点将其配置为一个按钮将Payload类型改为boolean并勾选Inject once when clicked。这个节点将用于手动触发指令发送。拖入一个function节点用于构造要发送的CAN报文。假设我们要发送ID为0x100数据为01 02 03 04的指令。// 构造一个Buffer作为数据负载 msg.payload Buffer.from([0x01, 0x02, 0x03, 0x04]); // 将CAN ID赋值给msg.topic can out节点会读取这个字段 msg.topic 100; // 十六进制字符串不带0x前缀 return msg;拖入can out节点配置其Interface为can0Frame type根据指令帧类型选择。将inject节点连接到这个function节点再将function节点连接到can out节点。 点击inject节点的按钮指令就会通过CAN总线发送出去。你可以用candump can0在终端里验证是否发送成功。7.2 应对CAN总线错误与网络负载工业环境复杂CAN总线可能会遇到错误或高负载情况。错误帧处理SocketCAN接口在遇到总线错误如格式错误、ACK错误时可能会产生错误帧。默认情况下can in节点可能不会处理这些。你可以通过监听特定的CAN错误帧ID如0x1FFFFFFF范围内的ID具体取决于驱动来捕获错误并在流程中添加报警逻辑。总线负载监控高频数据可能导致总线负载过高。你可以使用can-utils中的canbusload工具来监控实时负载率。在Node-RED中也可以通过定期查询系统文件如/sys/class/net/can0/statistics/下的某些文件来估算负载但这需要更底层的操作。7.3 常见故障排查指南即使按照步骤操作你也可能会遇到问题。以下是一个快速排查清单问题现象可能原因排查步骤Node-RED中can in节点收不到数据1. CAN接口未启动或配置错误2. 比特率不匹配3. 物理连接问题线接反、终端电阻4. 节点配置错误ID过滤1.ip link show can0确认状态为UP比特率正确。2. 用candump can0在终端测试确认硬件层能收到数据。3. 检查can in节点的Frame type和ID过滤设置尝试设为*接收所有。can out节点发送失败1. 总线错误或关闭2. 发送格式错误1. 检查can0接口状态。2. 用candump监听看是否有错误帧产生。3. 检查function节点构造的msg.payload是否为Buffermsg.topic是否为正确的十六进制ID字符串。Node-RED流程部署后无反应1. 流程有语法错误2. 节点未正确连接3. Node-RED服务异常1. 检查部署时是否有错误提示通常在调试窗口。2. 检查节点之间的连线是否完整。3.sudo systemctl status nodered查看服务状态和日志。reTerminal DM重启后CAN接口失效/etc/rc.local配置未生效或启动顺序问题1. 检查/etc/rc.local文件中的命令是否正确是否有执行权限(sudo chmod x /etc/rc.local)。2. 考虑创建更可靠的systemd服务单元来配置CAN。7.4 性能优化与资源管理reTerminal DM的CM4模块性能不错但毕竟是嵌入式设备。当流程非常复杂、处理大量CAN数据时需要注意流设计优化避免在function节点中进行复杂的循环或同步阻塞操作如长时间的文件读写。对于密集计算考虑使用node-red-contrib-function-npm节点它允许你调用已安装的npm模块可能效率更高。内存与CPU监控使用top或htop命令监控Node-RED进程的资源占用。如果发现内存持续增长可能内存泄漏检查流程中是否有未正确清理的上下文变量或全局变量。使用子流程将功能独立的模块封装成子流程Subflow可以提高流程的复用性和可读性也便于管理。日志与调试合理使用debug节点但生产环境中对于高频数据流不要一直开启调试输出到侧边栏这会消耗大量资源。可以改用file节点将关键日志写入到SD卡或外接存储中。将reTerminal DM的硬件坚固性、CAN总线连接能力与Node-RED的图形化编程、快速集成优势相结合为工业边缘侧的数据采集、协议转换和轻量级控制提供了一个极具性价比和敏捷性的解决方案。从驱动配置、数据解析到逻辑编排和云端对接整个过程虽然涉及层面较多但每一步都有迹可循。最关键的是这个方案极大地降低了传统嵌入式网关开发的软件门槛让工程师能更专注于业务逻辑本身而非底层通信细节。在实际部署中确保硬件连接可靠、比特率配置准确是基础而合理设计Node-RED流程结构则是项目长期可维护性的保障。