ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ROS环境搭建:rosdep init/update报错与镜像源替换修复指南

ROS环境搭建:rosdep init/update报错与镜像源替换修复指南 简介遇到 rosdep init 与 rosdep update 反复报错的机器人操作系统开发者可以直接用这份离线包解决。绝大多数报错是因为网络无法获取 rosdep 依赖包导致 init 和 update 两条命令在 etc/ros 目录下安装软件包时失败下载解压后将对应文件直接拷贝覆盖即可在本地恢复完整的 rosdep 数据省去反复排查网络环境的麻烦。资源共 6 个文件以 yaml 格式的跨平台依赖描述和 list 格式的源列表为主压缩包仅 43KB轻量且易保存适合本地离线环境、内网机器或网络不稳时的快速修复场景。内部按 sources.list.d 目录组织不同操作系统对应的 yaml 配置清晰可辨方便读者理解 rosdep 的数据结构也能在更换环境后快速复用。目前已有 1982 人学习下载对长期受网络问题困扰的 ROS 初学者和项目开发者来说是一份很实用的排错与备份资料。1. 为什么 rosdep init 和 rosdep update 报错会拦住环境搭建新装一台 Ubuntu按官方流程走到sudo rosdep init命令打下去几分钟没反应最后抛一个The read operation timed out就退出这种经历在 ROS 开发者里几乎人人都有。rosdep init和rosdep update是 ROS 环境搭建中最早出现的两条命令前者写入依赖源清单后者把清单里的多份 YAML 索引拉到本地缓存任何一环失败后续的rosdep install都拿不到完整依赖表工程编译就会卡在 package not found 上。这两条命令的报错率在真实环境里很高报错文案又很抽象unable to process source、found character that cannot start any token、SSL: CERTIFICATE_VERIFY_FAILED每一句都看不出来是网络问题、权限问题还是缓存问题。很多人选择反复重试或者直接跳过结果代价在catkin_make、colcon build之后才爆发。本文先把两个命令各自做了什么拆开再给出一套可复现的修复流程把默认公网数据源替换成可稳定访问的镜像地址同时修正 rosdep 自身源码里的硬编码地址最后用命令验证依赖解析真的恢复正常。2. rosdep 链路拆解init 落本地文件update 抓远端 YAML2.1 init 的唯一成品/etc/ros/rosdep/sources.list.d/20-default.listrosdep init表面上看是初始化 rosdep实际做的工作非常少它只负责从一个固定的远端地址下载描述默认源的文件然后写到/etc/ros/rosdep/sources.list.d/20-default.list。这个文件里每一行是一个yaml数据源声明其中osx-homebrew行带有osx标签其余行是通用依赖定义。init 结束之后rosdep 的系统配置就完成了后续所有动作只围绕这个文件展开。因为 init 逻辑太短它的报错几乎都集中在两处下载默认清单时网络超时或者写入目录时权限不足。后者通常表现为Permission denied用sudo就能解决前者才是一半以上 init 报错的来源。手工创建这个文件是绕开下载步骤最直接的办法文件内容可以从同步了 rosdistro 仓库的镜像站取sudo mkdir -p /etc/ros/rosdep/sources.list.d/ sudo tee /etc/ros/rosdep/sources.list.d/20-default.list /dev/null EOF # os-specific listings first yaml https://mirrors.tuna.tsinghua.edu.cn/rosdistro/rosdep/osx-homebrew.yaml osx # generic yaml https://mirrors.tuna.tsinghua.edu.cn/rosdistro/rosdep/base.yaml yaml https://mirrors.tuna.tsinghua.edu.cn/rosdistro/rosdep/python.yaml yaml https://mirrors.tuna.tsinghua.edu.cn/rosdistro/rosdep/ruby.yaml yaml https://mirrors.tuna.tsinghua.edu.cn/rosdistro/rosdep/nodejs.yaml yaml https://mirrors.tuna.tsinghua.edu.cn/rosdistro/rosdep/gentoo.yaml EOF这段命令先创建 rosdep 需要的目录再用tee把六类默认数据源一次性写入文件。yaml关键字告诉 rosdep 这一行是 YAML 格式的源osx标签说明这份数据只作用于 macOS 系统。URL 从默认地址换成镜像站之后init 自身就不存在拉取大文件的耗时点速度会明显改善。2.2 update 真正在拉取什么osx/base/python/ruby/nodejs/gentoo 五类数据rosdep update则完全不同。它读取 20-default.list 中声明的所有源逐条向远端发起请求把每个 YAML 文件下载后做合法性校验再聚合保存到~/.ros/rosdep/sources.cache/目录。update 之所以比 init 慢因为它的目标是五类系统依赖定义数据文件覆盖范围解析后的用途base.yamlUbuntu、Debian 系系统包把 ROS 包名映射到apt包名python.yamlPython 生态依赖把 ROS 包名映射到pip包名ruby.yaml、nodejs.yamlRuby、Node.js 生态处理少数基于语言生态的依赖osx-homebrew.yamlmacOS对应 Homebrew 包名gentoo.yamlGentoo Linux对应 emerge 包名update 期间任何一个文件抓取失败rosdep 会立即停止并报错。但更隐蔽的失败是文件抓到了内容是某个错误页面体积很大而且不是 YAML 格式于是解析阶段报出found character that cannot start any token。这种错误完全容易造成误解——看起来像 YAML 语法问题其实是网络返回内容不对。2.3 先判断故障发生在哪条链路上拿到报错先别急着改配置。第一步判断故障在 init 阶段还是 update 阶段第二步判断是网络层问题还是数据解析问题。中间的判断成本可以压到一条命令curl -I --max-time 10 https://mirrors.tuna.tsinghua.edu.cn/rosdistro/rosdep/base.yaml-I只取响应头--max-time 10限制十秒超时。返回HTTP/2 200说明镜像通路正常问题大概率在本机缓存或 rosdep 源码如果超时或返回 502就需要换一个更快的镜像源。这一步只验证某个 URL 能不能访问不会改动系统任何文件适合反复执行。3. 修复包的实现源码地址替换与手工初始化3.1 为什么要同时改源码和清单文件只手工写好 20-default.list 只能解决 initupdate 阶段依然会按照文件里的地址去抓取。如果文件里是官方地址等于把压力转移到 update如果文件里是镜像地址而 rosdep 部分源码里仍然写死了老地址某些流程仍可能回退到不可用的源。社区里各种完美解决 rosdep init/update 报错的打包方案核心内容就是两件事替源码里的硬编码地址打补丁以及用镜像源重写清单文件。自称完美的无非是把这两个动作连同环境清理都脚本化让使用者少操作几步。我一般按同样的思路处理先定位 rosdep2 源码里的默认 URL做批量替换再生成 20-default.list最后清理历史缓存重跑。这样处理之后init 和 update 都固定在镜像数据源上不再依赖默认公网站点的稳定性。3.2 定位 rosdep2 并替换硬编码域名不同 Ubuntu 版本下 rosdep 的安装位置不一样先找包的实际路径python3 -c import rosdep2; print(rosdep2.__file__)输出通常类似/usr/lib/python3/dist-packages/rosdep2/__init__.py说明整个 rosdep2 包都在这级目录下。接着用grep找出哪些文件写死了旧的默认数据源地址grep -rn raw.githubusercontent.com/ros/rosdistro /usr/lib/python3/dist-packages/rosdep2/-r递归读取目录内所有文件-n输出行号。这一步的意义是确认需要修补的文件范围而不是盲目 sed 整个目录。确认之后做统一替换sudo sed -i s#https://raw.githubusercontent.com/ros/rosdistro#https://mirrors.tuna.tsinghua.edu.cn/rosdistro#g \ /usr/lib/python3/dist-packages/rosdep2/*.pysed -i直接改原文件这里用#作为分隔符避免转义 URL 里的斜杠。替换之后rosdep 内部凡是默认抓取数据的路径都会指向镜像站处理逻辑完全不变只换数据来源。如果机器上仍是 Python 2 时代的 Melodic包路径可能是/usr/lib/python2.7/dist-packages/rosdep2/命令里的路径要对应改成这个。3.3 把 20-default.list 写成镜像源绕开 init 下载源码替换完成后再处理清单文件。我刚在 2.1 里写过一份镜像源版本的清单这里需要注意的是不要直接复制官方文件再改因为官方文件中的 URL 是完整绝对路径替换域名时容易漏行。直接按镜像站的目录结构重新写一份结构更可控。写入后立刻验证行数和地址wc -l /etc/ros/rosdep/sources.list.d/20-default.list cut -d -f2 /etc/ros/rosdep/sources.list.d/20-default.list | sort -uwc -l应该输出 6 行源声明加若干注释行cut取出每行第二列即 URLsort -u可以直观查看是否有重复或非镜像地址。校验通过后init 的下载动作就不再是瓶颈。3.4 固化成脚本分发到同构环境这类操作在一台机器上做一次容易放进脚本才能在多台机器或 CI 里复用。把 3.2 和 3.3 的内容合并成一个幂等脚本#!/usr/bin/env bash set -euo pipefail ROSDEP_PKG_DIR/usr/lib/python3/dist-packages/rosdep2 if [ -d /usr/lib/python2.7/dist-packages/rosdep2 ]; then ROSDEP_PKG_DIR/usr/lib/python2.7/dist-packages/rosdep2 fi sudo sed -i s#https://raw.githubusercontent.com/ros/rosdistro#https://mirrors.tuna.tsinghua.edu.cn/rosdistro#g \ $ROSDEP_PKG_DIR/*.py sudo mkdir -p /etc/ros/rosdep/sources.list.d/ sudo tee /etc/ros/rosdep/sources.list.d/20-default.list /dev/null EOF yaml https://mirrors.tuna.tsinghua.edu.cn/rosdistro/rosdep/osx-homebrew.yaml osx yaml https://mirrors.tuna.tsinghua.edu.cn/rosdistro/rosdep/base.yaml yaml https://mirrors.tuna.tsinghua.edu.cn/rosdistro/rosdep/python.yaml yaml https://mirrors.tuna.tsinghua.edu.cn/rosdistro/rosdep/ruby.yaml yaml https://mirrors.tuna.tsinghua.edu.cn/rosdistro/rosdep/nodejs.yaml yaml https://mirrors.tuna.tsinghua.edu.cn/rosdistro/rosdep/gentoo.yaml EOF echo rosdep source patch doneset -euo pipefail保证任意一步失败立即中断避免执行到一半留下残缺状态。路径判断兼容 Python 2 和 Python 3 两代环境bash 脚本本身也适合塞进各种自动化工具。真正要打包分发时把这份脚本和说明放在一起就是最常见的修复包结构。4. rosdep update 换源后重跑清理、拉取与验证4.1 清理半成品状态只要之前的 init 或 update 失败过本地必然留下半成品。/etc/ros/rosdep下可能有写入了一半的清单~/.ros/rosdep下可能有不完整的缓存。如果不清理重跑时可能出现源已存在或者加载坏缓存的二次报错。干净的做法是把这两个目录整体移走sudo rm -rf /etc/ros/rosdep rm -rf ~/.ros/rosdep有人习惯用mv备份而不是直接删这在交互式环境可以脚本里不建议残留目录会让后续判断结果不干净。注意只删 rosdep 相关目录不要把~/.ros整个删掉日志、录制包等重要内容都在这个目录下。4.2 换源后的 init 和 update 命令及输出判断清理之后按顺序执行sudo rosdep init rosdep update源码替换生效后init 会直接从镜像站下载 20-default.list 并写入系统目录正常情况下几秒内结束。接着 update 从镜像读取五类数据文件成功结束时输出多个Hit和 reading 记录见不到ERROR或Timeout。如果机器上有历史发行版依赖比如要维护老工程可以加上参数rosdep update --include-eol-distros--include-eol-distros让 update 也拉取已经停止维护的 ROS 发行版数据。默认情况下这些数据会被跳过涉及老版本依赖时反而会出现找不到目标发行版的困惑。4.3 用 rosdep check 确认依赖解析可用update 本身成功不等于依赖解析一定正确。最直接的验证是拉一个真实工程测试rosdep check --from-paths src --ignore-src--from-paths src告诉 rosdep 从src目录读取所有包定义--ignore-src跳过本地已有源码包的依赖检查只关心外部系统依赖。输出All system dependencies have been satisfied时说明缓存中的依赖索引可以支撑工程安装。如果这步报错继续看下一章的排查对照表。5. 高频报错对照排查从 timeout 到 YAML 解析错误5.1 报错信息与处理动作对照表下面这些报错是 init 和 update 失败时最常见的按报错特征、失败环节、处理动作三列整理报错片段失败环节处理动作The read operation timed outinit 下载清单或 update 拉取 YAML换成镜像源用 curl 验证连通性SSL: CERTIFICATE_VERIFY_FAILEDHTTPS 握手阶段更新ca-certificates后重试found character that cannot start any tokenupdate 解析 YAML清空~/.ros/rosdep后重新 updateThe source is already installedinit 写入阶段清理/etc/ros/rosdep后重跑Failed to download target platform data for gbpdistroupdate 获取发行版数据确认镜像站同步状态或切换另一镜像表格里最容易误判的是found character that cannot start any token。它表面上是 YAML 语法错误实际多数情况是 update 把某个 HTML 错误页当成 YAML 下载回来了内容里自然全是非法字符。出现这个报错先检查~/.ros/rosdep/sources.cache/下有没有体积异常大的文件有就删除整个缓存重跑。5.2 针对缓存损坏的定向清理实验缓存损坏有个典型可复现场景update 到一半网络中断再次 update 时缓存文件已经写入一半。这种问题不需要动/etc/ros/rosdep只清理用户态缓存即可rm -rf ~/.ros/rosdep/sources.cache rosdep update/etc/ros/rosdep下的清单仍然有效update会重新拉取并生成缓存。这个动作比全量清理省事适合在编译机、CI 节点上快速重试。执行完再回来看一眼sources.cache目录的更新时间时间戳刚刷新就说明拉取链路是通的。如果清理后还是反复失败用 2.3 的 curl 命令检查镜像地址确认不是镜像本身的问题。镜像站偶尔也会出现同步延迟或临时不可用这时换一个镜像源是成本最低的做法不需要再改源码。6. 收尾技巧在 Docker 和离线环境中复用 rosdep 缓存还有一个常被忽略的场景Docker 构建或者离线内网环境里rosdep 根本没有机会访问外网。这类环境里最优做法不是反复重试 update而是把上一次成功生成的缓存复制进新环境。先在联网机器上跑通一次 update然后把~/.ros/rosdep/sources.cache/和/etc/ros/rosdep/sources.list.d/20-default.list打包作为构建阶段的输入。Dockerfile 可以这样写FROM ros:noetic-ros-base-focal COPY sources.list.d/20-default.list /etc/ros/rosdep/sources.list.d/20-default.list RUN mkdir -p /root/.ros/rosdep COPY sources.cache /root/.ros/rosdep/sources.cache RUN rosdep install --from-paths /ros_ws/src --ignore-src -y拷入缓存后rosdep install不会触发重新 update依赖解析直接读本地缓存。构建过程完全与外网解耦不会出现 timeout 导致整个镜像构建失败的问题。COPY sources.cache这一层的缓存命中率很高只要依赖不变化后续构建会直接复用这一层镜像缓存构建速度也会明显加快。离线场景还有一招更省事在联网机器上跑一次rosdep install --simulate-only --from-paths src --ignore-src拿到依赖包列表然后直接apt-get download把 deb 包拉下来离线机器上用dpkg -i *.deb批量安装。这等于绕过了 rosdep 本身但对环境要求高包依赖顺序乱时会比较折腾。相比之下复制缓存是兼容性最好、行为最接近在线环境的方式。最后留一个可执行的验收动作。修复完成后的机器上执行time rosdep update /dev/null 21第一次修复前如果这条命令要三分钟修复后通常几秒到十几秒结束。速度变化本身就能说明数据通路是否真的切换到了稳定源上如果仍然卡顿回到第 5 章逐个排查链路timeout类报错几乎只出现在网络抓取阶段问题源头不会跑出20-default.list里的那几行地址。本文还有配套的精品资源点击获取
返回列表