简介:这份数据包面向机器人知识表示与语义环境建模的学习者和研究者,围绕 KnowRob 知识库汇集可复用的样本、测试与评估数据,并以 Web Ontology Language 组织动作配方、语义地图和对象模型等内容,便于在独立于主程序仓库的上下文中开展实验。压缩包共 406 个文件、约 52.4MB,主体为 353 张 jpg 图像,另含 35 个 owl 本体文件及少量 md、xml、sh、Dockerfile 等配置与说明文件,可支撑日志回放、对象属性查询和地图可视化等任务。已有 208 人学习下载。读者可从中获得按动作、日志、地图、对象分类的数据组织样例,理解 OWL 文件如何描述任务步骤、部件组成与 CAD 模型链接,并借助附带的容器与脚本文件快速复现实验环境,适合作为知识图谱构建、机器人任务规划与语义地图研究的参考数据集。
1. 装 knowrob_data 之前,先认清它不是个能 make 的包
有次接手一台机器人实验机,knowrob_data 拉进 src,catkin_make 一路绿灯,结果进 rosprolog 一调owl_parse就报文件不存在——数据仓库压根没有编译产物,绿灯只说明 CMake 扫到了一个空壳。knowrob_data 是 KnowRob 知识库的配套数据仓库,装的是地图、机器人日志、对象模型,外加一批用 Web Ontology Language 写的 .owl 本体。它不能被编译,只能被挂载到 KnowRob 的解析路径上。真正卡人的不是命令有多长,而是 ROS 发行版、SWI-Prolog、KnowRob 主线三条线能不能对上,以及package://这种路径 Prolog 到底认不认。下面按我实际装机的顺序走一遍。
2. 环境清点:ROS 发行版、SWI-Prolog 与 KnowRob 主线的对齐
装数据仓库最怕的不是缺依赖,是三条版本线各走各的。我见过最典型的一台机器:系统是旧的 Ubuntu,ROS 也是旧发行版,SWI-Prolog 却是从源码编的新版本,knowrob_data 因为本体里用了较新的 OWL 构造,解析到一半直接抛异常,前面几十个文件全部白加载。所以动手之前先花五分钟把现状打印出来,比事后翻日志划算得多。
2.1 三条版本线要先对齐
KnowRob 这套东西的版本耦合比一般 ROS 包紧。Ubuntu 版本决定能装哪个 ROS 发行版,ROS 发行版决定 rosprolog 和 catkin 的接口形态,SWI-Prolog 的版本决定 OWL 解析器吃不吃得下某些语法。三条线里任意一条越级,症状都不会是「装不上」,而是「装上了但查不出东西」,这种更难查。
先打这几条命令,把现状摸清楚:
# 系统版本:决定你能装哪个 ROS 发行版,别跨版本硬凑 lsb_release -ds # 当前 shell 的 ROS 发行版,输出为空说明还没 source echo "$ROS_DISTRO" # SWI-Prolog 版本,KnowRob 对 8.x 系列更友好 swipl --version # KnowRob 本体装在哪,找不到说明工作空间没 source 或者没编 rospack find knowrobswipl --version这行要重点看。常见做法是直接用 apt 装,但发行版自带的 SWI-Prolog 往往偏旧,遇到本体里稍微新一点的 OWL 写法就会在解析阶段报语法错,而报错位置通常指向本体文件的某一行,很容易被误判成「数据仓库的文件坏了」。我一般会先apt-cache policy swi-prolog看一眼候选版本,低于 8 就换官方源装 8.x 系列,装完再swipl --version复核一次,确认 shell 里调到的不是旧二进制。
| 组件 | 我锁的口径 | 核对命令 |
|---|---|---|
| Ubuntu | 与 ROS 发行版一一绑定,不跨版本 | lsb_release -ds |
| ROS | 跟随系统版本,$ROS_DISTRO必须有值 | echo "$ROS_DISTRO" |
| SWI-Prolog | 8.x 系列,apt 太旧就换源 | swipl --version |
| KnowRob 主线 | 与 knowrob_data 同一分支或同一 tag | git -C src/knowrob log -1 --oneline |
| rosprolog | 跟 catkin 工作空间一起编 | rospack find rosprolog |
提示:knowrob_data 的分支要和 knowrob 主线的分支对上。本体里的
owl:imports指向的是主线的类名和命名空间,主线换了命名空间而数据仓库还停在老分支,加载不会报错,但查出来的个体全是孤立的,这条坑后面第 5 章还会展开。
2.2 磁盘规划:数据仓库别放临时目录和网络盘
knowrob_data 的体积主要不在 .owl 文本上,而在对象模型和日志上。网格模型动辄几十兆一个,机器人日志按时间切片存,几百个小文件很常见。这类仓库有两个特点:单文件大、文件数量多。放到/tmp里重启就没了;放到网络挂载盘上,Prolog 逐个打开文件解析时延迟会被放大,表现出来就是「加载卡住不动」,你会以为是死循环,实际上是在等 IO。
我一般固定在 home 下开一个独立工作空间,别和别的 ROS 项目混在一起:
# 独立工作空间,避免和别的 catkin 项目互相污染 mkdir -p ~/knowrob_ws/src # 装之前先看剩余空间,对象模型和日志很吃盘 df -h ~/knowrob_ws # 装完再回头看实际占了多少,方便以后清理 du -sh ~/knowrob_ws/src/knowrob_datadf -h这一步别省。有次在一台小容量机器上装,加载到对象模型那一段直接失败,日志里报的是解析错误,查了半天才发现是磁盘写满导致临时文件没落盘。另外注意权限:不要用 root 跑 rosprolog,加载过程中会在工作空间里写缓存文件,root 写完之后普通用户读不了,下一次加载会莫名其妙地失败。
2.3 依赖安装顺序:系统包在前,rosdep 在后
依赖安装有个顺序问题。rosdep 需要联网拉取 rosdep key 的索引,如果系统基础工具还没装好、时间不同步、证书不全,rosdep update会以一个和依赖完全无关的报错失败,很容易带偏排查方向。所以先把 git、编译工具、SWI-Prolog 这些落地,再动 rosdep。
sudo apt update sudo apt install -y git cmake build-essential curl # 先看 apt 给的 SWI-Prolog 候选版本,太低就换源再装 apt-cache policy swi-prolog sudo apt install -y swi-prolog # rosdep 初始化,重复执行会报已存在,用 || true 兜住 sudo rosdep init 2>/dev/null || true rosdep updatesudo rosdep init只在第一次需要,第二次会提示配置文件已存在并以非零码退出,脚本里不兜住就会中断后面的步骤。rosdep update失败最常见的原因是索引下载不完整,重跑一次通常就过;如果连续失败,去看~/.ros/rosdep/sources.cache里有没有残留的半截文件,清掉再试。
系统依赖装完之后,再回到工作空间里跑一次rosdep install把 ROS 侧的依赖补齐。这一步要在src目录下已经有包的前提下执行,否则它什么都不会装,还会返回成功,让人误以为依赖齐了。
cd ~/knowrob_ws # --from-paths src 指定扫描目录,-y 免交互,-r 忽略个别装不上的包 rosdep install --from-paths src --ignore-src -y -r--ignore-src的意思是本地已有源码的包不去装二进制版本,避免和源码编译冲突;-r表示某个包解析失败时继续装其余的。这两个参数在装 KnowRob 这类多包工作空间时基本是必带的,不带的话经常因为一个可选包卡住整个流程。
3. 落盘与挂载:把 knowrob_data 放进 catkin 工作空间并让 rospack 认出来
环境摆平之后才是真正的落盘。这一步的目标不是「代码拉下来」,而是「rospack 能找到它,Prolog 能通过package://解析到里面的文件」。这两件事是两回事,很多人卡在后者。
3.1 克隆策略:数据仓库别盲目加 --recursive
knowrob_data 是纯数据包,没有子模块依赖,--recursive在这里没有意义。但仓库里的模型文件可能用了 Git LFS 管理,直接 clone 下来拿到的是指针文件(几十字节的文本,内容以 version 开头),加载时不会报错,只是模型读不出来。
cd ~/knowrob_ws/src # 克隆数据仓库,地址换成你自己手上的那个 git clone <knowrob_data 仓库地址> knowrob_data # 进目录确认模型文件是不是 LFS 指针 head -c 64 knowrob_data/**/*.ply 2>/dev/null # 如果是指针,需要先把 LFS 拉下来 git -C knowrob_data lfs install git -C knowrob_data lfs pullhead -c 64这个检查很土但很有效。正常的二进制网格模型前几十个字节是乱码,LFS 指针则是一行可读文本,一眼就能分辨。另外克隆时如果只想要某个分支,用git clone -b <分支> --single-branch能省下不少时间,但注意分支要和 knowrob 主线对齐,别贪快选错。
克隆完顺手看一眼目录结构,心里有个数:owl/下放本体文件,地图、日志、模型通常在各自的子目录里,有的还会带一个package.xml让 catkin 认出来。这个package.xml是数据包能被rospack find找到的关键,缺了它,后面所有的package://解析都会失败。
# 确认包描述文件在,rospack 靠它识别包 ls knowrob_data/package.xml # 看一层目录,知道本体、模型、日志各在哪 ls knowrob_data3.2 环境变量:source 顺序不对,rospack 就是瞎的
package://这个 URI 能被 Prolog 打开,靠的是 rosprolog 内部把包名翻译成实际路径,而翻译的依据是ROS_PACKAGE_PATH。这个变量是devel/setup.bash注入的,顺序错了就找不到。
# 先 source 系统 ROS,再 source 工作空间,顺序不能反 source /opt/ros/$ROS_DISTRO/setup.bash source ~/knowrob_ws/devel/setup.bash # 确认变量里包含你的工作空间 src 路径 echo "$ROS_PACKAGE_PATH" # 这一步必须能打印出真实路径,否则后面全白搭 rospack find knowrob_datasource的顺序反了会怎样?工作空间里的包会被系统路径覆盖,rospack find knowrob_data返回找不到或者返回一个旧路径。更隐蔽的情况是:你在一个终端里 source 了,新开一个终端忘了 source,然后对着「明明装好了却找不到」抓狂。我一般把这两行写进一个env.sh,每次开终端先source ~/env.sh,比记忆靠谱。
# ~/env.sh 内容,开新终端时 source 一次 export ROS_DISTRO=<你的发行版代号> source /opt/ros/$ROS_DISTRO/setup.bash source ~/knowrob_ws/devel/setup.bash注意:每次
catkin_make之后,devel下的环境文件会被重写,已经开着的终端必须重新 source 一次。这条看起来是废话,但我敢说一半以上的「改了没生效」都是因为它。
3.3 编一次:数据包也要过 catkin_make
数据包没有 C++ 目标,编译几乎是空转,但这一步不能跳。catkin_make会生成devel下的环境文件,把新包注册进ROS_PACKAGE_PATH。不跑这一步,哪怕src下的目录结构完全正确,rospack find也看不见它。
cd ~/knowrob_ws catkin_make # 编译完立刻验证包是否被注册 rospack find knowrob_data编译过程中如果某个依赖包报错,先单独编那个包看完整报错,catkin_make会把错误信息截断,容易看漏关键行。常见做法是catkin_make --pkg <包名>单独编一次,报错更完整。
编完之后有一个反直觉的地方:catkin_make成功只代表文件系统层面准备好了,跟数据能不能被 Prolog 读懂是两码事。我第一次装的时候就栽在这,看到[100%] Built target以为万事大吉,结果一加载本体就发现 imports 链断了。所以下一步必须真的进 Prolog 里跑一遍。
4. 加载 OWL 本体:knowrob_data 里的 .owl 文件怎么被 Prolog 解析进去
到这一步才是 knowrob_data 真正「生效」的地方。加载不是把文件全塞进去就行,顺序和 URI 写法都有讲究。
4.1 加载顺序:顶层本体在前,数据本体在后
KnowRob 的加载链路是分层的:先加载核心本体(定义类、属性、顶层命名空间),再加载 knowrob_data 里的扩展本体(定义具体的个体、地图、对象实例)。顺序反了,扩展本体里的个体找不到对应的类,会被静默丢掉一部分,或者直接抛undefined class一类错误。
% 第一步:加载核心本体,建立顶层类体系 ?- owl_parse('package://knowrob_common/owl/knowrob.owl'). % 第二步:加载数据仓库里的扩展本体,文件名以你仓库实际内容为准 ?- owl_parse('package://knowrob_data/owl/<你的数据本体>.owl'). % 第三步:确认加载成功,看命名空间下有没有个体 ?- findall(X, rdf_has(X, rdf:type, knowrob:'Map'), L), length(L, N).owl_parse/1的参数是 URI 字符串,不写完整路径。返回值是true不代表全都加载进去了,要留意 stderr 里有没有 warning。我一般会顺手加一句owl_parse之后的断言查询,确认关键类能查到,别等到下游用的时候才发现少了一半。
第三步那条findall是最朴素的检查:把类型为Map的个体全部收集起来数个数。如果返回L = [],说明地图个体没进来,不一定是解析失败,很可能是命名空间对不上,这个问题在第 5 章细说。
4.2 imports 链与相对路径:断在哪一环
OWL 的owl:imports是按相对路径解析的,基准是当前正在加载的那个文件所在的目录。所以数据仓库里的本体如果 import 了同目录下的另一个文件,只要整个仓库在,链就不断;但如果你只把某一个 .owl 单独拷出来测,imports 立刻报文件找不到。
排查这条链有个笨但可靠的办法:直接在 Prolog 里把所有已加载的本体列出来,看缺哪个。
% 列出当前已加载进内存的所有本体文件 ?- findall(F, owl_ontology_file(_O, F), L).这条查询的语义是「找出每个已加载本体对应的物理文件路径」。如果列表里缺少你期望的那几个,说明加载在某个 imports 处中断了。配合owl_parse的返回值一起看,基本能定位到是哪一环断的。
还有一种情况是循环 import:A 引 B,B 又引 A。解析器一般能兜住,但会重复解析同一个文件,内存和时间都浪费,遇到大文件时表现出来就是「加载到一半卡死」。第 5 章会单独说这个。
4.3 地图、日志、对象模型各归各的位
knowrob_data 里的数据不是同一种东西,加载方式和用途都不一样,混着处理容易出错。我按这张表来区分:
| 内容类型 | 常见格式 | 存放位置 | 谁去读它 |
|---|---|---|---|
| 地图 | 栅格图加一份元数据描述,或本体里的地图个体 | 地图子目录 | 导航或定位模块、本体查询 |
| 机器人日志 | 时序记录文件、本体个体 | 日志子目录 | 行为分析、回放脚本 |
| 对象模型 | 网格文件加本体中的类定义 | 模型子目录 | 视觉或抓取模块、本体中按类检索 |
| 本体文件 | .owl | owl 子目录 | SWI-Prolog 解析器 |
本体文件是唯一需要 Prolog 主动解析的部分,其余三类都是被程序按路径读取的文件。所以「加载失败」这件事,只在 .owl 上会表现为 Prolog 报错,模型和日志读不出来时表现是下游模块拿不到数据,两者排查路径完全不同。
模型文件的路径通常也写在个体属性里,格式同样是package://开头。这意味着换机器或者换工作空间路径之后,个体里的路径不会自动变更,需要重新生成或者用相对路径。这一点迁移的时候特别容易踩,后面第 6 章的验证会专门查这个。
5. 常见问题排查:knowrob_data 加载链路上的五处翻车点
下面这几条都是我自己撞过或者帮人定位过的,按「现象 → 原因 → 解决」记。
5.1 现象:rospack find 报找不到包,但目录明明在 src 下
原因一般有三个:package.xml缺失、没跑过catkin_make、当前终端没 sourcedevel/setup.bash。这三个的区分方法很简单,echo "$ROS_PACKAGE_PATH"看输出里有没有你的工作空间路径。
解决:先确认ls src/knowrob_data/package.xml存在;再cd ~/knowrob_ws && catkin_make;最后重新source devel/setup.bash并rospack find knowrob_data复核。如果package.xml里的包名和目录名不一致,rospack find要用package.xml里声明的名字,不是目录名,这条坑过一个同事半天。
5.2 现象:owl_parse 报文件不存在,但用 ls 就能看到那个文件
原因通常是 URI 拼错了。package://后面必须跟包名,不是工作空间路径,也不是绝对路径。写成package://home/user/knowrob_ws/...一定失败。另外多一个斜杠、包名大小写不对,都会导致解析失败,而且报错信息只说找不到,不会告诉你 URI 错在哪。
解决:把package://换成绝对路径先试一次,能加载说明文件没问题、纯粹是 URI 的事;然后对照rospack find knowrob_data的输出,把包名部分对齐。还有个小概率原因:文件是软链,解析器按真实路径解析后跨了目录,imports 链断了。
5.3 现象:加载过程中 SWI-Prolog 内存一路涨,最后卡死或者被杀
原因是大本体文件重复解析。循环 import 会重复加载,多次手动执行owl_parse同一个文件也会叠加。另外某些本体里个体数量极大,全量加载进内存本身就吃资源。
解决:先确认没有重复执行加载语句,同一个会话里重新加载前先清掉旧的;再看 imports 是否有环,用 4.2 那条owl_ontology_file查询把已加载文件列出来,看有没有同一个文件出现两次。实在吃不下就分批加载,只加载当前任务需要的那个子本体。
5.4 现象:类能查到,个体死活查不出来
这是最典型的一个。原因基本落在命名空间上:knowrob_data 的个体声明用的命名空间,和你在查询里写的命名空间不一致。本体里常见的是相对 URI 加xml:base,加载时的 base 由加载顺序决定,如果核心本体还没加载就解析数据本体,个体的 URI 会被拼成另一个前缀。
解决:加载完立刻查一个已知个体的完整 URI,看它的前缀是什么,再按实际前缀改写查询。另外注意owl:imports指向的主线版本,如果 knowrob 主线换过分支而数据仓库没跟着换,命名空间整体对不上,这类问题只能靠对齐版本解决。
5.5 现象:路径里带空格或中文,加载报各种奇怪的解析错误
原因很直白,URI 不认空格,中文路径在转义环节容易出问题。ROS 工作空间默认建在 home 下一般没事,但如果把项目放在「我的文档」这类目录里,就会撞上。
解决:把工作空间挪到纯 ASCII、无空格的路径下,重建一次catkin_make,重新 source。这个成本比修复解析器低得多,我现在的习惯是任何 ROS 工作空间都不放在带空格或中文的目录里。
6. 装完怎么验证:三条查询确认数据仓库真的接上了
装完最怕的是「一切看起来正常」。catkin_make是绿的,rospack find有输出,owl_parse返回 true,但下游一用就发现地图个体是空的、模型路径全是旧机器的。所以验证不能停在命令成功,要查到数据层。
我现在固定跑三段,一段比一段深。
第一段确认环境,纯 shell:
# 三条都通过,说明包注册和路径解析这一层通了 rospack find knowrob_data rospack find knowrob_common swipl --version | head -1第二段确认本体加载,进 Prolog:
% 加载核心本体,再加载数据仓库本体,观察有没有 warning ?- owl_parse('package://knowrob_common/owl/knowrob.owl'). ?- owl_parse('package://knowrob_data/owl/<你的数据本体>.owl'). % 已加载的本体文件列表,缺哪个一眼能看出来 ?- findall(F, owl_ontology_file(_O, F), L).第二段的关键不是true,是 stderr 里有没有 warning,以及第三条查询列出来的文件列表是否完整。列表里少一个文件,说明 imports 链断在那里,后面的查询结果不可信。
第三段确认数据层,这是最能暴露问题的一步:
% 查地图个体的数量,返回 0 说明个体没进来 ?- findall(X, rdf_has(X, rdf:type, knowrob:'Map'), L), length(L, N). % 抽查一个对象模型个体,看它的路径属性指向哪 ?- rdf_has(M, rdf:type, knowrob:'ObjectModel'), rdf_has(M, knowrob:'pathToModel', P).最后那条查询返回的路径值要重点看。如果打印出来的是/home/某台旧机器/...这种绝对路径,说明这批数据是从别的机器拷过来的,路径没更新,加载时不会报错,但下游读模型一定失败。这种情况要么重新生成本体里的路径,要么用package://形式重建,别指望它自己修正。
| 验证层级 | 通过标准 | 失败时先看哪里 |
|---|---|---|
| 包注册 | rospack find返回真实路径 | package.xml、是否catkin_make、是否 source |
| 本体加载 | 无 warning,文件列表完整 | owl:imports链、包 URI 拼写 |
| 数据可用 | 个体数量非零,路径为当前机器可读 | 命名空间、路径迁移、分支对齐 |
我踩过最冤的一次是三层各自都「看起来通过」,唯独对象模型路径是旧的,结果抓取模块在真机上抓空。从那以后我每次装完 knowrob_data,都会强制把第三段最后那条查询跑一遍,把返回的路径逐个ls一遍,确认文件真的在当前机器上。验证这件事,信输出比信状态靠谱。希望这套顺序能帮你少走两趟弯路。
本文还有配套的精品资源,点击获取