蚂蚁集团旗下具身智能公司灵波科技在 LingBot-Depth 1.0 开源之后,继续推出新一代空间感知模型 LingBot-Depth 2.0,并同步开源视觉基座模型 LingBot-Vision。这个动作值得关注:对真实世界中的机器人来说,识别“这是什么”只是第一步,更难的是判断“它离我多远、形状怎样、能不能绕过去或抓起来”。
从来源信息看,LingBot-Depth 面向真实场景的深度补全,可以理解为给机器人补齐物理世界的空间视力。LingBot-Depth 2.0 的重点在于新一代空间感知能力,而 LingBot-Vision 的开源则让开发者有机会围绕视觉基座搭建更完整的具身智能感知链路。
深度补全为什么是机器人“眼睛”的硬活
很多机器人并不是拿到一张完美的三维地图再行动。现实环境里,深度传感器可能有空洞、反光区域会丢数据、远距离物体的深度不稳定,遮挡和动态物体还会让几何信息变得断断续续。
深度补全模型要做的事情,是把这些稀疏、不完整、带噪声的深度信息,结合 RGB 图像或其他视觉特征,补成更连续、更可用的深度图。它不只是让图像“看起来完整”,而是服务于后续决策:
- 移动机器人需要估计通行空间,避免碰撞。
- 机械臂需要知道目标表面和边缘,规划抓取位姿。
- 人形机器人需要理解地面、台阶、桌面和障碍物之间的空间关系。
- 仓储、家庭、零售等场景会遇到大量透明、反光、堆叠和遮挡对象。
所以 LingBot-Depth 2.0 的意义不在于又多了一个视觉模型名字,而在于它瞄准的是机器人落地时绕不开的感知短板:真实场景里的空间数据往往不干净。
LingBot-Vision 开源的价值:从模型能力走向系统集成
同步开源 LingBot-Vision 也很关键。具身智能系统通常不是单模型作战,而是一条流水线:相机采集图像,视觉基座提取语义和结构特征,深度模型补齐空间信息,规划模块再根据这些信息做路径、抓取或避障决策。
如果视觉基座模型开放,开发者可以更直接地做几类工作:
- 在自己的机器人数据上评估视觉特征是否稳定。
- 将视觉基座和深度补全、目标检测、分割、SLAM 等模块组合。
- 对特定场景做微调或蒸馏,例如仓储货架、家庭桌面、实验室操作台。
- 比较不同视觉编码器对深度估计和空间规划的影响。
这里要注意边界:来源摘要没有给出 LingBot-Vision 的具体模型结构、权重地址、许可证和推理接口。因此下面的代码不是官方调用方式,而是一个“可以这样实践”的最小深度补全评估框架。等模型仓库公开后,可以把其中的 complete_depth() 换成真实模型推理。
可以这样实践:先搭一个深度补全评估骨架
下面示例用 Python 构造一个最小可运行流程:读取 RGB 图、读取稀疏深度图,用 OpenCV 做一个基线补全,并输出可视化结果。这个基线不能替代 LingBot-Depth 2.0,但它能帮团队先把数据格式、评估脚本、结果保存流程跑通。之后接入开源模型时,只需要替换补全函数。
运行前需要准备:
- 一张 RGB 图:
rgb.png - 一张单通道 16-bit 深度图:
sparse_depth.png,单位假设为毫米,空洞位置为 0
安装依赖:
python -m venv .venv
source .venv/bin/activate
pip install opencv-python numpy
保存为 depth_completion_demo.py:
import argparse
from pathlib import Path
import cv2
import numpy as np
def read_depth_mm(path: str) -> np.ndarray:
depth = cv2.imread(path, cv2.IMREAD_UNCHANGED)
if depth is None:
raise FileNotFoundError(path)
if depth.ndim != 2:
raise ValueError("depth image must be single-channel")
return depth.astype(np.float32)
def complete_depth_baseline(rgb: np.ndarray, sparse_depth_mm: np.ndarray) -> np.ndarray:
"""
A simple baseline: fill holes with nearest valid depth, then smooth lightly.
Replace this function with LingBot-Depth 2.0 inference when the model API is available.
"""
valid = sparse_depth_mm > 0
if valid.sum() == 0:
raise ValueError("sparse depth has no valid pixels")
holes = (~valid).astype(np.uint8)
_, labels = cv2.connectedComponents(holes)
filled = sparse_depth_mm.copy()
valid_u8 = valid.astype(np.uint8)
# Distance transform returns nearest valid pixel coordinates when labels are requested.
_, nearest_labels = cv2.distanceTransformWithLabels(
1 - valid_u8,
distanceType=cv2.DIST_L2,
maskSize=5,
labelType=cv2.DIST_LABEL_PIXEL,
)
valid_points = np.column_stack(np.where(valid))
flat_valid_depth = sparse_depth_mm[valid]
# OpenCV labels start at 1. Clamp defensively for tiny test images.
idx = np.clip(nearest_labels - 1, 0, len(flat_valid_depth) - 1)
filled[~valid] = flat_valid_depth[idx[~valid]]
# Edge-aware filtering would be better; this keeps the demo dependency-light.
filled = cv2.medianBlur(filled.astype(np.uint16), 5).astype(np.float32)
return filled
def colorize_depth(depth_mm: np.ndarray) -> np.ndarray:
nonzero = depth_mm[depth_mm > 0]
lo, hi = np.percentile(nonzero, [2, 98])
normalized = np.clip((depth_mm - lo) / max(hi - lo, 1.0), 0, 1)
gray = (normalized * 255).astype(np.uint8)
return cv2.applyColorMap(gray, cv2.COLORMAP_TURBO)
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--rgb", default="rgb.png")
parser.add_argument("--depth", default="sparse_depth.png")
parser.add_argument("--out", default="out")
args = parser.parse_args()
out_dir = Path(args.out)
out_dir.mkdir(parents=True, exist_ok=True)
rgb = cv2.imread(args.rgb, cv2.IMREAD_COLOR)
if rgb is None:
raise FileNotFoundError(args.rgb)
sparse = read_depth_mm(args.depth)
completed = complete_depth_baseline(rgb, sparse)
cv2.imwrite(str(out_dir / "completed_depth_mm.png"), completed.astype(np.uint16))
cv2.imwrite(str(out_dir / "sparse_depth_vis.png"), colorize_depth(sparse))
cv2.imwrite(str(out_dir / "completed_depth_vis.png"), colorize_depth(completed))
print(f"valid sparse pixels: {(sparse > 0).sum()}")
print(f"saved results to: {out_dir.resolve()}")
if __name__ == "__main__":
main()
运行:
python depth_completion_demo.py --rgb rgb.png --depth sparse_depth.png --out out
接入真实模型时,可以把 complete_depth_baseline() 改成类似这样的结构。下面是假设性接口,具体参数要以开源仓库为准:
# Assumption-only example. Replace package name and API with the official LingBot repo.
from lingbot_depth import LingBotDepthModel
model = LingBotDepthModel.from_pretrained("path/to/lingbot-depth-2.0")
model.eval()
completed_depth_mm = model.predict(
rgb_path="rgb.png",
sparse_depth_path="sparse_depth.png",
depth_unit="mm",
)
这个骨架的价值在于提前固定工程问题:输入单位、空洞编码、输出格式、可视化方式、离线评估目录。等模型替换进来,团队可以把注意力放在效果、延迟和部署成本上,而不是临时补脚本。
评估时别只看一张漂亮图
深度补全模型进入机器人系统前,至少要检查四类指标。
一是几何误差。常见做法是拿高质量深度或激光数据做参考,计算 MAE、RMSE、绝对相对误差等。指标不必一开始就复杂,但要覆盖近距离、中距离、远距离。
二是任务收益。对机器人来说,更低的深度误差不一定自动变成更高的抓取成功率。应该在下游任务里看避障误触发率、路径规划失败率、抓取成功率、重定位稳定性。
三是延迟和算力。空间感知通常在控制闭环里工作,模型再准,如果推理延迟让机器人错过动态障碍,也会出问题。部署时要测端侧 GPU、工控机、边缘服务器上的实际吞吐。
四是失败模式。透明杯、镜面金属、黑色物体、强逆光、低纹理墙面、密集线缆、货架缝隙,都可能让深度补全产生看似连续但实际错误的结果。机器人系统需要给低置信度区域留出保守策略。
落地建议:把它当成感知模块,不要当成万能世界模型
LingBot-Depth 2.0 和 LingBot-Vision 的组合,代表具身智能感知栈正在向更开放、更可复用的方向走。对开发团队来说,比较稳妥的采用路径是:先离线评估自己的场景数据,再接入仿真或回放系统,最后进入真实机器人闭环。
一个实用清单如下:
- 明确输入:RGB、稀疏深度、相机内参、时间戳是否对齐。
- 固定输出:深度单位、坐标系、分辨率、置信度图是否需要。
- 建立基线:用简单算法或现有模型做对照,不要只看单一模型结果。
- 测下游任务:避障、抓取、导航、建图至少选一个真实指标。
- 准备降级策略:模型输出异常时,机器人应减速、停止或切换保守规划。
- 审查许可证:开源模型能否用于商业产品、能否微调、能否再分发,需要在项目早期确认。
真正的机器人感知不是演示视频里的单帧效果,而是在灰尘、反光、遮挡、延迟和传感器噪声里持续工作。LingBot-Depth 2.0 补的是空间感知这块硬骨头;LingBot-Vision 开源则给开发者留下了把模型装进系统、验证到任务里的空间。