简介ROS开发者在执行rosdep init与rosdep update时往往因网络问题无法从官方仓库拉取依赖包而频繁报错导致后续功能包安装寸步难行。这份配置包将这两条命令最终写入/etc/ros目录所需的全部文件预先打包下载解压后直接覆盖拷贝即可生效无需等待在线获取适用于校园网、内网等受限环境也适合刚接触ROS的初学者快速扫清环境配置障碍。zip压缩包体积非常轻量总计43KB仅包含6个文件其中5个为yaml格式的依赖源清单分别定义base、python、ruby、gentoo等系统模块的源信息另有一个list文件用于管理数据源索引结构一目了然备份和迁移都方便也便于核对默认源地址。目前已有1984人学习下载经多数使用者验证可快速解决init和update阶段各种网络报错让ROS环境配置回归顺畅节省大量排错时间值得在配置机器人开发环境时备一份。1. 完美解决 rosdep init 和 rosdep update 报错问题先把两个命令拆开看装好 Ubuntu 20.04 和 ROS Noetic满心欢喜敲下sudo rosdep init结果三秒后给你一行cannot download default sources list from: https://raw.githubusercontent.com/...——这个场面劝退了至少一半刚入门的 ROS 新手。rosdep 是 ROS 的依赖解析工具init 负责写入源列表update 负责把所有软件包的索引拉到本地缓存。这两步过不去后面 catkin_make 之前的依赖检查全部停摆。网上流传的“解决 zip”拆开看无非三招修正域名解析、把官方源换成可访问的镜像、把 rosdep2 源码里的硬编码地址一并替换。本文就按这三招展开适合 Ubuntu 18.04/20.04 的 ROS1 用户ROS2 的 rosdep 流程同样适用。你不需要解压一个神秘包只需要知道每步在干什么。2. init 和 update 各自在拉什么两个报错不是同一个病根2.1 rosdep init只拉一个文件为什么还翻车sudo rosdep init做的事情非常单一从https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/sources.list.d/20-default.list下载一个文本文件放到/etc/ros/rosdep/sources.list.d/20-default.list。/etc/ros是 root 才能写的目录所以 init 必须加 sudo。整个依赖下载链路里init 只是“拿到一份地址清单”它自己不下载任何包索引。但恰恰是这个“只拉一个文件”的动作失败率最高。原因通常不在 GitHub 本身而是raw.githubusercontent.com这个域名在你的网络环境里解析出来的 IP 不可达或者 TLS 握手在中途被断开。你可以用下面两条命令现场确认问题出在哪一层getent hosts raw.githubusercontent.com curl -I --connect-timeout 5 https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/sources.list.d/20-default.listgetent读的是系统/etc/hosts和 DNS 配置能看到当前域名被解析成哪些 IPcurl -I只取响应头--connect-timeout 5让连接在 5 秒内失败而不是无限等。如果getent返回的 IP 列表里第一个地址连不通curl 会一直尝试到超时最终报错。把这两个输出对比一下能直接判断是“没解析出来”还是“解析出来但连不上”。2.2 rosdep update一次递归展开几十个 yaml网络稍差就 timeoutrosdep update的体量完全不一样。它先去读/etc/ros/rosdep/sources.list.d/下每个.list文件然后逐个下载文件里列出的 yamlbase.yaml、python.yaml、ruby.yaml还有 gbpdistro 条目指向的releases/fuerte.yaml。这些 yaml 内部还会引用index-v4.yaml和各个发行版的 distribution 文件rosdep 需要递归展开最终把“每个 ROS 包依赖哪些系统包”的映射关系写进缓存。所以 update 实际要访问的 URL 数量不是几个而是几十个且全部集中在raw.githubusercontent.com域名下。只要这个域名解析或连通性有问题update 就会在中途抛The read operation timed out或者某几个 yaml 下到一半变成残缺文件随后报ERROR: unable to process source。这就能解释一个常见现象有人把 hosts 改好之后 init 秒过但 update 跑到一半仍然翻车——因为 init 只依赖一次连接update 依赖几十次。2.3 报错文本先归类下载失败、超时、TLS 校验失败是三种路线在动手改任何配置之前先把报错字符串归类。同一套措辞往往对应不同的失败阶段修法完全不同报错文本截取失败阶段主要根因cannot download default sources list frominitraw.githubusercontent.com 域名解析或连接失败The read operation timed outupdate多次连接中断部分文件未下载成功ERROR: unable to process sourceupdateyaml 文件下载不完整或内容损坏SSL: CERTIFICATE_VERIFY_FAILEDinit / update系统时间与真实时间偏差过大我处理这类问题的习惯是先跑一次rosdep update把完整输出存到日志文件再grep -E ERROR|timed out|SSL看是哪一类。如果是 init 阶段报错重点查域名解析如果是 update 中途超时重点换源或换本地文件如果报证书错误先对时间别急着改 hosts。归类对了后面的操作才不会白做。3. 从 init 报错切进去hosts 修正与手工兜底写源3.1 验证域名解析把 raw.githubusercontent.com 指到能用的 IPinit 报cannot download default sources list时第一步不是改 rosdep 配置而是看当前机器把raw.githubusercontent.com解析成了什么。常见做法是用getent ahosts拿到全部解析结果再手工挑一个能连通的 IP 写进/etc/hostsgetent ahosts raw.githubusercontent.com nslookup raw.githubusercontent.com如果这两个命令返回的 IP 连不通或者根本没返回就找一台能正常访问 GitHub 的机器在上面执行同样命令把得到的 IP 拿过来。GitHub raw 内容托管在 Fastly 边缘节点上常见地址段是185.199.108.133这类但不同地区、不同运营商可达性不一样不要照抄网上任意一个 IP要以你现场能连通的那个为准。确认 IP 后追加 hostssudo sh -c echo 185.199.108.133 raw.githubusercontent.com /etc/hosts getent hosts raw.githubusercontent.com追加之前先grep -n raw.githubusercontent.com /etc/hosts看有没有旧记录同一域名重复写多行系统只认第一行旧记录会干扰验证。写完 hosts 再跑curl -I能拿到HTTP/2 200就说明 init 这关的网络通路已经通了。3.2 init 死活不过时直接手写 20-default.list等于跳过 init如果 hosts 已经写上还是不稳定或者你压根不想折腾域名解析可以用手工方式直接把 init 的结果写出来。rosdep init 的全部意义就是生成/etc/ros/rosdep/sources.list.d/20-default.list这一个文件既然网络拉不下来那就自己写一份等价文件写入后完全可以跳过 init。sudo mkdir -p /etc/ros/rosdep/sources.list.d sudo tee /etc/ros/rosdep/sources.list.d/20-default.list EOF yaml https://gitee.com/your-id/rosdistro/raw/master/rosdep/base.yaml yaml https://gitee.com/your-id/rosdistro/raw/master/rosdep/python.yaml yaml https://gitee.com/your-id/rosdistro/raw/master/rosdep/ruby.yaml gbpdistro https://gitee.com/your-id/rosdistro/raw/master/releases/fuerte.yaml fuerte EOF把your-id替换成你实际使用的 Gitee 镜像账号。操作方法是在 Gitee 上注册账号找到别人已经镜像好的rosdistro仓库或者自己从 GitHub 导入一份把仓库里rosdep/和releases/目录的 raw 路径填进去。这里刻意省略了官方文件里的osx-homebrew.yaml osx那一行——那行只在 macOS 上有意义Linux 机器不写不影响任何功能。文件写完后sudo rosdep init这个命令就不再需要了直接进rosdep update。3.3 写完怎么确认来源正确手工写完源要确认文件真的落在了 rosdep 会读取的位置并且内容没有被截断。检查命令ls -l /etc/ros/rosdep/sources.list.d/ cat /etc/ros/rosdep/sources.list.d/20-default.list正常情况下列表里应该只有一个20-default.list大小在几百字节左右内容末尾是fuerte那行。如果发现目录里有多个.list文件说明之前残留了旧版本 rosdep 的配置建议备份后清空目录再放这一个文件。确认无误后这一步就算完成了。要特别记住手工写完了这个文件后面不要再手贱跑sudo rosdep initinit 会用官方地址覆盖你写好的内容这一点我在第 5 章会展开讲。4. 治 update 的根替换 rosdep2 源码里的硬编码 URL或走 file:// 本地源4.1 先用 Python 定位 rosdep2 安装路径再批量 sed 换源rosdep update下载索引时除了读/etc/ros/rosdep/sources.list.d/里的地址还有一部分 URL 是硬编码在 Python 源码里的。典型的是rosdep2/sources_list.py里的默认源地址、rosdep2/gbpdistro_support.py里的releases/fuerte.yaml模板以及rosdistro/__init__.py里的index-v4.yaml。所以就算你手工把 20-default.list 换成了 Gitee 地址update 过程中仍可能有几个请求打在raw.githubusercontent.com上这就是很多人换源后 update 依然超时的原因。先确认 rosdep2 到底装在哪个 Python 路径下python3 -c import rosdep2, rosdistro; print(rosdep2.__file__, rosdistro.__file__)Ubuntu 20.04 Noetic 的输出通常是/usr/lib/python3/dist-packages/rosdep2/__init__.pyUbuntu 18.04 Melodic 则是/usr/lib/python2.7/dist-packages/rosdep2/__init__.py。拿到路径后把dist-packages目录下三处文件里的官方域名统一替换成你的 Gitee 镜像sudo sed -i s|https://raw.githubusercontent.com/ros/rosdistro/master|https://gitee.com/your-id/rosdistro/raw/master|g \ /usr/lib/python3/dist-packages/rosdep2/sources_list.py \ /usr/lib/python3/dist-packages/rosdep2/gbpdistro_support.py \ /usr/lib/python3/dist-packages/rosdep2/rep3.py \ /usr/lib/python3/dist-packages/rosdistro/__init__.py这条 sed 命令把“域名仓库路径”整段前缀做了全局替换your-id必须和 20-default.list 里的镜像账号保持一致否则会出现 sources.list 走 A 镜像、源码兜底走 B 镜像的分裂状态。替换完先grep -n gitee.com验证每个文件都命中再跑 update。备份这一步建议做sudo cp每个文件成.bak后缀换源翻车时可以一条命令还原。4.2 更省心的本地化方案clone rosdistro 后用 file:// 更新如果你的工作环境对镜像也不稳定或者干脆是内网机器、隔离网络最可靠的手段是把整个 rosdistro 仓库拉到本地让 rosdep 走file://协议。这个方案和网上各种 zip 包的离线思路一致把官方索引文件当作静态资源搬运到目标机器上。先在能联网的机器上把仓库 clone 下来推荐--depth 1只取最新快照rosdep 用不到历史记录mkdir -p ~/rosdistro_local cd ~/rosdistro_local git clone --depth 1 https://gitee.com/your-id/rosdistro.git然后修改/etc/ros/rosdep/sources.list.d/20-default.list把所有 URL 换成绝对路径。注意file://后面跟的是绝对路径不能用~代替用户目录rosdep2 不会帮你展开波浪号sudo tee /etc/ros/rosdep/sources.list.d/20-default.list EOF yaml file:///home/yourname/rosdistro_local/rosdistro/rosdep/base.yaml yaml file:///home/yourname/rosdistro_local/rosdistro/rosdep/python.yaml yaml file:///home/yourname/rosdistro_local/rosdistro/rosdep/ruby.yaml gbpdistro file:///home/yourname/rosdistro_local/rosdistro/releases/fuerte.yaml fuerte EOF同一台机器上如果 4.1 节的 sed 也执行过要确认 20-default.list 里的file://路径没有被 sed 影响。实际上 sed 只替换https://raw...前缀对file://开头的行无副作用两者可以共存。本地化的额外收益是update 变成纯本地文件读取速度从几分钟降到几秒而且每次报错都能直接打开 yaml 检查内容是否完整。4.3 怎么算真正成功source.cache 文件与 rosdep update 的输出跑rosdep update时不要只盯着有没有红色 ERROR看完整输出更靠谱。走本地源时的正常输出大致是reading in sources list data from /etc/ros/rosdep/sources.list.d/20-default.list Hit file:///home/yourname/rosdistro_local/rosdistro/rosdep/base.yaml Hit file:///home/yourname/rosdistro_local/rosdistro/rosdep/python.yaml Query rosdistro index file:///home/yourname/rosdistro_local/rosdistro/index-v4.yaml ... rosdep update finished末尾出现rosdep update finished才算结束。之后检查缓存文件ls -l ~/.ros/rosdep/source.cache head -20 ~/.ros/rosdep/source.cachesource.cache是 update 的最终产物里面保存着全部包索引的映射关系。文件存在且开头不是ERROR就说明 update 真正过了。注意缓存是写在当前用户家目录的sudo rosdep update会写到/root/.ros普通用户跑则写到/home/xxx/.ros这直接引出第 5 章的第一个坑。5. 避坑排查init 过了 update 还翻车的 5 类现场5.1 前半程正常、后半程 timeout现象update 开头几个Hit都很顺利跑到中段突然卡住然后The read operation timed out。 原因rosdep update 是递归展开的前几个 yaml 只是入口中段会去拉index-v4.yaml和各发行版的 distribution 文件这些请求可能落在完全不同的 URL 或 hosts 策略没覆盖到的节点上。你只解决了入口域名没解决全部请求。 解决这类情况不要继续在 hosts 上打补丁直接切到 4.2 节的file://方案。本地文件不存在“中段超时”如果本地文件缺失报错会明确指向具体 yaml 路径用ls检查一下就知道是 clone 不完整还是路径拼错。5.2 sudo 和普通用户交替跑 update缓存不在一个目录现象sudo rosdep update明明成功切回普通用户跑rosdep update又提示找不到缓存或重新下载。 原因rosdep update 的缓存写在执行者的家目录下。sudo 跑写进/root/.ros/rosdep/普通用户跑写进/home/当前用户/.ros/rosdep/两边互不相通。init 需要 sudo但 update 根本不需要管理员权限。 解决统一用普通用户执行rosdep update。如果之前用 sudo 跑过把 root 下的缓存删掉或忽略让普通用户完整更新一次后续rosdep install也用普通用户跑权限模型才一致。5.3 hosts 改了报错却和之前一模一样现象/etc/hosts里已经追加了 raw.githubusercontent.com 的记录getent hosts也返回了新 IP但 init 报错文本一字不差。 原因三个常见来源。一是/etc/hosts里存在多行同一域名记录系统只认第一行旧行抢先了二是 rosdep 或 curl 走的解析栈不一定读 hosts某些环境下 nsswitch 配置导致files优先级低三是你验证时用curl -I https://raw.githubusercontent.com能通但 init 请求的是完整路径证书 SNI 或重定向行为不同。 解决先用cat -n /etc/hosts清理重复行再用grep -q raw /etc/hosts echo ok确认然后直接跑一次curl -I完整 URL 复现看是不是 200。若确认 hosts 没问题仍失败就放弃 hosts 这条线直接走 4.2 节手写源或file://不要死磕。5.4 证书校验失败先对系统时间现象报错里有SSL: CERTIFICATE_VERIFY_FAILED网络看起来通别的 HTTPS 网站也能开。 原因系统时间比真实时间慢或快太多TLS 校验证书有效期时失败。Rosdep 装完索引要校验 GitHub 证书时间不对时证书被认为“过期”或“未生效”。 解决先date看时间偏差大就用sudo timedatectl set-ntp true打开自动同步或手动sudo date -s 2025-01-01 12:00:00校准后再跑 update。时间修正后证书报错会直接消失不需要改任何源。5.5 手写好的源被 init 再次覆盖现象手工写好 20-default.listupdate 也成功了隔天或另开一个终端手滑又跑了一次sudo rosdep init之后 update 报错回到最初的cannot download default sources list。 原因rosdep init每次执行都会重写/etc/ros/rosdep/sources.list.d/20-default.list内容恢复成官方raw.githubusercontent.com地址手工配置被无声覆盖。问题的关键是很多人习惯顺手补 init以为“初始化嘛多跑一遍无害”。 解决写完 20-default.list 后把它设为只读sudo chmod 444 /etc/ros/rosdep/sources.list.d/20-default.list。之后再跑 init 会因权限不足报错但 update 不受影响等于用文件系统权限拦住手贱。等你确认整套 rosdep 流程稳定了再考虑要不要恢复写权限。6. 把补丁沉淀成脚本备份、幂等替换和离线搬运逐个敲命令治标不治本尤其是换机器重装 ROS 时同样的坑要再踩一遍。我会把前面所有操作打包成一个 bash 脚本做到重复执行不产生副作用#!/bin/bash set -euo pipefail stamp$(date %Y%m%d_%H%M%S) sudo mkdir -p /etc/ros/rosdep/sources.list.d [ -f /etc/ros/rosdep/sources.list.d/20-default.list ] \ sudo cp /etc/ros/rosdep/sources.list.d/20-default.list \ /etc/ros/rosdep/sources.list.d/20-default.list.bak_$stamp grep -q raw.githubusercontent.com /etc/hosts || \ sudo sh -c echo 185.199.108.133 raw.githubusercontent.com /etc/hosts python3 -c import rosdep2, rosdistro sudo sed -i s|https://raw.githubusercontent.com/ros/rosdistro/master|https://gitee.com/your-id/rosdistro/raw/master|g \ /usr/lib/python3/dist-packages/rosdep2/*.py \ /usr/lib/python3/dist-packages/rosdistro/*.py rosdep update脚本结构按三件事组织先备份现有配置保证翻车有后悔药再通过grep -q判断 hosts 是否已有记录避免重复追加最后替换源码 URL 并执行 update。set -euo pipefail会让脚本在第一步出错时立即退出不会带着半截配置继续跑。这里没有做复杂逻辑但已经把“可重复执行”和“失败可回滚”两个基本点落实了。离线机器上的用法是把脚本、rosdistro 仓库快照、写好的 20-default.list 模板一起打包带走。目标机上先解压仓库到固定路径再按 4.2 节修改 20-default.list 里的路径最后执行脚本前把 sed 那行注释掉——离线环境不需要换源重复执行也没有意义。部署完成后用rosdep check --dependencies随便指定一个包验证能正常解析依赖就说明整个链路是通的。我自己的教训是第一次折腾时手工写好了源又忍不住补跑了一次 init结果配置被官方地址覆盖来回试了两个小时。后来把文件权限改成只读再顺手把备份、hosts、sed 三个动作写成一个脚本从那以后换机器重装 ROS 再没在这个环节耗过时间。rosdep 这套流程本身不难难的是一遍遍重复踩同一个坑。希望这份思路帮你也少走一趟弯路。本文还有配套的精品资源点击获取