Skip to content

Debugging

konodoki edited this page Jul 26, 2026 · 26 revisions

调试、验证与常见问题

先定位故障层,再查看具体章节。 不要同时修改输入 YAML、Mod route、Transition 和状态代码。

快速定位

现象 首先检查
节点启动时 Mod 加载失败 Mod 清单、factory、依赖和参数
按键没有消息 sources -> controls -> outputs
/motion_commands 但不切状态 Mod events 和当前状态的 route
状态已切换但没有正确输出 on_enter/on_update 和 MotorFrame
切换过程跳变或报 capability 错误 Transition 与 Entry/RunningFrameProvider
修改后没有生效 是否重新构建并重启了对应节点
模型文件报错 Resource asset 路径、loader 和预热阶段

构建

colcon build --packages-select remote_controller bxi_example_py_elf3 \
  --symlink-install --merge-install
source install/setup.bash

若提示 install layout 不一致,使用匹配的 --merge-install,或换一组全新的 build/install/log 目录。旧构建缓存仍引用已删除模型目录时,只清理对应包缓存。

查看输入和状态

ros2 topic echo /motion_commands
ros2 topic echo /simulation/state_machine_info

硬件前缀时将 simulation/ 换成 hardware/。状态信息 JSON 包含 current、pending、active transition、事件和图快照;action 的显示信息位于 graph.actions[],Mod 的 loaded/unavailable/disabled 状态位于 mods[],Mod 节点的 running/stopped/restarting/faulted/unavailable 状态位于 nodes[]

Mod 没有被发现

  • mod.yaml 是否位于内置 mods/mod_paths 子树。
  • 安装后的 share/bxi_example_py_elf3/mods/ 是否包含文件。
  • 12 个描述头字段是否全部显式填写,runtime_requirements 是否包含 python/ros/systemschema/api 是否为 1,name、id 和版本是否合法。
  • 是否出现重复 id。
  • 日志是否打印 Mod <id>@<version>: <root>

Mod 加载失败

  • needs factory: module:Class:使用 entrypoint: null 的状态漏写约定 factory。
  • factory ... must name a RobotControlState class:模块、类名或继承关系错误。
  • entrypoint module does not exist:entrypoint 与文件名不一致。
  • entrypoint is not callable:冒号后的工厂不存在。
  • state manifest/factory mismatchstatesstate_factories 键不同。
  • requires missing/version:依赖缺失或版本不匹配。
  • dependency cycle:Mod 依赖形成环。
  • enabled Mods conflict:两个已启用 Mod 命中至少一方的 conflicts 声明。
  • missing Python module/ROS package/system libraryruntime_requirements 不满足;检查 Mod 自带 vendor/ 或目标机器运行环境。
  • status: unavailable:依赖检查阶段未通过,框架没有导入对应 Mod 或节点模块,也不会消耗重启次数。
  • unknown params:清单参数未被 StateBuildContext 消费。
  • missing required param / must be ...:dataclass 参数缺失或 YAML 类型错误。

模型找不到或没有加载

  • 资产必须在当前 Mod 的 assets/
  • context.asset() 路径应写 assets/file.onnx
  • 模型是惰性资源;仅加载 Mod 不会打开文件,首次 ResourceHandle.get() 才加载。
  • on_prepare() 预热,不要在插件 import 时创建推理 Session。
  • 替换模型后需要重启控制节点;启动失败时查看具体资源加载异常。

按键有输出但状态不切换

  1. /motion_commands 的槽位和值是否与 mod.yaml events 一致。
  2. 当前完整状态名是否存在对应 route。
  3. 跨 Mod名称是否含 / 且依赖存在。
  4. 是否正在等待 delay 或处于活动过渡。
  5. 同一状态是否存在可同时触发的输入;加载器会提前拒绝。

priority 与 index

正常状态只写 manifest.priority。数值越大越靠前,相同 priority 按完整状态名升序排列,框架会自动生成 index。

ValueError: duplicate explicit state manifest index N: 'state-a' and 'state-b'

只有确实需要固定绝对位置时才显式写 manifest.index。两个显式 index 重复会直接终止启动;负数、布尔值和字符串同样是配置错误。

过渡错误

Transition 负责状态切换期间的电机帧,用来避免源、目标状态的 qpos/kp/kd 不连续时直接产生突跳。因此这类错误要同时检查 route 选用的过渡以及两端状态提供的帧能力。

  • must implement EntryFrameProvider:目标没有进入帧。
  • must implement RunningFrameProvider:动态采样侧没有运行帧能力。
  • unknown transition type:业务过渡模块未被 Mod entrypoint 导入。
  • unknown fieldsConfigReader.finish() 发现字段拼写错误。
  • 发生突跳:检查进入帧与 on_enter() 后第一帧是否一致。

修改后没有生效

  • Mod、系统 YAML、模型资产和遥控器 YAML 都只在对应节点启动时读取。
  • 修改源码后先重新构建工作区,再重启控制节点或 remote_controller
  • 使用 --symlink-install 时 Python 源码可能直接生效,但仍然必须重启进程。
  • 已部署的非 symlink install/ 可以直接向 share/bxi_example_py_elf3/mods/ 复制完整 Mod 目录;重启后自动发现,不需要在目标机器重新构建。
  • 启动失败不会进入控制循环;根据首个加载异常修正清单、依赖、模块或资源路径。

发布树失败

python3 tools/sanitize_release.py --out /tmp/public_release --self-check

若报告公开 Mod 依赖 protected Mod,检查显式 requires 和跨 Mod route。共享资源应移到 public 资源 Mod。

离线加载检查

模型是惰性的,因此可以只验证 Mod 和状态图:

from pathlib import Path
import yaml
from bxi_example_py_elf3._runtime.mod_loader import load_mod_runtime

package = Path("src/bxi_example_py_elf3")
base = yaml.safe_load((package / "config/elf3_state_machine.yaml").read_text())
runtime = load_mod_runtime(base, built_in_root=package / "mods")
print([mod.id for mod in runtime.mods])
print(sorted(runtime.state_factories))
runtime.close()

当前默认树应发现 5 个 Mod 和 15 个状态。

RealSense 内置节点反复重启

com.bxi.normal_depth 的相机节点运行在独立 Python 子进程中。检查 state_machine_info.nodes[] 中的 status/restart_attempts/error,并确认:

  • python3 -c "import pyrealsense2" 能成功执行。
  • RealSense 设备可见,配置支持 480×270@60 Hz 的 Z16 深度流。
  • 没有另一个进程占用同一序列号的设备。
  • mod.yaml 中的 serial、stream 尺寸和帧率与硬件能力一致。

节点异常退出后默认最多重启 3 次;达到上限后状态变为 faulted。color、IR 和 IMU 默认关闭,需要时在 nodes.realsense_depth_publisher.params 中启用。

如果节点一开始就是 unavailable,说明依赖检查没有通过,此时不会启动或重启子进程。pyrealsense2realsense2 可以由目标机器提供,也可以分别放在当前 Mod 的 vendor/pythonvendor/lib 中。没有插入相机但依赖均存在时,节点会实际启动后失败,因此状态最终是 faulted,而不是 unavailable

Clone this wiki locally