引言
使用真实的机械臂做识别和抓取的项目已经很多了,但在Nvidia开发的Isaac sim软件上做机械臂的识别抓取好像没有,该项目正好是毕业设计,题目是基于虚拟仿真的机器人自动控制,后续还会继续添加完善机械臂,或者研究其它机器人的功能。
背景
全球各地的电子制造商正在使用新的综合参考工作流程来推进其工业数字化进程,这套流程将融合生成式AI、3D协作、仿真模拟和自主机器人技术。全球最大的行业所制造的都是实物,如果能先以数字化的方式制造这些产品,就可以节省大量成本。通过使电子产品制造商能够轻松构建和运营虚拟工厂,并实现制造和检查工作流程的数字化,可以大大提高了质量和安全性,同时减少了最后一刻的意外和延误。
借助虚拟仿真平台,可以改进工厂工作流程,并在现实世界实施想法之前,先在虚拟世界中进行大量模拟。目前业界已经有Nvidia推出Omniverse物理模拟器解决方案,并构建了用于仿真和测试机器人的NVIDIA Isaac Sim 应用。
国际发展状况上看,有虚拟仿真与工业机器人技术的结合的NVIDIA Omniverse,Omniverse 已被多家国际领先企业采用,用于虚拟工厂建设和工业机器人测试,如宝马(BMW)使用 Omniverse 构建数字孪生工厂,Isaac Sim 是 Omniverse 的一个应用,专注于机器人仿真和AI训练,提供机器人导航、拾取和放置的自动化解决方案。西门子 Digital Twin:以数字孪生技术为核心,构建虚拟工厂和自动化流程的仿真环境,推动虚拟调试与现实生产的深度结合。在机器人控制技术的应用与研究上近年来,国外学术界对机器人仿真与控制的研究持续深入:强化学习(如OpenAI的机器人手控制研究)被广泛用于复杂任务的自动化操作。仿真与现实结合(Sim-to-Real)技术的研究也在不断推进,解决了仿真模型与实际环境之间的偏差问题。应用案例:亚马逊和波音公司广泛采用机器人仿真技术来优化自动化操作和供应链管理。
国内发展状况上看,国内工业界与学术界在虚拟仿真技术上正逐步追赶国际水平:阿里巴巴的达摩院研究团队利用虚拟仿真技术优化仓储物流机器人的调度与路径规划。清华大学、浙江大学等高校在数字孪生与机器人仿真研究方面取得了阶段性成果。虽然国内平台自主性尚存不足,但借助 Omniverse 等国际平台的技术,国内企业已能快速构建高精度仿真环境。国内工业机器人市场快速增长,企业和高校在机器人运动控制与自动化领域展开了深入研究:华为研发的仓储机器人通过仿真技术实现路径规划优化。京东物流采用机器人仿真技术提升自动分拣系统的效率。当前国内重点关注机器视觉、强化学习与多机器人协作等研究方向上。
分析
首先是视觉模块的选择,本人使用github上的一个项目:Grounded-Segment-Anythinghttps://github.com/IDEA-Research/Grounded-Segment-Anything主要原因是,能够通过给定文本词,在图像中检测到目标物体,并生成的图像用2D框框出目标物体且有检测目标物体的相似度百分比,会框出不止一个物体,相似度的百分比不一样,还会生成目标物体的mask,适合做机械臂的视觉处理。先把它到conda跑通了再做后续的把它和Isaac sim放一起。
其次,Isaac sim是有内置的python包的,但是不能把原有的python环境修改,需要有一个独立的运行环境,跟conda一样,所以需要Isaac sim的高级运行方式
使用 Anaconda 运行
在Isaac sim的安装位置下,使用以下命令创建新环境:
conda env create -f environment.yml
conda activate isaac-sim
如果您已有 Conda 环境,请确保 environment.yml安装。或者,您可以删除并重新创建 Conda 环境,如下所示:
conda remove --name isaac-sim --all
conda env create -f environment.yml
conda activate isaac-sim
最后,您必须设置环境变量,以便 Isaac Sim Python 软件包 正确定位。在 Linux 上,您可以按如下方式执行此作:
source setup_conda_env.sh
然后,您可以在 Conda 环境中按如下方式运行示例:
python path/to/script.py
然后在将前面的groundedsam全部拷贝到Isaac Sim安装路径下,因为后续运行代码会用到saac Sim的api,本人放在standalone_examples/api/isaacsim.cortex.framework下。
把拷贝过来的groundedsam在isaac-sim环境下再次配置一下环境,分别试试跑一下Isaac sim的独立脚本和groundedsam。
代码编写
接下来是Isaac sim代码的编写部分,基本逻辑是,仿真环境的初始化,包括相机、各类物体、franka,开启仿真后拍摄一张RGB照片,将该照片给视觉模块groundedsam处理得到目标物体的坐标,机械臂franka根据坐标去抓取。
world = CortexWorld() // 初始化世界
my_task = PickPlace() // 初始化任务类
world.add_task(my_task) // 给世界添加夹取任务
world.reset()
task_params = my_task.get_params() // 另一种添加franka机械臂的方式
world.scene.add_default_ground_plane() // 添加地面
stage = omni.usd.get_context().get_stage() // 取得舞台实例
robot = world.scene.get_object(task_params["robot_name"]["value"])
my_controller = PickPlaceController(
name="pick_place_controller", gripper=robot.gripper,
robot_articulation=robot
) // 关节控制类
articulation_controller = robot.get_articulation_controller()
i = 0
reset_needed = False
camera = Camera(
prim_path="/World/camera",
position=np.array([0.0, 0.0, 4.0]),
frequency=30,
resolution=(800, 800),
orientation=rot_utils.euler_angles_to_quats(
np.array([0, 90, 0]
), degrees=True),
) // 初始化相机
camera.initialize()
camera.add_motion_vectors_to_frame()
camera.add_distance_to_image_plane_to_frame() // 给相机添加相机到图像平面的帧,用于深度检测
// 初始化物体
obs_specs = [
objspec("mustard_bottle",
"/home/n1/isaacsim_assets/Assets/Isaac/4.5/Isaac/Props/YCB/Axis_Aligned/006_mustard_bottle.usd",
Gf.Vec3f(0.3,0.2,0.01),
Gf.Vec3f(0.75, 0.75, 0.75)
),
objspec("wood_block",
"/home/n1/isaacsim_assets/Assets/Isaac/4.5/Isaac/Props/YCB/Axis_Aligned/036_wood_block.usd",
Gf.Vec3f(0,0.3,0.01),
Gf.Vec3f(0.4, 0.4, 0.4)
),
objspec("foam_brick",
"/home/n1/isaacsim_assets/Assets/Isaac/4.5/Isaac/Props/YCB/Axis_Aligned/061_foam_brick.usd",
Gf.Vec3f(-0.3,0.3,0.01),
Gf.Vec3f(0.8, 0.8, 0.8)
),
objspec("tuna_fish_can",
"/home/n1/isaacsim_assets/Assets/Isaac/4.5/Isaac/Props/YCB/Axis_Aligned/007_tuna_fish_can.usd",
Gf.Vec3f(0,-0.3,0.01),
Gf.Vec3f(0.7, 0.7, 0.7)
)
]
width = 0.0515
// 添加物体
for i, (x, spec) in enumerate(zip(np.linspace(0.3, 0.7, len(obs_specs)), obs_specs)):
add_reference_to_stage(usd_path=spec.path, prim_path="/World/{}".format(spec.name))
obj_mesh = UsdGeom.Mesh.Get(stage, "/World/{}".format(spec.name))
physicsUtils.set_or_add_translate_op(obj_mesh, translate=spec.position)
physicsUtils.set_or_add_orient_op(obj_mesh, orient=Gf.Quatf(0.7, 0.9, 0,-97))
physicsUtils.set_or_add_scale_op(obj_mesh, scale=spec.scale)
UsdPhysics.RigidBodyAPI.Apply(stage_utils.get_current_stage().GetPrimAtPath("/World/{}".format(spec.name)))
geom = GeometryPrim(
prim_paths_expr="/World/{}".format(spec.name),
name=spec.name,
collisions=[True]
)
// 给物体添加碰撞属性
geom.set_collision_approximations(["convexDecomposition"])
// 拍摄准备
if not os.path.exists(save_dir):
os.makedirs(save_dir)
print(f"Folder '{save_dir}' created.")
else:
print(f"Folder '{save_dir}' already exists.")
SimulationContext().play()
// 等待物理世界渲染完成
for _ in range(100):
world.step(render=True) # 必须启用渲染
// 获取distance_to_image_plane帧
camera.get_current_frame()["distance_to_image_plane"]
// 获取rgb图和深度图
rgb_img = camera.get_rgb()
depth_img = camera.get_depth()
//rgb的转换,float->int
rgb_img = rgb_img.astype(np.uint8)
depth_normalized = depth_normalized.astype(np.uint8)
# Convert the NumPy array to a PIL Image
image = Image.fromarray(rgb_img)
image1 = Image.fromarray(depth_normalized)
# Save the image as a PNG file
file_name = f"{save_dir}/robo_img_fir_rgb.png"
file_name1 = f"{save_dir}/robo_img_fir_dep.png"
image.save(file_name)
image1.save(file_name1)
print(f"[Data Collected] {file_name}")
// 目标物体中心像素坐标的计算
x0, y0, x1, y1 = boxes_filt[0].numpy() # 取第一个检测框
print(boxes_filt[0])
u = (x0 + x1) / 2 # 中心点 x 坐标
v = (y0 + y1) / 2 # 中心点 y
i = (int)(u)
j = (int)(v)
// 像素坐标到世界的转换
world_p = camera.get_world_points_from_image_coords(np.array([[u,v]]),depth_img[j-1:j,i-1:i][0] + (4.0 - depth_img[j-1:j,i-1:i][0]) / 2 + 0.01)
print(world_p)
结尾
后续代码会放到GitHub上,有什么错误欢迎指正。