KnowRob知识库搭建:knowrob_data层级配置与查询为空排查
简介面向机器人知识表示与语义环境建模学习者及研究者的 KnowRob 配套数据集合聚焦为知识库实验提供可复用的样本、测试与评估数据并与主程序代码分离存放便于在不同项目中重复使用。压缩包共 406 个文件约 52.4MB以 353 张 jpg 图像和 35 个 owl 本体文件为主辅以 md、xml 说明配置以及 sh、Dockerfile 等运行脚本覆盖动作配方、任务日志、语义地图和对象模型等数据形态。已有 208 人学习下载适合用于机器人任务描述、环境语义建模与知识库推理的对照实验。读者可据此搭建接近真实场景的数据目录理解 OWL 如何表达动作、对象属性与部件组成并借助日志和地图样本完成数据加载、检索与评估流程的练习也能为自建 KnowRob 数据集提供组织方式与文件命名上的参考。1. 编译全部通过查询结果却全为空knowrob_data 装错层级的代价第一次在一台机器人主控机上重建 KnowRob 知识库场景很典型主仓库编译全部通过roscore 起得来rosprolog 的交互 shell 也进得去可只要一查地图里的对象返回的永远是空。翻了半天日志才发现问题不在代码knowrob_data 这个数据仓库没被 catkin 正确识别OWL 本体和语义地图全是空的推理器在一套空公理上跑不报错只返回空。这是 KnowRob 最反直觉的地方——数据和推理是分层的装错了层症状看着像算法坏了。knowrob_data 承担的是地图、机器人日志、对象模型这些实打实的素材基于 WebOntologyLanguage 的本体文件也在里面。下面这套流程适合第一次搭 KnowRob 环境、或者要把旧机器上的知识库迁到新机器的人按顺序走完能避开大部分常见问题。2. 装前定版ROS 发行版、SWI-Prolog 与 knowrob_data 的目录归属KnowRob 的环境不是apt install一个包就完事的类型它至少牵扯四层操作系统与 ROS 发行版、Prolog 推理引擎、构建工具链、数据仓库本身。这四层里任意一层版本对不上表现出来的症状都可能跑到别的层去——比如 SWI-Prolog 装得太新rosprolog 的调用接口变了你会误以为是 OWL 文件写错。我一般会在动手前先把版本口径固定在表上后面所有命令都以这张表为准。2.1 两条版本线KnowRob 1 走 catkinKnowRob 2 走 colcon先说最容易搞错的一条。KnowRob 有两代主线ROS 1 时代那套用 catkin 构建ROS 2 的移植版走 ament/colcon。两者的 package.xml 格式、launch 文件语法、消息依赖全不一样。拿 ROS 2 的环境去编 ROS 1 的仓库colcon 会直接告诉你 package.xml 的 format 不认反过来在 ROS 1 下roslaunch一个 ROS 2 的 launch.py报错信息会绕到 Python 导入路径上去很难一眼看穿。我一般先确认机器上装的是哪套 ROS再决定拉哪个分支的数据仓库绝不混着来组合系统底座ROS 发行版Python构建工具典型场景KnowRob 1Ubuntu 20.04Noetic3.8catkin_tools存量机器人平台生态最全KnowRob 1Ubuntu 18.04Melodic2.7 / 3.6catkin_make老旧机器迁移Python 2 遗留多KnowRob 2Ubuntu 22.04Humble3.10colcon新建项目ROS 2 生态选型建议很直白手上的机器人驱动、传感器包如果还是 ROS 1 的就别折腾 KnowRob 2数据仓库和消息包对不上迁移成本远高于收益。新起的项目才考虑 Humble 那条线。另外 Python 版本别自己升Noetic 自带 3.8把系统 Python 换成 3.10 之后 rosdep 和 catkin 的 shebang 全会失效这种问题排查起来非常麻烦。还有一条血泪经验Ubuntu 的版本要和 ROS 发行版严格对应。有人在 22.04 上装 Noetic 的 apt 源rosdep install会报一堆依赖找不到看着像仓库缺包其实是 apt 源根本对不上号。2.2 推理引擎与系统包SWI-Prolog、MongoDB 的预装顺序KnowRob 的推理靠 SWI-Prologrosprolog 是 ROS 和 Prolog 之间的桥。这个桥对 SWI-Prolog 的版本是敏感的Noetic 官方源里的版本一般够用不建议自己去编译最新版——编译出来的 swipl 和 apt 装的库路径不一致rosprolog 加载时会出现找不到.so的情况。系统层我通常一次性装齐# Ubuntu 20.04 ROS Noetic 环境下的基础依赖 sudo apt update sudo apt install -y \ swi-prolog \ git cmake build-essential \ python3-pip python3-rosdep python3-catkin-tools \ raptor2-utils # 提供 rapper后面校验 OWL 文件语法要用逐条说一下取舍。swi-prolog走 apt不要源码编译理由上面讲了。python3-catkin-tools提供catkin命令比catkin_make更好控制增量编译和包级编译后面排查单个包编译失败时会省很多事。raptor2-utils里的rapper是个独立于 KnowRob 的 RDF/OWL 解析器用它来验证本体文件是否语法合法能把「文件坏了」和「加载逻辑坏了」这两类问题分开这个习惯我建议所有人都养成。MongoDB 视你要不要用 experience 日志而定。KnowRob 里机器人长时间运行产生的经验数据一般落到 MongoDB 里地图和对象模型则还是文件形式。如果只是加载本体和地图MongoDB 可以先不装要接机器人日志回放就得提前准备好# experience 日志的持久化实例我一般固定大版本不用 latest docker run -d --name knowrob-mongo \ -p 27017:27017 \ -v /data/knowrob-mongo:/data/db \ mongo:4.4-v挂载一定要加容器删了数据还在。-p只映射到本机回环就够知识库数据没必要暴露出去。端口如果不是 27017记得后面配置里同步改默认值写死在不同文件里是常见的出错点。rosdep 的初始化也要在这一步做掉别等到编译报错才想起来sudo rosdep init # 已经初始化过会提示文件已存在忽略即可 rosdep updaterosdep update拉的是依赖规则索引网络不好时会超时重试两三次通常能过。这一步失败不会立刻炸但后面rosdep install会告诉你找不到某个键错误信息指向的是包名而不是网络很容易误判。2.3 目录结构knowrob_data 放在 src 还是独立仓knowrob_data 有两种放法各有适用场景。第一种是作为 knowrob 主仓库的子目录存在跟着主仓库一起 clone第二种是单独 clone 到src/下作为独立包。两种都能编译区别在依赖解析和升级粒度。我一般推荐独立仓尤其是数据仓库这种体积大、更新频率和代码不一致的东西knowrob_ws/ ├── src/ │ ├── knowrob/ # 主仓库含 knowrob_common 等代码包 │ ├── knowrob_data/ # 数据仓库地图 / 对象模型 / OWL │ └── iai_common_msgs/ # 消息依赖按实际报错补 ├── build/ ├── devel/ └── logs/这样放的好处是升级代码时不用连带把几 GB 的地图数据一起拉一遍数据仓库单独git pull就行。坏处是包的搜索路径多一层如果knowrob_data的package.xml里声明了对主仓库某个包的依赖catkin 的拓扑排序会自己处理不用手动改顺序。需要提醒的是数据仓库里的大文件地图、点云、对象模型经常用 Git LFS 管理。git clone完发现 owl 目录是空的、或者地图文件只有几百字节的指针文本八成是没装 LFSsudo apt install -y git-lfs git lfs install cd ~/knowrob_ws/src/knowrob_data git lfs pull这个错误很隐蔽文件存在、路径正确、ls看得到但内容是version https://git-lfs...开头的几行文本。推理器读进去会报解析错误你以为是 OWL 写错了其实文件根本就没下载下来。3. 拉取与编译catkin 工作空间里把 knowrob_data 挂上去目录定好之后就是落地编译。这一步的坑主要集中在对「数据包」的误解上——knowrob_data 里几乎没有可执行代码它的 CMakeLists.txt 主要是把数据文件 install 到指定位置。这意味着它能编译通过不代表它被正确挂载反过来它编译失败往往也不是代码问题而是依赖声明或路径问题。3.1 建工作空间与放置数据仓库先建空间再放包顺序别反。catkin 的初始化只认当前src/下已有的内容先 init 再 clone 会导致新包不被索引需要重新catkin init。# 建工作空间骨架 mkdir -p ~/knowrob_ws/src cd ~/knowrob_ws catkin init catkin config --extend /opt/ros/noetic # 明确扩展基础空间避免环境串味 catkin config --cmake-args -DCMAKE_BUILD_TYPERelease # 放数据仓库仓库地址以你手上那份为准这里只体现位置 cd ~/knowrob_ws/src git clone knowrob_data 仓库地址 knowrob_datacatkin config --extend这条容易被忽略。机器上如果 source 过别的工作空间catkin 可能把那个空间当成扩展空间编译时链接到别人的库症状是「明明改了代码但行为没变」。显式指定/opt/ros/noetic能把这个变数消掉。CMAKE_BUILD_TYPERelease对纯数据包影响不大但主仓库里的推理相关组件在 Debug 下会慢一个数量级一起设了省事。放好之后立刻验证包能不能被找到这一步比编译更重要cd ~/knowrob_ws catkin build --dry-run # 只列出将要编译的包不真编 rospack find knowrob_data # 应输出 ~/knowrob_ws/src/knowrob_datarospack find返回空或者报 not found说明包的package.xml有问题格式错、缺 name 标签或者你还没source devel/setup.bash。刚catkin init完还没 build 时rospack是找不到包的得先 build 一次或者手动 source这一点新手经常困惑。3.2 用 rosdep 补齐依赖依赖分两类系统级apt 能装的库和包级工作空间内的其他 ROS 包。rosdep 管前者catkin 的拓扑排序管后者。cd ~/knowrob_ws # 只解析声明在 package.xml 里的系统依赖-r 表示忽略解析失败的键继续 rosdep install --from-paths src --ignore-src -r -y--from-paths src限定扫描范围别写成整个家目录否则会把无关包一起解析。--ignore-src的作用是如果某个依赖在工作空间里已经有源码包就不要再去 apt 装一份避免同一个包出现两份、运行时加载到错误的那个。-r让它在遇到无法解析的 rosdep key 时继续往下走而不是整体中断——这在混合了多个来源的仓库里很实用最后看一眼哪些被跳过就行。跑完之后建议再核对一遍关键依赖是否到位# 关键依赖的存在性检查缺哪个装哪个 dpkg -l | grep -E swi-prolog|raptor2-utils rospack find rosprolog || echo rosprolog 未找到检查是否随主仓库一起拉取rosprolog 通常随 KnowRob 主仓库一起提供如果没有说明主仓库没拉全或者分支不对。3.3 catkin build 的编译顺序与增量问题编译本身没什么玄学但有两个细节值得说。cd ~/knowrob_ws catkin build knowrob_data -v # 单包编译-v 输出完整命令 catkin build -j4 # 全量编译核数按机器内存调先单包编knowrob_data是因为它最独立编过说明基础环境没问题。-v会把每条编译命令打出来出错时能直接看到是哪个路径没找到。全量编译时-j别开太大主仓库里有几个模板重的包内存小的机器开到-j8会 OOM表现为编译器被 killed日志里只有一行 Killed很容易误判成代码错误。纯数据包的 CMakeLists 一般长这样理解它才能理解「编译通过但数据没到位」是怎么发生的# 典型的数据包安装规则把数据目录整体装到 share 下 install(DIRECTORY owl/ DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/owl) install(DIRECTORY maps/ DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/maps)CATKIN_PACKAGE_SHARE_DESTINATION展开后是share/包名。这意味着在 devel 空间里实际访问路径是devel/share/knowrob_data/owl而源码在src/knowrob_data/owl。两处都有文件改哪一处生效取决于你运行时代码里用的是package://还是绝对路径。这就是第 1 章那个「查询全空」的根源之一改了 src 里的 OWL运行的却是 devel 里的旧副本。增量编译还有个坑数据文件改了但 install 规则不会自动重跑catkin build认为没有源文件变化就跳过了。遇到这种情况catkin build knowrob_data --force-cmake # 强制重跑 cmake触发 install # 或者干脆清掉重编 catkin clean knowrob_data catkin build knowrob_data--force-cmake比重编快得多日常改数据文件我一般用它。4. 配置OWL 本体路径、地图目录与日志接入编译过了只是「东西在硬盘上」能不能被推理器读到全靠配置这一层。KnowRob 的配置分散在环境变量、launch 参数和包内相对路径三处任何一处写死绝对路径换台机器就废。4.1 OWL 本体路径用 package:// 而不是绝对路径本体文件是 WebOntologyLanguage 写的加载入口一般通过package://协议解析再由 ROS 的包查找机制转成实际路径。这样做的好处是路径跟着包走不依赖具体机器布局。# ~/.bashrc 里加的环境变量用于自定义数据根目录 export KNOWROB_WS$HOME/knowrob_ws export KNOWROB_DATA_ROOT$(rospack find knowrob_data) source /opt/ros/noetic/setup.bash source $KNOWROB_WS/devel/setup.bash注意source的顺序先基础 ROS再工作空间。反过来会让工作空间里的setup.bash找不到catkin相关变量报一串 command not found。rospack find依赖已经 source 的环境所以KNOWROB_DATA_ROOT这一行必须放在两个 source 之后——写在这之前的话登录新终端时会得到空值而且不报错。launch 文件里传路径时常见写法是!-- 用 package:// 让 ROS 自己解析换机器不用改 -- arg nameowl_file defaultpackage://knowrob_data/owl/knowrob.owl/如果某个第三方 launch 只接受绝对路径就把它包一层$(find knowrob_data)别手写/home/xxx/...。绝对路径是迁移时最常见的出错点换台机器用户名一变所有路径全废而且报错信息是「文件不存在」很容易被当成文件丢了。4.2 地图与对象模型的目录约定knowrob_data 里的数据一般按用途分目录语义地图、对象模型、测试用的小规模数据、以及本体文件。这个结构不是强制的但推理代码里往往写死了相对路径所以别随便挪。我把常打交道的几个位置列一下方便对照自己的仓库目录内容谁在用迁移注意事项owl/WebOntologyLanguage 本体、类层次、公理rosprolog 推理器必须语法合法改完要重新 installmaps/语义地图、栅格图、区域标注地图加载与查询常被 LFS 管理注意指针文件objects/对象模型、网格、位姿对象识别与抓取网格文件格式要和加载器匹配data/小规模测试数据、回放素材单元测试、演示体积小的可以进 git 正文对象模型目录里常见的坑是相对路径引用。一个对象模型文件里如果写了mesh: file:///home/xxx/models/part.stl那这个模型只有在那台机器上能用。正确的做法是用相对路径加包名前缀让加载器自己拼。这类问题在批量导入模型时特别多几百个文件里混进几个绝对路径查询时表现为「部分对象能查到、部分查不到」非常难定位。批量检查一遍很值得# 扫出模型/地图文件里的绝对路径引用 cd $(rospack find knowrob_data) grep -rn --include*.owl --include*.json --include*.yaml \ -E file:///(home|root|Users) . || echo 没有绝对路径引用一条 grep 就能把迁移前的问题提前暴露比运行时报错强得多。4.3 rosbag 与机器人日志MongoDB 连接配置机器人日志一般有两种用法一是 rosbag 回放把话题数据重新灌进系统二是 experience 数据直接落 MongoDB供长期查询。前者不需要额外配置后者要确认连接串和数据库名。# 启动前确认 MongoDB 可达再确认库里有数据 mongosh --host 127.0.0.1 --port 27017 --eval \ db.getSiblingDB(knowrob).getCollectionNames()连接串里的库名要和 KnowRob 配置里的对齐默认值通常是knowrob或roslog具体以你手上那份配置为准。库名不一致的典型症状是写入时不报错MongoDB 会自动建库查询时永远是空——又是一个「不报错但结果不对」的例子。rosbag 回放要注意时间同步# --clock 让 /clock 话题生效回放速度按需调整 rosbag play robot_log.bag --clock -r 1.0不带--clock时如果系统里有节点依赖仿真时间时间戳会乱表现为日志里的时间跳变、查询按时间窗口过滤时结果缺失。-r控制回放倍速调试时用 0.5 慢放能看清时序问题正式灌数据用 1.0 或更高。5. 排查knowrob_data 加载失败的几个典型现场这一节记录的是我实际遇到过、且症状和原因离得比较远的几类问题。KnowRob 这类分层系统最麻烦的地方在于错误最终都表现为「查询结果为空」但根源可能在文件、在路径、在环境、在数据库。下面每条都按现象、原因、解决三段来写。5.1 现象rospack 找得到包加载本体却报文件不存在现象很明确rospack find knowrob_data正常返回路径但推理器加载package://knowrob_data/owl/xxx.owl时报文件不存在ls到源码目录里看文件明明在。原因基本是源码空间和 devel 空间的路径不一致。package://解析到的是 devel 空间下的 share 目录而这个目录是 install 规则生成的只在编译时同步。如果只 clone 了文件没有重新 build或者用--force-cmake之外的方式改了文件devel 里就没有对应文件。另一种可能是工作空间没有 sourcerospack找到的是另一份同名包。解决分两步先确认解析结果落在哪再让两边一致。# 看 package:// 实际解析到哪个目录 rospack find knowrob_data ls -l $(rospack find knowrob_data)/owl | head # 如果指向 devel/share 且内容缺失重新触发 install cd ~/knowrob_ws catkin build knowrob_data --force-cmake日常改数据文件我习惯直接改 src 然后--force-cmake不去碰 devel 里的副本避免两边内容分叉。5.2 现象本体能解析推理结果却是空现象加载过程没有任何报错查询也无异常但返回结果集是空的。换一个查询谓词还是空。原因通常是本体加载进了独立的命名空间或独立的推理上下文。KnowRob 允许多个本体共存查询时如果没指定正确的命名空间前缀或者查询用的类名和本体里定义的类名大小写、拼写不一致就会静默返回空。另一个高频原因是本体文件确实加载了但只加载了顶层文件owl:imports引用的子本体没有跟着解析。解决从两头查先确认本体里到底有什么再确认查询用的名字对不对。% 进入 rosprolog 交互后先看本体里注册了哪些类 ?- owl_parse(package://knowrob_data/owl/knowrob.owl). ?- owl_class_count(Count). % 数量为 0 说明根本没加载进去owl_class_count返回 0 是最直接的判据不是查询写错是加载没成功。这时候去看owl:imports里的路径逐个确认能不能解析到。子本体缺失时不会报错只会让类层次不完整查询自然落空。5.3 现象rosprolog 启动即退出没有有效报错现象rosrun rosprolog rosprolog 包名执行后立刻回到 shell退出码非 0但终端上只有一两行看不出因果的信息。原因常见的有两个SWI-Prolog 版本与 rosprolog 编译时用的版本不一致导致动态库加载失败或者 Prolog 的初始化文件用户目录下的.plrc之类里有语法错误启动时加载失败直接退出。第二种更隐蔽因为跟项目本身完全无关。解决方法是绕开用户配置启动看能不能进交互# 用干净的家庭目录启动排除用户级 Prolog 配置干扰 HOME/tmp/swipl-clean rosrun rosprolog rosprolog knowrob_common # 同时确认 swipl 的实际版本与路径 which swipl swipl --version如果干净目录下能起来问题就在用户配置里把~/.plrc之类临时改名再试。如果还是不行看ldd输出里有没有 not found 的库那基本就是版本不匹配回退到 apt 提供的 swipl 重编。5.4 现象机器人日志写不进 MongoDB程序却不报错现象回放 rosbag 之后查询 experience 数据永远是空但节点日志里没有任何写入失败的记录。原因是连接配置写在了某个 launch 的默认参数里而实际启动用的是另一份 launch两边库名或端口不一致。MongoDB 的特性是写入时如果库不存在会自动创建所以「写进了一个没人查的库」这种情况完全不会报错。解决启动前统一确认连接参数启动后用 MongoDB 侧的命令反查。# 从数据库侧确认到底有没有数据落进来 mongosh --host 127.0.0.1 --port 27017 --eval \ db.adminCommand({listDatabases:1}).databases.map(dd.name)列出所有库看看有没有意料之外的库名。有的话把配置里的库名对齐重启节点即可。这个排查思路同样适用于「数据写进去了但查不到」的情况先怀疑库名再怀疑查询条件。5.5 现象catkin build 报找不到 knowrob_data现象全量编译时报某个包依赖knowrob_data找不到但单包编译knowrob_data又是成功的。原因一般是包的可见性被切断src/下存在CATKIN_IGNORE文件或者数据仓库所在的目录层级不对比如多套了一层目录src/knowrob_data/knowrob_data/package.xmlcatkin 扫描时按目录深度找package.xml层级错了就找不到。还有一种情况是之前编译失败留下了build/里的缓存拓扑排序用的是旧结果。解决# 确认包的实际位置和层级 find ~/knowrob_ws/src -maxdepth 3 -name package.xml # 清理缓存后重新扫描 cd ~/knowrob_ws catkin clean -y catkin buildfind的输出能直接暴露层级问题。正常的应该是src/包名/package.xml多一层或少一层都不行。清理缓存这一步别省catkin 的缓存偶尔会记住已经不存在的包导致报错信息指向一个早就不在目录里的东西。6. 验证跑通三层自检从文件到查询再到日志回放装完之后怎么确认「真的能跑」而不是「看起来能跑」我总结成三层自检。三层的顺序不能反因为后一层的失败往往是前一层没做干净导致的。第一层是文件层用独立的解析器验证 OWL 本体语法。这一步的意义在于把「文件本身坏了」和「KnowRob 加载逻辑有问题」彻底分开用的是rapper跟 KnowRob 一点关系都没有# 逐个校验 owl 目录下的本体文件语法不合法会直接报错 cd $(rospack find knowrob_data) for f in owl/*.owl; do if rapper -i rdfxml -c $f /dev/null 21; then echo OK $f else echo FAIL $f fi done输出里出现 FAIL 就别往下走了先修文件。注意如果文件是 LFS 指针这里会全部 FAIL回头检查git lfs pull有没有执行。第二层是加载层确认本体真的进了推理上下文。进 rosprolog 交互看类数量% 包名以 rospack list 里的实际名字为准 ?- register_ros_package(knowrob_data). ?- owl_parse(package://knowrob_data/owl/knowrob.owl). ?- owl_class_count(Count).Count是 0 说明加载没成功非 0 说明本体进来了。这一步过了第 5 章里前两类问题基本就排除了。第三层是端到端回放一段真实日志看地图和对象能不能被查出来。这一层我一般用一张小表记录每次验证的期望值方便对比检查项命令期望结果包可见性rospack find knowrob_data返回 src 下路径本体语法rapper -c owl/*.owl无错误输出类数量owl_class_count(Count)大于 0地图查询查询已知区域个体返回至少一条日志落库mongosh列库与集合目标集合非空三层的意义在于定位速度。跳过第一层直接跑查询一旦返回空你得同时怀疑文件、路径、环境、数据库四个方向按顺序走每次只多引入一个变量出问题基本能一眼看出来是哪一层断的。从那以后我每次搭 KnowRob 环境都强制走完这三层自检再交付哪怕时间再紧也不跳。因为跳过省下的十分钟往往要在后面花一整天去追一个「查询返回空」的模糊问题。这套数据仓库加上配套的本体文件按上面的结构放好、环境变量配对、三层自检走通迁移到新机器上也就是重复一遍的事。需要这份 knowrob_data 的话按文中的目录结构放好从第一层自检开始走就行希望帮到你。本文还有配套的精品资源点击获取