Skip to content

Debugging

konodoki edited this page Aug 2, 2026 · 26 revisions

调试、验证与常见问题

本文覆盖从输入设备、Mod 加载、状态切换、推理资源、Mod 子节点到构建部署的常见故障。 机器人状态订阅、电机输出或硬件看门狗问题请转到机器人平台适配指南; 控制周期超时请转到框架控制调度

先定位故障层,再查看具体章节。 不要同时修改输入 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、 preparing、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 是否为 1api 是否与当前框架兼容,name、id 和版本是否合法。
  • 是否出现重复 id。
  • 日志是否以 Mod logger 打印 loaded v<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
  • policy="startup" 的资源在控制循环启动前准备;失败会阻止启动。
  • policy="on_demand" 的资源在第一次请求目标状态时异步准备;状态信息会显示 preparing
  • 状态必须通过 resources=(handle,) 声明依赖;ResourceHandle.get() 不会触发加载或阻塞等待。
  • on_prepare() 预热,不要在插件 import 时创建推理 Session。
  • 替换模型后需要重启控制节点;启动失败时查看具体资源加载异常。

按键有输出但状态不切换

  1. /motion_commands 的槽位和值是否与 mod.yaml events 一致。
  2. 当前完整状态名是否存在对应 route。
  3. 跨 Mod名称是否含 / 且依赖存在。
  4. 是否正在等待 delay、资源 preparing 或处于活动过渡。
  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。

离线加载检查

下面的检查会加载所有 startup 资源,但不会请求 on_demand 资源,可用于验证 Mod、 启动资源和状态图:

from pathlib import Path
import yaml
from bxi_example_py_elf3.framework.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()

当前默认树应发现 7 个 Mod 和 17 个状态。

深度相机没有数据

com.bxi.normal_depth 不再内置 RealSense 节点。真机相机由独立 bxi_depth_camera 包管理,硬件示例 launch 默认包含其 cameras.launch.py

先检查节点和标准话题:

ros2 node list
ros2 topic list | grep body_depth_camera
ros2 topic hz /hardware/body_depth_camera/depth/image_rect_raw
ros2 topic echo /hardware/body_depth_camera/depth/camera_info --once

仿真把 hardware 换成 simulation。继续确认:

  • cameras.body_depth_camera.serial_no 是否与实际设备对应;单相机回退只适合确实只连接一台设备的情况。
  • 深度图和 CameraInfo 是否同时存在,且属于相同分辨率 profile。
  • 图像编码是否为 16UC1mono1632FC1
  • mod.yamlcamera_name、显式 topic/camera_info_topic 是否互相一致。
  • 日志是否出现 waiting for CameraInfo、非法内参、错误 shape 或深度超时。

状态不可进入时查看 /hardware/state_machine_info:相机数据不新鲜会让 is_available() 返回 False;模型资源仍在准备时会出现 preparing。两者不是同一个 问题,前者检查 ROS 数据,后者检查 Resource 状态和加载错误。

root 运行后普通用户无法重新构建

若运行用户是 root、构建用户是普通用户,旧版本可能在 Mod 或 vendor 目录生成 root 所有的 __pycache__/*.pyc,随后构建会报 Permission denied: '*.pyc'。当前框架禁止 Mod 主进程、依赖探测进程和内置节点子进程向 Mod 目录写 Python 字节码;升级前已经生成的文件仍需一次性修正工作区属主:

cd ~/bxi_ws/bxi_rl_controller_ros2_example
sudo chown -R bxi:bxi src build install log
./build.sh

不希望修改整个工作区时,可先用 find src build install log -user root -print 定位 root 所有的文件。运行命令也可显式加 PYTHONDONTWRITEBYTECODE=1,进一步阻止 ROS launch 自身生成缓存:

PYTHONDONTWRITEBYTECODE=1 ros2 launch \
  bxi_example_py_elf3 example_demo_hw.launch.py

相关文档

Clone this wiki locally