1. 为什么要在 WSL2 里折腾 ROS2 + CodexCLI
如果你正在学机器人操作系统,大概率会遇到一个尴尬局面:Windows 上装 ROS2 各种依赖打架,虚拟机里跑 Gazebo 又卡到怀疑人生。WSL2 刚好卡在中间——它跑的是真 Linux 内核,能直接apt装 ROS2 Humble,同时 WSLg 还能把 RViz2、Gazebo 的图形窗口弹到 Windows 桌面上。这套组合我实测下来,比传统虚拟机省一半内存,编译速度也接近原生。
但光有环境还不够。ROS2 的坑在于:包依赖、colcon build报错、Gazebo 和 ROS2 话题对不上、桥接参数写错一个符号就全废。这时候如果有个能读懂你工作区、帮你补全节点代码、解释报错的命令行助手,效率会高很多。CodexCLI 就是干这个的——它能在终端里直接读你的src/目录,生成 publisher/subscriber 骨架,甚至帮你写ros_gz_bridge的桥接命令。
这篇要解决的核心问题是:怎么从零创建一个干净的 WSL2 Ubuntu 22.04,装好 ROS2 Humble + Gazebo Fortress,把 CodexCLI 接进来辅助开发,并且用一个统一的 Key 通道(TaoToken)管理模型调用。适合谁?适合正在做 ROS2 操作系统实验、需要反复编译验证、又不想在环境配置上耗一整天的同学。下面每一步都是可复制的命令,你跟着敲就能复现。
2. TaoToken 前置:统一 Key 与 config.toml 骨架
在把 CodexCLI 接进 WSL2 之前,先解决模型调用通道的问题。CodexCLI 这类工具默认走官方端点,但如果你同时用多个模型(比如写代码用一个、解释报错用另一个),每个都配一遍 Key 很烦。TaoToken 提供的是一个统一入口:一个 Key 对应多个模型,通过config.toml声明式配置,CLI 侧只需要指向同一个 base_url。
官网入口在这里:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 端点是 https://taotoken.net/api (注意这个不加 UTM 参数,直接填进配置里)。
先在你的 WSL2 里建配置目录。CodexCLI 和很多同类工具都遵循~/.config约定:
mkdir -p ~/.config/codex mkdir -p ~/.config/taotoken然后是config.toml骨架。这个文件我建议放在~/.config/codex/config.toml,CodexCLI 启动时会读它。核心字段是base_url、api_key、model:
# ~/.config/codex/config.toml # TaoToken 统一 Key 接入骨架 [provider] name = "taotoken" base_url = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY" # 从环境变量读,不硬编码 timeout_seconds = 120 [model] default = "claude-sonnet-4-20250514" fallback = "gpt-4o" [cli] auto_context = true # 自动读取当前工作区文件 max_context_files = 20 ignore_dirs = ["build", "install", "log", ".git"] [ros2] workspace_hint = "~/code/ros2_ws"这里有个关键设计:api_key_env指向环境变量,而不是把 Key 明文写进 toml。这样你git commit的时候不会手滑泄露。环境变量在~/.bashrc里设置:
# ~/.bashrc 追加 export TAOTOKEN_API_KEY="你的Key" export TAOTOKEN_BASE_URL="https://taotoken.net/api"改完source ~/.bashrc生效。注意base_url结尾不要带斜杠,很多 CLI 拼接路径时会因此产生双斜杠导致 404。
如果你更习惯用环境变量完全替代 toml,也可以只保留base_url在 toml 里,Key 全走 env。两种都行,看你团队规范。我倾向 toml 管结构、env 管密钥,职责分开。
3. 可复制配置:WSL2 创建 + ROS2 + CodexCLI 全流程
3.1 清理旧发行版并安装 Ubuntu 22.04
先看当前装了哪些。如果你之前折腾过,残留的注册表会让新装出问题:
wsl -l -v如果列表里有旧的 Ubuntu-22.04 或 Ubuntu-20-Noetic,先注销:
wsl --unregister Ubuntu-22.04 wsl --unregister Ubuntu-20-Noetic注销完再验证一次,应该提示没有已安装的分发:
wsl -l -v然后安装干净的 Ubuntu 22.04:
wsl --install -d Ubuntu-22.04装完确认版本是 2:
wsl -l -v期望看到Ubuntu-22.04 Running 2。进系统:
wsl -d Ubuntu-22.043.2 基础开发工具
进 Ubuntu 后第一件事是补全编译链。ROS2 的colcon依赖 Python 和 gcc,缺一个都会在 build 阶段报奇怪的错:
sudo apt update sudo apt install -y build-essential git curl wget unzip zip \ ca-certificates gnupg lsb-release software-properties-common \ python3 python3-pip python3-venv验证一下版本,确认都在:
git --version python3 --version pip3 --version gcc --version3.3 安装 CodexCLI
CodexCLI 有两种装法。官方脚本:
curl -fsSL https://chatgpt.com/codex/install.sh | sh source ~/.bashrc codex --version或者走 npm。我推荐 npm,因为版本管理更清晰,升级也方便:
node -v npm -v如果没装 Node,用 NodeSource 装 22.x:
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt install -y nodejs然后全局装:
sudo npm install -g @openai/codex验证:
codex --version3.4 安装 ROS2 Humble
这里不用一键脚本,走官方源,排查问题时链路清楚。先加 ROS2 的 GPG key 和源:
sudo add-apt-repository universe sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key \ -o /usr/share/keyrings/ros-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" \ | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null sudo apt update sudo apt install -y ros-humble-desktop装完把 ROS2 环境写进~/.bashrc,省得每次开终端都手动 source:
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc source ~/.bashrc echo $ROS_DISTRO期望输出humble。
3.5 编译依赖与工作区
sudo apt install -y python3-colcon-common-extensions python3-rosdep python3-vcstool sudo rosdep init rosdep update建工作区。注意路径一定放在 Linux 文件系统里,别放/mnt/c,否则colcon build会慢到你想砸键盘:
mkdir -p ~/code/ros2_ws/src cd ~/code/ros2_ws3.6 创建功能包并编译
cd ~/code/ros2_ws/src ros2 pkg create my_first_pkg --build-type ament_python --dependencies rclpy std_msgs cd ~/code/ros2_ws colcon build source install/setup.bash ros2 pkg list | grep my_first_pkg看到my_first_pkg就说明包注册成功。
3.7 安装 Gazebo Fortress
ROS2 Humble 对应的新一代 Gazebo 是 Fortress,通过ros_gz系列包集成:
sudo apt install -y ros-humble-ros-gz which gz which ign如果which ign有输出,说明 Fortress 命令体系在。验证包:
source /opt/ros/humble/setup.bash ros2 pkg list | grep ros_gz应该看到ros_gz、ros_gz_bridge、ros_gz_sim、ros_gz_image。
4. 验证请求:从节点通信到 Gazebo 桥接
4.1 写一个 publisher 和 subscriber
在my_first_pkg里建两个节点文件。先看目录:
cd ~/code/ros2_ws/src/my_first_pkg/my_first_pkg lssimple_publisher.py:
import rclpy from rclpy.node import Node from std_msgs.msg import String class SimplePublisher(Node): def __init__(self): super().__init__('simple_publisher') self.pub = self.create_publisher(String, 'chatter', 10) self.timer = self.create_timer(1.0, self.tick) def tick(self): msg = String() msg.data = 'Hello from my_first_pkg' self.pub.publish(msg) self.get_logger().info(f'Publishing: {msg.data}') def main(): rclpy.init() node = SimplePublisher() rclpy.spin(node) node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()simple_subscriber.py:
import rclpy from rclpy.node import Node from std_msgs.msg import String class SimpleSubscriber(Node): def __init__(self): super().__init__('simple_subscriber') self.sub = self.create_subscription(String, 'chatter', self.cb, 10) def cb(self, msg): self.get_logger().info(f'I heard: {msg.data}') def main(): rclpy.init() node = SimpleSubscriber() rclpy.spin(node) node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()改setup.py注册入口:
entry_points={ 'console_scripts': [ 'simple_publisher = my_first_pkg.simple_publisher:main', 'simple_subscriber = my_first_pkg.simple_subscriber:main', ], },重新编译:
cd ~/code/ros2_ws colcon build source install/setup.bash4.2 跑起来验证
终端 1:
source /opt/ros/humble/setup.bash source ~/code/ros2_ws/install/setup.bash ros2 run my_first_pkg simple_publisher终端 2:
source /opt/ros/humble/setup.bash source ~/code/ros2_ws/install/setup.bash ros2 run my_first_pkg simple_subscriber终端 2 应该持续打印I heard: Hello from my_first_pkg。再开终端 3 看话题:
ros2 topic list ros2 topic echo /chatter ros2 topic info /chatterros2 topic info会显示Type: std_msgs/msg/String,Publisher count: 1,Subscription count: 1。
4.3 Gazebo 稳定启动
WSL2 下直接跑ign gazebo empty.sdf经常撞 OGRE 渲染错误。稳定方式是强制软件渲染:
LIBGL_ALWAYS_SOFTWARE=1 ign gazebo empty.sdf嫌命令长就加别名:
echo "alias ign-gazebo-safe='LIBGL_ALWAYS_SOFTWARE=1 ign gazebo'" >> ~/.bashrc source ~/.bashrc ign-gazebo-safe empty.sdf4.4 桥接 /clock
Gazebo 和 ROS2 默认是两套通信系统,话题互相看不见。用ros_gz_bridge把仿真时钟桥过来。
终端 1 保持 Gazebo 运行。终端 2:
source /opt/ros/humble/setup.bash ros2 run ros_gz_bridge parameter_bridge /clock@rosgraph_msgs/msg/Clock[ignition.msgs.Clock方括号[表示单向,从 Gazebo 到 ROS2。终端 3:
source /opt/ros/humble/setup.bash ros2 topic list ros2 topic echo /clock ros2 topic info /clockros2 topic echo /clock会持续输出sec和nanosec,说明仿真时间已经流进 ROS2。ros2 topic info显示类型rosgraph_msgs/msg/Clock。
4.5 用 CodexCLI 辅助
回到工作区,让 CodexCLI 读上下文:
cd ~/code/ros2_ws codex在交互里输入类似「解释 src/my_first_pkg 里 publisher 和 subscriber 的通信链路」,它会读文件后给出解释。你也可以让它生成一个新的 launch 文件骨架,或者把ros_gz_bridge的命令整理成脚本。config.toml里的auto_context和ignore_dirs就是为这个场景准备的——它会跳过build/install/log,只读源码。
5. 本篇常见错排查
ros2 run找不到包:九成是没 source 工作区。每次新终端都要source ~/code/ros2_ws/install/setup.bash。确认包在不在用ros2 pkg list | grep my_first_pkg。
ros2 --version报错:ROS2 的ros2命令不支持--version。查版本用echo $ROS_DISTRO,查帮助用ros2 --help。
Gazebo 一开就崩:WSL2 的 GPU 直通对 OGRE 兼容性不稳。加LIBGL_ALWAYS_SOFTWARE=1强制软件渲染,或者用上面那个别名。
ROS2 看不到 Gazebo 话题:这是正常的,两套系统默认隔离。必须用ros_gz_bridge显式桥接。桥接命令里方向符号写错([和])会导致话题不出现,Gazebo→ROS2 用[。
colcon build特别慢:检查工作区是不是在/mnt/c下。移到~/code这种 Linux 原生路径,速度差好几倍。
CodexCLI 读不到文件:确认config.toml里workspace_hint指向正确路径,且ignore_dirs没把src误伤。启动时用pwd确认当前目录在工作区内。
桥接后/clock没数据:先确认 Gazebo 真的在跑(ign topic -l能看到/world/empty/clock),再确认桥接终端没报错。桥接进程和 Gazebo 必须同时活着。
6. 把 Key 通道和 CLI 沉淀成日常流程
环境搭好只是开始,真正省时间的是把「改代码 → 编译 → 验证 → 问助手」这条链路固定下来。我的做法是在~/.bashrc里加几个函数,把 source 和 build 打包:
rosbuild() { cd ~/code/ros2_ws colcon build --symlink-install source install/setup.bash }--symlink-install对 Python 包特别有用,改完.py不用重新 build 就能生效,省掉大量等待。
模型调用这块,统一 Key 的价值在长期编码场景里最明显。如果你只是偶尔问一句,用网页版模型对话就够了;但如果你要反复让 CLI 读工作区、生成节点、解释colcon报错,那就该走 Coding Plan 这类长期通道,把 Key 和额度固定下来,避免每次临时配。接入文档里有config.toml的完整字段说明和不同 CLI 的适配方式,排障时对着看比瞎猜快。
最后提醒一个容易忽略的点:config.toml里的base_url和api_key_env是两个独立维度,换模型不用换 Key,换 Key 不用动模型配置。把这两件事解耦,你后面加新模型、切新通道都不会牵一发动全身。ROS2 实验本身已经够多变量了,工具链这边能少一个变量就少一个。