如何用 Renode 仿真运行未修改的 ArduPilot STM32 固件并查看启动心跳?
【免费下载链接】ardupilotArduPlane, ArduCopter, ArduRover, ArduSub source项目地址: https://gitcode.com/GitHub_Trending/ar/ardupilot
这个任务的目标是:不改动任何 ArduPilot 固件代码,直接在 Renode 硬件仿真器里跑一块 STM32 飞控板上的固件,等它完成启动流程后,用 MAVProxy 连上仿真出的串口,确认收到固件发出的 MAVLink 心跳(HEARTBEAT)。仓库自带的 Tools/renode 工具链覆盖了从生成板级描述、加载固件到暴露 MAVLink 串口的全过程,支持 STM32 F1、F3、F4、F7、G4、H7、L4 家族——支持范围不是板子白名单,而是由 hwdef 中的 MCU 家族驱动,用Tools/renode/run.py --list可以列出当前接受的 hwdef 目标。
以下以文档中反复使用、且有完整启动结果记录的 KakuteF4 + ArduCopter 为主路径。
确认前提条件
开始之前核对这几项:
- 你有一份可以构建的 ArduPilot 源码检出,Renode 工具位于仓库内的 Tools/renode 目录;
- 官方固件下载辅助脚本 tests/fetch_renode.sh 只发布 Linux 包(x86_64 与 aarch64),非 Linux 主机上该脚本会直接报错退出;
- 新建 SD 镜像时
run.py需要 Python 包 pyfatfs 1.1.0; - Renode 本身:基础仿真在原版 Renode 1.16.1 上即可工作。仓库建议改用
fetch_renode.sh下载的打包版本,它附带性能补丁栈(文档实测约 4 倍提速)和 USB/IP 支持。
run.py按--renode参数、$RENODE环境变量、PATH 这个顺序查找 renode 可执行文件,所以下面显式传入下载目录。
准备固件与 Renode 可执行文件
第一步是像平时一样构建目标板固件:
./waf configure --board KakuteF4 && ./waf copter产物是build/KakuteF4/bin/arducopter,这也是run.py的默认固件位置(--elf/--firmware可覆盖,接受 APJ、raw BIN、Intel HEX 或 ELF)。
第二步下载 Renode 运行时(副作用:从firmware.ardupilot.org下载并解压到build/renode/,脚本会先校验包的latest.json清单、文件尺寸和 SHA-256 再解压):
Tools/renode/tests/fetch_renode.sh注意该脚本在build/renode已存在时会报错退出,不会覆盖已有目录;确认成功后它会打印 Renode 源码 revision 与可执行文件路径,并运行--version。
启动仿真并观察启动过程
从仓库根目录运行:
Tools/renode/run.py KakuteF4 --renode build/renode/renode这一条命令会做几件事:调用 ArduPilot 生产环境的 ChibiOS hwdef 编译器展开KakuteF4的hwdef.dat/hwdef-bl.dat,在build/KakuteF4/renode/generated/下生成板级 REPL/RESC(文档说明该过程不到一秒);把固件和参数存储加载进仿真;然后进入交互式 Renode monitor,此时固件已经在运行,SERIAL_ORDER中的第一个硬件 UART 暴露在tcp:localhost:5762上。
几个影响后续操作的行为需要提前知道:
- 板卡状态默认持久化在仓库根目录
renode/<board>/下:sdcard.img(512 MiB FAT32 稀疏镜像)、flash.img(完整内部 flash,固件写入的参数、任务等会保留)、mcu_id.txt(随机 96 位 MCU 唯一 ID)。状态跨固件 reset 和多次run.py调用保留;移动或删除该目录即可回到擦除/出厂状态。 - 虚拟时间默认按墙钟速度限速(real-time paced);
--unthrottled则全速运行,用于基准测试和自动化回归。 - 文档给出的 KakuteF4 启动耗时数据(KakuteF4 boot benchmark,文档实测值):原版 portable 构建约 33.6 倍实时、boot 到主循环墙钟 325 秒;启用完整性能补丁栈后约 5.1 倍实时、boot 到主循环 44 秒墙钟。第一次运行时按这个量级预期等待。
连接 MAVProxy 并确认启动心跳
在另一个终端连接仿真出的串口:
mavproxy.py --master tcp:localhost:5762连接建立后,MAVProxy 应当持续收到固件发出的 HEARTBEAT 消息。文档对 KakuteF4 的标准arducopterELF 记录了完整的启动结果:参数从仿真 flash 加载、BMP280 气压计校准、ICM20689 探测与陀螺校准、RCOut 初始化、EKF3 激活并完成 tilt 对齐,随后进入主车辆循环并执行 prearm 检查(新板子会提示 "Check frame class and type",属预期残留);MAVLink 通道上可见 heartbeat、statustexts、参数读取以及车辆自己发出的 save-notify 广播。收到稳定心跳并能在 MAVProxy 中读出参数,即可认为固件已启动到主循环。
一个容易踩的坑:5762 端口的 socket 终端一次只服务第一个客户端。断开后想重连,需要重启run.py进程,而不是重连终端。
如果你不想手工接 MAVProxy,仓库提供了自动化等价物:Tools/renode/test_all.py 会扫描build/*/bin下已存在的固件,等待 MAVProxy 检测到心跳,再执行完整的param ftp参数下载并要求其非空。单块板可以这样跑:
Tools/renode/test_all.py 'KakuteF4/arducopter'自动化心跳检查的判定逻辑也见 test_crashdump.py:以recv_match(type='HEARTBEAT', blocking=True, timeout=2)接收心跳,超时则报timed out waiting for the initial MAVLink heartbeat。
常用选项与排查
--serial N:改选SERIAL_ORDER中另一个串口索引;--uart-port N改 TCP 端口(默认 5762);--uds则改用 Unix 域套接字(APM-UDS-serialN),避免占用 TCP 端口。注意:暴露哪个 UART 不改变固件参数,所选 UART 仍需在固件里配置了目标协议(如 MAVLink)才有对应流量。--exec可在启动后追加 monitor 命令;--port N用 telnet monitor 替代交互控制台。- monitor 里
echo的参数要加引号(如echo "=== x ==="):未加引号的===是语法错误,会在仿真跑完之后中止脚本,表现为 Renode 卡在提示符上、quit永远不执行,看起来像挂死。 - 已知 Renode 模型缺陷:原版
STM32_Timer在 CCR 为 0 时不触发比较,约 1/65536 概率命中,症状是虚拟时间还在走、CPU 空闲,但 MAVLink、I2C 等全部停住。run.py生成的板级脚本已内置一行SetHookBeforePeripheralWrite修复,走正常启动路径即可覆盖;自己手写 resc 时不能省这一步。 - 不要在固件运行时以读写方式在主机上挂载同一张
sdcard.img;需要主机侧检查文件系统时先停止 Renode。
后续方向
启动心跳确认之后,Tools/renode/README.md 的其余章节覆盖了与本任务相关的延伸能力:--gdb附加带 ChibiOS 线程信息的 GDB、--can桥接 DroneCAN 多播总线、--ethernet-tap把仿真 MAC 挂到主机 TAP、以及图形化启动器launch.py(板卡选择、串口/I2C/CAN 设备挂载与运行时状态面板)。
【免费下载链接】ardupilotArduPlane, ArduCopter, ArduRover, ArduSub source项目地址: https://gitcode.com/GitHub_Trending/ar/ardupilot
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考