ARTICLE DETAIL

资讯详情

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

Jetson小车开发:从Jupyter到VSCode远程调试的完整实践

Jetson小车开发:从Jupyter到VSCode远程调试的完整实践 做Jetson小车开发这几年我身边不少朋友一上来就用Jupyter Notebook调车。快速验证一个颜色识别算法、看看摄像头拍的画面Jupyter确实够方便。但一旦跑到Rosmaster这种真正上车的控制程序——节点一多、线程一多、状态一复杂Jupyter的体验就会断崖式下跌。我后来花了一周时间把整套开发流程从Jupyter迁移到VSCode远程开发整个调试效率可以说是质的提升。这篇就完整聊聊我现在的这套VSCode Jetson Rosmaster的Python远程调试方案从原理讲到实操把踩过的坑都给你摊开。1. 为什么要告别Jupyter转向VSCode远程开发先用真实场景回忆一下Jupyter有多痛。1.1 Jupyter写ROS程序的三个致命伤状态不可控机器人程序的调试往往是一个漫长的过程。你在Jupyter里从上往下执行单元格第5个格子里跑了一段while循环结果没等跑完你就手动中断了过一会儿你又从第3个格子开始执行此时全局变量早已被改得乱七八糟。ROS程序里这种问题更危险因为节点、Topic、Service往往有全局生命周期你重复初始化了一个Publisher并且没有shutdown整个话题的收发就可能错乱你打开过一次摄像头后面再开就直接Failed to open device。Jupyter的交互模式天然鼓励这种“反复跑同一段代码”的习惯但在真实机器人系统里这种习惯就是各种神秘bug的温床。断点调试形同虚设Jupyter虽然也能通过辅助插件打断点但跟真正IDE的调试体验相比差距太大。调试机器人程序的时候你最需要的是看到“程序现在停在哪一行”“当前self状态是什么”“这个列表里到底存了什么东西”。在Jupyter里这些基本都只能靠print来猜。print本身没问题但一旦代码量上来你print的变量数量失控输出窗口根本没法看找一条关键日志得翻半天效率极低。更别提条件断点、逐变量监视这些IDE基本功在Jupyter里基本是奢望。工程结构完全组织不起来Rosmaster这种系统级程序很少是单个Python文件——它要管理底盘运动、传感器数据、决策逻辑、通讯协议还会拆出大类、工具函数、消息定义。Jupyter的.ipynb是基于单元格的文档结构只能在一个文件里按顺序执行根本没法支撑这种工程化。你想import自己的工具模块路径处理、相对导入、参数传递全是坑。你可能在Jupyter里写了一两年算法demo但真正完整跑一个上车项目时发现连个代码跳转都做不到全局搜索一个函数定义都要靠肉眼翻。1.2 VSCode Remote-SSH真正的优势在哪VSCode远程开发Remote-SSH解决的就是上面三件事同时它还保留了Jupyter最好的部分界面在本地运行在远端。我把最核心的几个优势按实际使用频率排一下真正的断点调试。点一下行号程序停住左侧变量窗口实时显示所有局部变量。你在Jupyter里千辛万苦print出来的东西在VSCode里只需要看一眼WATCH窗口就能解决。工程级代码管理能力。整个ROS工作空间就是一个普通的文件夹文件树、全局搜索、代码跳转、右键引用查找、Git可视化这些功能在机器人工程里几乎是必需品。终端和编辑器无缝衔接。VSCode内置终端打开的就是Jetson上的shell你可以直接source devel/setup.bash、roslaunch xxx.launch然后切回编辑器继续看代码不再需要来回切换窗口。Python语言服务Pylance。在远程环境下Pylance能提供完整的类型检查、自动补全、快速修复对rospy、sensor_msgs这些库的识别比Jupyter智能太多。也许有人会说Jupyter也能连远程kernel啊也能查看变量啊对但大家要清楚一点Jupyter的远程kernel更多是为了“跑在远端、结果回传”它的编辑器、调试器、代码结构是割裂的。你写代码还是在网页端密密麻麻的单元格里断点、监视、调用堆栈、项目文件导航这些能力完全不在一个量级。1.3 Remote-SSH到底是怎么工作的VSCode的Remote-SSH并不是让你在本地打开一个远端文件这么简单。它的本质是本地VSCode界面UI通过SSH隧道连接到Jetson然后在Jetson上自动启动一个vscode-server后端进程。你打开的每一个文件都保存在远端每一次补全、跳转、语法检查都由远端的vscode-server处理结果再回传到本地界面渲染。这种架构有个很优雅的好处它对本地电脑性能要求很低因为重活都在Jetson上完成同时对网络要求也不高普通Wi-Fi环境下延迟也能接受因为我实际测试下来只要网络延迟在几十毫秒以内打字、补全几乎感觉不到差别。它唯一的弱点是断网时连接会立刻卡住所以后面我会专门讲SSH的KeepAlive配置问题。2. 开始之前软硬件环境准备与常见版本陷阱正式动手之前先把环境梳理清楚。这一步如果草率后面会浪费大量时间在版本兼容性上。2.1 Jetson板卡、JetPack和Python版本的对应关系我接触过的Jetson板卡主要是Nano、Xavier NX、Orin NX、AGX Orin这些。不同板卡出厂预装的JetPack版本不同对应的Ubuntu和Python版本也有差异这对后续选择解释器非常关键。板卡JetPack版本Ubuntu版本默认系统PythonJetson NanoJetPack 4.6Ubuntu 18.04Python 3.6Jetson Xavier NXJetPack 4.6 / 5.0Ubuntu 18.04 / 20.04Python 3.6 / 3.8Jetson AGX XavierJetPack 4.xUbuntu 18.04Python 3.6Jetson Orin NXJetPack 5.x / 6.xUbuntu 20.04 / 22.04Python 3.8 / 3.10Jetson AGX OrinJetPack 5.x / 6.xUbuntu 20.04 / 22.04Python 3.8 / 3.10网上搜jetson agx xavier python3.6基本都是JetPack 4.x时代的老用户。那部分板子的ROS发行版一般是Melodic。如果你是Ubuntu 20.04 ROS NoeticPython就是3.8。这个差异会在后面选解释器的时候体现出来先把版本底数摸清楚调试的时候才不会一头雾水。2.2 Jetson端SSH服务的配置Jetson刷好JetPack镜像后很多出厂镜像并没有自动开启SSH服务或者默认只允许Ubuntu用户本机登录。所以第一步先把SSH打开sudo apt update sudo apt install openssh-server -y sudo systemctl enable --now ssh sudo systemctl status ssh看到active(running)说明服务起来了。然后查IPhostname -I建议把Jetson设成固定IP。我吃过太多次亏调试到一半路由器重启Jetson的IP变了VSCode直接断连还得爬起来接显示器查IP。在小车这种移动场景下固定IP绝对能帮你省下大量时间和血压。设置方法可以在路由器后台做MAC绑定也可以用Jetson自带的NetworkManager图形界面设置。另外如果你打算用VSCode调试时需要sudo权限比如配置串口、摄像头权限建议在Jetson上配置一下sudo免密sudo visudo在文件末尾添加一行ubuntu ALL(ALL) NOPASSWD: ALL注意这里的ubuntu要换成你自己的用户名。这样远程调试时不会因为sudo密码输入而卡住流程。这个小细节在自动化跑测试的时候尤其有用。2.3 本地VSCode与插件安装本地电脑上的VSCode可以直接从官网下载稳定版。国内网络环境不好的话也可以走VSCode的国内镜像源加速下载安装包和官方一致没有任何区别。安装完VSCode后必装插件有这几个Remote - SSH核心组件用于建立远程连接。Remote - SSH: Editing Configuration Files编辑SSH config配置时更顺手。PythonPython语言支持基础包内含debugpy调试器支持。PylancePython语言服务器提供补全、类型检查和跳转。这些插件安装之后不需要额外配置第一次连接Jetson时会自动同步到远端vscode-server里。唯一要注意的是插件版本会在首次连接时自动匹配一定耐心等它下载完成不要在进度条跑一半的时候乱点。3. 远程连接与Python调试实操环境准备好之后就进入正式操作环节了。我会分几步把从连接Jetson到跑起断点调试的完整过程走一遍。3.1 从零开始建立Remote-SSH连接第一步打开本地VSCode点击左下角绿色按钮类似图标弹出命令面板后选择Remote-SSH: Connect to Host。然后输入用户名加上IPubuntu192.168.1.100如果这是第一次连这台JetsonVSCode会让你选择SSH配置文件更新位置直接选第一个默认路径就行。接着输入密码。首次连接时VSCode会在Jetson上安装vscode-server这个过程根据网络情况可能需要几十秒到几分钟。连接成功后左下角会显示SSH: 192.168.1.100表示你已经处于远程模式。打开资源管理器Explorer面板点击“打开文件夹”输入ROS工作空间的路径比如/home/ubuntu/catkin_ws或/home/ubuntu/dev回车后工作区就被加载进来了。到这里代码浏览、编辑、搜索、终端这些功能已经在远程模式工作了。但还有一个关键点在远程模式下VSCode里打开的终端默认就是Jetson的shell这是一个容易被忽略但非常好用的地方——你不必再开一个putty或Terminator窗口去敲ROS命令了。3.2 免密登录与SSH config配置每次输入密码其实还好但调试时频繁重连就比较烦。配置免密登录很简单在本地终端生成SSH密钥然后把公钥拷贝到Jetson上。ssh-keygen -t rsa -b 4096 -C your_emailexample.com ssh-copy-id ubuntu192.168.1.100ssh-copy-id会让你输入一次Jetson密码之后就能免密登录。如果本地是Windows没有ssh-copy-id命令也可以手动把本地~/.ssh/id_rsa.pub的内容追加到Jetson的~/.ssh/authorized_keys文件中记得把权限设成600。接着优化SSH config。VSCode的Remote-SSH会读取~/.ssh/config文件你可以在这个文件里给Jetson定义一个简单别名省得每次打完整IP。Host jetson HostName 192.168.1.100 User ubuntu Port 22 ServerAliveInterval 60 ServerAliveCountMax 3ServerAliveInterval 60表示每60秒发一次心跳包这样即使Wi-Fi信号波动或者路由器NAT超时连接也不容易被踢掉。这个参数是远程调试的保命配置强烈建议加上。3.3 选对Python解释器Rosmaster调试的第一道坎连接成功后打开任意一个.py文件VSCode右下角会弹出Python解释器选择提示。如果没有弹出按CtrlShiftP输入Python: Select Interpreter。这里必须选Jetson上的系统Python路径一般是/usr/bin/python3。如果你用的ROS版本是Melodic对应Python 3.6Noetic对应Python 3.8。路径上可能会带上具体版本号比如/usr/bin/python3.8选它就对。为什么要强调这步因为我见过太多人在这里踩坑本地电脑上明明装了Python 3.10结果VSCode远程打开后右下角自动选中的是本地解释器Pylance直接报No module named rospy。你代码翻来翻去都找不到问题其实只是解释器选错了。正确选择解释器之后Pylance还需要能找到ROS相关的包和自定义消息包。比如geometry_msgs.msg、sensor_msgs.msg这些包默认在/opt/ros/distro/lib/python3/dist-packages下而你自己生成的msg/srv则在工作空间的devel/lib/python3/dist-packagescatkin或install/lib/python3/dist-packagescolcon下。如果Pylance还是报红可以手动添加搜索路径。在VSCode的settings.json里加{ python.analysis.extraPaths: [ /opt/ros/noetic/lib/python3/dist-packages, /home/ubuntu/catkin_ws/devel/lib/python3/dist-packages ] }路径要按你的ROS发行版和实际工作空间路径改。这一步做完代码补全和类型提示基本就正常了。3.4 两种调试方式单文件启动与remote attach调试Rosmaster这类ROS程序根据使用场景不同我总结出两种比较成熟的调试模式。模式一单文件调试适合独立节点如果你要调试的是一个相对独立的Python节点比如底盘通信节点、串口转发节点可以直接打开这个.py文件按F5。VSCode会默认使用Python Debugger配置启动当前文件。但直接启动大概率会报No module named rospy因为调试器进程没有source过ROS环境。解决办法有两个。最简单的是确保Jetson的用户~/.bashrc里已经source了ROS环境echo source /opt/ros/noetic/setup.bash ~/.bashrc echo source /home/ubuntu/catkin_ws/devel/setup.bash ~/.bashrc source ~/.bashrc但VSCode的调试器启动的子进程不一定完全继承shell环境。更稳妥的是在.vscode/launch.json里显式注入环境变量{ version: 0.2.0, configurations: [ { name: Python: Rosmaster Node, type: debugpy, request: launch, program: /home/ubuntu/catkin_ws/src/rosmaster/scripts/rosmaster_node.py, console: integratedTerminal, env: { PYTHONPATH: /opt/ros/noetic/lib/python3/dist-packages:/home/ubuntu/catkin_ws/devel/lib/python3/dist-packages }, cwd: /home/ubuntu/catkin_ws } ] }PYTHONPATH把那两个核心目录加进去之后再按F5rospy就能正常import了。调试Rosmaster节点时程序会正常初始化ROS节点进入spin循环然后你就可以在代码里打断点观察状态变化了。记得在跑调试节点前先启动roscore或者把另一个节点用roslaunch跑起来否则节点注册时找不到master会直接报错。模式二remote attach适合roslaunch整体启动后再挂载调试更复杂的场景是整个Rosmaster系统通过roslaunch xxx.launch一起启动几个节点互相通信、共享状态。这时候单文件F5不适合因为节点的启动被launch文件接管了。我的做法是在被调试节点的代码里加上debugpy监听pip3 install debugpy然后在需要调试的节点入口代码前面加import debugpy debugpy.listen((0.0.0.0, 5678)) debugpy.wait_for_client() print(debugger attached, continue...)接着正常roslaunch整套系统。程序跑到这一行会阻塞等待调试器连接。此时回到VSCode创建一个attach配置{ name: Python: Remote Attach, type: debugpy, request: attach, connect: { host: 192.168.1.100, port: 5678 }, pathMappings: [ { localRoot: ${workspaceFolder}, remoteRoot: /home/ubuntu/catkin_ws } ] }按F5选择这个配置VSCode就会通过5678端口attach到正在运行的Jetson进程。attach成功之后代码里所有断点都生效你可以随时暂停、单步、看变量。这种方式最贴近真实上车的运行场景因为它保持roslaunch管理的完整节点生命周期其他节点状态不受影响。有一个注意点debugpy.listen((0.0.0.0, 5678))的0.0.0.0表示监听所有网卡这样确实方便远程连接但也意味着同一网段其他设备都能尝试连接这个端口。如果你在多人共用的开发环境里做调试建议把它改成Jetson的具体IP或者用完就注释掉避免留下一个常开的调试端口。3.5 断点、监视这些调试功能怎么用出效率调试面板的基础操作大家应该都会行号左侧点一下是普通断点程序执行到这一行会停下。但机器人程序调试有几个更实用的技巧条件断点右键断点选择“编辑断点”可以设置条件表达式。比如调pid控制时我只想在self.speed 2.0的时刻停下来看看当时的计算状态就不用每次循环都停。监视窗口WATCH在调试时把关键的表达式加到监视列表里。ROS里最常见的写法是监视self.odom.twist.twist.linear.x这样程序跑起来后不用print也能实时看到速度值的变化。调试工具栏的“暂停”按钮如果你怀疑程序在某个回调里死循环或卡住直接点暂停调试器会停在当前正在执行的代码行你立刻就能看出卡在哪块逻辑里。这比在代码里放一堆print然后分析日志要快得多。线程面板rospy的spin()会阻塞主线程但其他回调线程是独立运行的。调试时如果只在主线程里看变量会漏掉很多信息。我调试Rosmaster这种多回调程序时会刻意关注线程列表确认每个回调是否按预期被触发。这些功能组合在一起调试体验跟Jupyter完全是两个世界。你在Jupyter里花一下午print排查的bug在VSCode里可能10分钟就定位了。4. 常见问题排查与避坑指南这一章是我实际的排查记录很多问题你在踩之前根本想不到但遇到之后又恨不得拍大腿——原来这么简单。4.1 远程连不上从网络到SSH的逐层排查现象可能原因处理方式Connection refusedSSH服务没起来sudo systemctl status ssh没起来就sudo systemctl start sshConnection timed out网络不通ping 192.168.1.100确认Jetson和电脑在同一网段密码错误用户名不对Jetson默认用户是ubuntu不是root如果是重装过系统要看你自己创建的用户名Host key verification failed重装系统后SSH密钥变化本地执行ssh-keygen -R 192.168.1.100清除旧的密钥记录连接后立刻断开用户shell配置异常检查~/.bashrc里是否写了会阻塞启动的命令必要时用mv ~/.bashrc ~/.bashrc.bak先排查这几种情况我基本都遇到过。尤其是“重装系统后连不上”VSCode会提示host key mismatch很多新手都懵了其实只要清楚一下本地存的主机指纹就行。4.2 vscode-server下载失败与离线安装Remote-SSH首次连接时VSCode要在Jetson上安装vscode-server。如果Jetson和本地的网络状态不好这个下载很容易失败卡在Installing...就再也不动。遇到这种情况先看一下日志里的commit id。然后到VSCode的官方server下载地址找对应的vscode-server-linux-arm64.tar.gz——注意Jetson是ARM架构一定不能下x86_64版本。下载到本地后通过scp传到Jetson解压到对应的~/.vscode-server/bin/commit-id/目录下然后重新连接。这个离线安装方法我经历过好几次。以前总觉得是网络问题重试几次就好但实际上vscode-server体积不小在弱网条件下反复失败的几率很高。学会手动部署之后这个问题就彻底解决了。4.3 Pylance报红、补全失效与ROS消息包远程模式下Pylance报红最典型的两种一种是解释器选错已经在3.3里讲过。另一种是import rospy和自定义消息报红但上板子跑却一点问题没有。这种情况需要检查两件事。第一python.analysis.extraPaths有没有配置正确的ROS包路径这是最常见的补全失效原因。第二如果你用的是colcon工作空间还需要把install目录加进去比如/home/ubuntu/ros2_ws/install/xxx/lib/python3/dist-packages。如果配置完仍然报红用命令面板执行Python: Clear Cache and Reload Window清掉Pylance缓存重启一次。4.4 调试时找不到rospy环境变量如何正确注入按F5开始调试底部终端直接报ModuleNotFoundError: No module named rospy这是初学者最容易遇到的问题。原因刚才说过调试器启动的子进程没有继承ROS环境变量。最彻底的解决方案还是在launch.json里设置env和cwd。但要注意PYTHONPATH要按你ROS发行版的实际路径填Melodic对应/opt/ros/melodic/lib/python3/dist-packagesNoetic对应/opt/ros/noetic/lib/python3/dist-packages。工作空间的devel目录也一并加上。配置好之后断点调试就和正常rosrun一样了。还有一个小技巧如果你不确定远端的Python路径和PYTHONPATH到底是什么可以在VSCode的内置终端里执行which python3 python3 -c import rospy; print(rospy.__file__)看到输出的路径再照着填基本不会错。4.5 硬件资源占用导致的诡异现象怎么定位机器人开发有个特有的问题上次程序异常退出后串口、摄像头、GPIO这些硬件占着不释放。你在VSCode里重新跑程序一直报Resource busy。这种现象在Jupyter时代其实更容易遇到只是大家当时没料到是这个问题。排查硬件的办法ls /dev/ttyUSB* /dev/ttyACM* fuser -v /dev/ttyUSB0 sudo kill -9 pidfuser -v会列出占用该设备的所有进程找到残留进程杀掉即可。摄像头设备往往占的是video0可以用fuser -v /dev/video0来查看。这个坑跟IDE本身无关但切换到VSCode之后由于调试流程变快了这种资源冲突出现的概率会明显上升最好养成每次异常退出后先清理设备的习惯。5. 每次上板我都会开的几个实用功能最后这部分分享几个让远程开发体验再上一个台阶的小功能都是我日常会用到的高频操作。5.1 把远程端口转发到本地浏览器调试小车时经常要打开web可视化面板比如rosboard、web_video_server、rviz web版。这些服务默认跑在Jetson的某个端口上但你在本地浏览器里访问地址一般是localhost:端口这让很多人觉得别扭。VSCode的Ports面板解决了这个问题。在远程模式下打开面板底部的PORTS标签页点击“转发端口”输入Jetson上的服务端口比如8888VSCode会自动把本地的一个端口映射到远程。然后你在本地浏览器直接访问http://localhost:8888就能看到小车的实时画面和话题数据。这个功能特别适合看调试结果不用每次手动找IP。5.2 远程终端与Git工作流VSCode远程模式下内置终端默认连接的是Jetson的shell这意味着你可以整个开发流程都在同一个窗口内完成左边看代码、右边跑roslaunch、底部敲git命令。我现在的习惯是Git仓库建在Jetson上本地VSCode远程操作commit和push都在Jetson上直接完成。这样代码永远不会出现“本地改了忘传远端”的经典事故。5.3 多板卡切换的小习惯如果你手头有Nano、Xavier NX、Orin NX多块板子不妨给每块板子在~/.ssh/config里配一个独立别名比如jetson-nano、jetson-orin。这样每次连接只用输一次别名不用记IP。连接面板里还能直接保存历史记录切换板卡只需点两下。我实际项目里会同时挂着两台板子一台跑主干测试一台做算法验证靠别名区分切换几乎零成本。最后聊点我个人的真实体会。从Jupyter迁到VSCode远程开发最大的变化不是功能多寡而是你终于可以在一个地方完整地“看见”程序的运行状态。ROS机器人程序本来就是个分布式系统节点、话题、硬件设备交织在一起调试时如果只能靠终端日志和print猜测很容易陷入越改越乱的死循环。而VSCode远程调试给到的断点视图、变量监视、调用堆栈让整个Rosmaster程序的每一行代码都变得透明可控。如果你现在还停在Jupyter阶段建议拿一个小项目试试迁移从连接SSH到跑通第一个断点半小时之内就能完成收益却是长期的。别用战术上的勤奋掩盖战略上的懒惰调试工具这件事值得一次升级。
返回列表