ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

045、VoxPoser语言生成3D价值地图:LLM与VLM协同的零样本操作

045、VoxPoser语言生成3D价值地图:LLM与VLM协同的零样本操作 045、VoxPoser语言生成3D价值地图LLM与VLM协同的零样本操作调试机器人抓取的时候最让人抓狂的不是模型不收敛而是明明仿真里跑得好好的一到真机上机械臂对着桌面上一堆杂物就是不知道该抓哪个、往哪儿放。你给它一个“把红色杯子放到托盘里”的指令它先得知道杯子在哪、托盘在哪、怎么避开旁边的障碍物。传统做法是训练一个端到端策略但换一个场景、换一类物体又得重新收集数据。VoxPoser这篇论文给了我一个完全不同的思路——它不训练任何策略网络而是用LLM去“写程序”用VLM去“看场景”最后生成一张3D价值地图直接引导机械臂运动。今天就把这个框架从原理到代码拆开讲清楚。从一次失败的抓取说起我最早尝试用RT-1做零样本操作输入自然语言指令输出动作序列。结果在训练集里没见过的物体组合上成功率直接掉到20%以下。后来换成VoxPoser的思路第一反应是“这也能行”——它居然把操作问题转化成了“在3D体素空间里标注哪些位置值得去、哪些位置必须避开”的问题。价值地图上正值高的地方就是目标点负值大的地方就是障碍区机械臂沿着梯度上升的方向运动自然就完成了任务。听起来像强化学习里的奖励塑形但VoxPoser的巧妙之处在于这个价值地图不是学出来的而是LLM根据指令和VLM对场景的描述现场“写”出来的。核心思想把操作拆成“看”和“想”VoxPoser的流程分三步。第一步用VLM比如CLIP或Grounded SAM对当前RGB-D图像做开放词汇检测得到场景中每个物体的名称和3D位置。第二步把用户指令、物体列表、以及一些预定义的动作原语比如“移动到”“推开”“抓取”一起丢给LLM让它生成一段Python代码这段代码会调用一组API来操作3D体素地图。第三步执行这段代码在体素地图上叠加“吸引”和“排斥”信号合成最终的价值地图再用一个简单的规划器比如MPC或RRT沿着价值梯度生成轨迹。这里的关键在于LLM生成的代码不是直接控制机器人而是控制价值地图的“画笔”。比如指令是“把杯子放到托盘里”LLM会生成类似这样的逻辑先找到杯子对应的体素区域给这些体素加上高正值吸引再找到托盘区域加上更高的正值目标最后把障碍物区域加上负值排斥。机械臂的运动规划器看到这张地图自然知道往哪儿走。模型架构LLM和VLM各司其职VLM部分用的是Grounding DINO SAM的组合输出每个检测框的掩码和类别。这里有个细节容易踩坑——VLM检测到的物体位置是2D像素坐标必须通过深度图反投影到3D空间。我一开始直接用相机内参做反投影结果在物体边缘出现大量噪声点后来加了深度图的双边滤波才稳定下来。别小看这一步价值地图的精度直接取决于3D点云的质量。LLM部分用的是GPT-4或CodeLlama输入是系统提示词加上场景描述。系统提示词里要写清楚可用的API函数比如add_attraction(region, weight)、add_repulsion(region, weight)、get_object_position(name)。LLM的任务就是组合这些API生成一段可执行的Python脚本。这里有个经验提示词里一定要给几个示例否则LLM容易生成不存在的函数名或者逻辑错误。我试过不给示例直接问GPT-4生成的代码有30%的概率会调用一个叫move_to_object的函数但我们的API里根本没这个。代码实现从零搭建VoxPoser先定义3D体素地图的数据结构。我用的是一个numpy数组尺寸是(W, H, D)每个元素代表该体素的价值。初始化全零然后根据VLM检测结果填充物体区域。importnumpyasnpimportopen3daso3dclassVoxelMap:def__init__(self,bounds,resolution):# bounds: [[xmin, xmax], [ymin, ymax], [zmin, zmax]]# resolution: 体素边长单位米self.boundsbounds self.resresolution self.voxel_size[int((b[1]-b[0])/res)forbinbounds]self.mapnp.zeros(self.voxel_size,dtypenp.float32)defworld_to_voxel(self,point):# 这里踩过坑直接除以分辨率会得到浮点索引必须取整idx[int((point[i]-self.bounds[i][0])/self.res)foriinrange(3)]# 边界检查别让索引越界idx[max(0,min(idx[i],self.voxel_size[i]-1))foriinrange(3)]returntuple(idx)defadd_attraction(self,center,radius,weight):# 在center周围radius范围内加上正价值cself.world_to_voxel(center)rint(radius/self.res)# 用切片操作比双重循环快得多x0,x1max(0,c[0]-r),min(self.voxel_size[0],c[0]r1)y0,y1max(0,c[1]-r),min(self.voxel_size[1],c[1]r1)z0,z1max(0,c[2]-r),min(self.voxel_size[2],c[2]r1)self.map[x0:x1,y0:y1,z0:z1]weightLLM生成的代码会调用这些方法。比如对于“把杯子放到托盘”的指令LLM可能生成# 这是LLM生成的代码不是我们手写的cup_posget_object_position(cup)tray_posget_object_position(tray)add_attraction(cup_pos,0.05,1.0)# 杯子附近吸引add_attraction(tray_pos,0.08,2.0)# 托盘附近更强吸引# 障碍物排斥forobjin[bottle,book]:posget_object_position(obj)add_repulsion(pos,0.06,-1.5)这里有个关键设计get_object_position返回的不是单个点而是一个区域。我实现的时候把VLM检测到的掩码投影到3D然后计算该区域所有体素的中心。但LLM生成的代码里add_attraction的radius参数需要根据物体大小调整否则大物体会被当成小点处理。我在提示词里特意加了一句“注意物体尺寸大物体用大radius”效果立竿见影。实验与调优从仿真到真机的坑在仿真环境比如RLBench或Robosuite里VoxPoser的成功率能达到85%以上但真机上会掉到60%左右。我排查下来主要问题出在深度图噪声和相机标定误差上。仿真里深度图是完美的真机上一米外就有±2cm的误差导致价值地图上的吸引点偏移。解决办法是在生成价值地图后加一步“局部细化”——用当前RGB图像重新检测目标物体用检测框中心修正吸引点位置。另一个坑是LLM生成的代码偶尔会有语法错误。我加了一个异常捕获机制如果生成的代码执行失败就把错误信息反馈给LLM让它重新生成。这个“自我纠错”循环最多跑三次超过就放弃。实测下来第一次生成成功率约70%经过一次纠错能到95%。调参方面价值地图的权重比例很重要。吸引权重和排斥权重的比值我设为2:1如果排斥太强机械臂会绕远路甚至卡住如果吸引太强会撞上障碍物。这个比例在仿真里可以固定真机上要根据场景动态调整——我写了一个简单的自适应逻辑如果规划出的轨迹与障碍物碰撞就自动降低吸引权重。落地经验别把VoxPoser当万能药VoxPoser最爽的地方是零样本但它的上限也明显。对于需要精细力控的任务比如插销、拧螺丝价值地图的粒度不够机械臂末端会抖动。我试过把体素分辨率从2cm提到5mm但计算量暴增实时性跟不上。目前我的做法是VoxPoser负责粗规划生成一条无碰撞的路径然后用一个轻量的力控策略做末端精调。另外LLM的推理延迟是个问题。GPT-4生成代码平均要2-3秒加上VLM检测整个流程要5秒左右。对于静态场景够用但如果有动态障碍物就得用更快的模型比如CodeLlama-7B或者缓存常见指令的生成结果。最后说点个人体会。VoxPoser的价值不在于它比端到端模型精度高而在于它把“语言理解”和“空间推理”解耦了。LLM负责理解指令的语义VLM负责感知场景价值地图是两者之间的“翻译层”。这种模块化设计让调试变得异常清晰——如果任务失败你能一眼看出是LLM理解错了、VLM检测漏了还是价值地图权重不对。相比之下端到端模型就是个黑盒出了问题只能瞎猜。如果你准备在真机上复现我的建议是先别急着上机械臂把价值地图的可视化做好。用Open3D把体素地图渲染出来吸引点标红、排斥点标蓝你一眼就能看出LLM生成的代码逻辑对不对。这一步能省下你大量调试时间。另外提示词工程值得花心思我最后用的提示词版本迭代了十几次每次都是在真机失败后总结出来的。比如“注意物体是刚体还是软体”“如果目标被遮挡先推开障碍物”这些细节都是血泪教训换来的。
返回列表