
1. 项目概述当Unity遇见MoveIt机器人抓取规划的新范式如果你正在探索机器人仿真与运动规划尤其是想在一个逼真的虚拟环境中验证你的抓取算法那么“Unity-Robotics-Hub MoveIt”这个组合绝对值得你投入时间。这不仅仅是一个简单的工具链集成它代表了一种从纯算法仿真迈向高保真、物理精确仿真的工作流升级。我最初接触这个组合是为了解决一个老问题在ROS中用MoveIt规划出的抓取路径放到真实世界或者更复杂的仿真环境里怎么就经常“翻车”了呢要么是碰撞检测不精确要么是忽略了物体在抓取过程中的动态形变和滑动。Unity-Robotics-Hub是Unity官方推出的机器人仿真工具包它提供了与ROS机器人操作系统通信的桥梁。而MoveIt则是ROS生态中运动规划的“事实标准”。这个项目的核心目标就是打通这两者在Unity的高保真、可视化环境中利用MoveIt强大的运动规划库完成从场景感知到机械臂运动、再到末端执行器抓取的全流程仿真验证。它非常适合机器人算法工程师、自动化专业的学生以及任何希望将运动规划算法在更真实物理环境中进行前期验证的开发者。简单说你可以在Unity里搭建一个带复杂光照、真实物理材质如摩擦系数的桌面场景摆放一个需要抓取的物体然后通过ROS话题调用MoveIt服务让虚拟机械臂完成规划并执行抓取整个过程所见即所得且物理反馈更可信。2. 核心架构与通信链路拆解要让Unity和MoveIt这对“隔行如隔山”的伙伴协同工作理解它们之间的数据流是第一步。整个系统的架构可以看作一个典型的客户端-服务器-客户端模型其中ROS扮演着消息总线和服务器层的角色。2.1 三方角色与数据流Unity端仿真与可视化客户端这是我们的“前店”。它利用Unity-Robotics-Hub提供的ROS-TCP-Connector或ROS-TCP-Endpoint包建立一个TCP客户端连接到ROS网络。它的职责很重场景构建利用Unity强大的编辑器搭建包含机器人模型URDF导入、目标物体、障碍物的三维场景。物理引擎运行NVIDIA PhysX或Unity自带的物理引擎处理碰撞检测、刚体动力学、关节驱动等。状态发布以固定的频率如30Hz将整个仿真世界的状态主要是机器人所有关节的实时角度、目标物体的位置/姿态封装成ROS标准消息如sensor_msgs/JointState,geometry_msgs/PoseStamped发送给ROS。指令执行订阅来自ROS的规划结果通常是关节轨迹trajectory_msgs/JointTrajectory并解析这些数据驱动Unity中的机器人模型按轨迹运动。ROS层通信与规划服务器这是我们的“后厂”。运行着标准的ROS 1 Noetic或ROS 2 Foxy/Humble。它是系统的中枢神经ROS Master管理所有节点的注册与发现让Unity和MoveIt能找到彼此。MoveIt! 节点这是核心服务器。它加载了你为机器人配置的MoveIt配置包其中包含了SRDF、运动学配置、规划器配置等。它提供标准的ROS服务如/compute_ik逆解、/plan_kinematic_path规划路径等。TF转换树维护着机器人坐标系、世界坐标系、相机坐标系、物体坐标系之间的变换关系这是规划正确的空间基础。数据流闭环感知-规划Unity发布关节状态和物体位姿到ROS例如/joint_states,/object_pose。MoveIt订阅这些话题更新其维护的机器人内部状态和规划场景中的碰撞物体。规划-执行外部节点可以是一个简单的Python脚本也可以是Unity发起的服务调用向MoveIt的规划服务发送请求例如“规划机械臂末端从当前位置移动到物体上方某个预抓取位姿”。MoveIt调用OMPL、CHOMP等规划器库进行计算。执行-反馈MoveIt将规划成功的关节轨迹发布到特定话题如/planned_trajectory。Unity订阅该话题接收轨迹数据并开始在仿真环境中逐帧执行这个轨迹同时将执行过程中的新关节状态再次发布回ROS形成闭环。注意这里有一个关键选择点。Unity可以直接调用MoveIt的服务也可以由一个独立的ROS节点作为“规划管理器”来协调。对于抓取任务后者更常见因为这个管理器可以编排“移动至预抓取点-闭合手爪-提升物体”这一系列动作的规划请求。2.2 Unity-Robotics-Hub关键组件解析Unity-Robotics-Hub不是一个单一功能它提供了一系列模块在这个项目中我们主要关注URDF Importer这是基石。它允许你将机器人的URDF文件直接拖入Unity项目自动生成包含关节、连杆、碰撞体从URDF中的collision标签生成和视觉网格的GameObject层次结构。实操心得URDF中的collision标签通常使用简化几何体如长方体、圆柱体以提高物理计算效率。在Unity中确保这些生成的碰撞体正确无误是后续MoveIt规划中碰撞检测与Unity物理同步的前提。如果导入后模型姿态怪异很可能是URDF中定义的坐标系与Unity的坐标系Y轴向上不匹配需要在导入设置中调整。ROS-TCP-Connector这是主要的通信器。它包含一个RosConnector组件负责与指定IP和端口的ROS端建立TCP连接。还提供了RosPublisher和RosSubscriber组件让你可以像在ROS中一样方便地发布和订阅话题。关键配置你需要在一个ROS节点中运行ros_tcp_endpointPython包作为TCP服务端。Unity中的RosConnector配置该服务端的IP和端口。Message Generation工具支持从你的ROS工作空间中的.msg和.srv文件自动生成C#脚本。这意味着你可以直接使用geometry_msgs.PoseStamped这样的C#类来构造消息无需手动序列化/反序列化JSON。3. 环境搭建与项目初始化实操理论清晰后我们进入实战环节。假设我们的目标是让一个Franka Panda机械臂在Unity中抓取一个方块。3.1 基础软件环境准备ROS端以Ubuntu 20.04 ROS Noetic为例# 1. 安装ROS Noetic桌面完整版 sudo apt update sudo apt install ros-noetic-desktop-full # 2. 创建并初始化工作空间 mkdir -p ~/unity_moveit_ws/src cd ~/unity_moveit_ws/src catkin_init_workspace # 3. 安装MoveIt和Franka Panda相关包 sudo apt install ros-noetic-moveit ros-noetic-franka-ros # 从GitHub克隆panda_moveit_config如果系统包未包含 git clone https://github.com/ros-planning/panda_moveit_config.git # 4. 安装ROS-TCP-Endpoint pip install ros-tcp-endpoint # 或者从源码安装推荐便于修改 cd ~/unity_moveit_ws/src git clone https://github.com/Unity-Technologies/ROS-TCP-Endpoint.gitUnity端以Unity 2022.3 LTS为例新建一个3D项目。通过Unity的Package Manager从Git URL添加以下包com.unity.robotics.ros-tcp-connectorcom.unity.robotics.urdf-importer从Asset Store或Package Manager添加Robot Academy或HDRP模板可选用于更好的视觉效果。3.2 机器人模型与场景导入导入Franka Panda URDF在Assets文件夹右键选择Import Robot from URDF。将文件选择器指向你的panda_arm.urdf文件通常位于/opt/ros/noetic/share/franka_description/robots或你克隆的panda_moveit_config包中。在导入设置面板中务必勾选“Generate Collision Meshes”和“Use URDF Collision Geometry”。关节驱动类型选择“Velocity”或“Position”便于后续控制。点击导入Unity会自动生成一个名为“panda”的预制体。搭建简单抓取场景将“panda”预制体拖入场景。调整其位置使其底座平稳放在一个平面如默认的Plane上。创建一个Cube立方体缩放至合适大小如0.05x0.05x0.05作为待抓取物体。将其放置在机械臂工作空间内的桌面上。为Cube添加Rigidbody组件并调整其质量Mass和摩擦系数Drag。实操心得这里的物理参数质量、摩擦系数会直接影响抓取仿真的真实性。如果手爪夹持力设置不当物体可能在抓取过程中掉落或滑动。建议根据真实物体属性进行粗略设置。配置ROS连接在场景中创建一个空GameObject命名为“RosBridge”。为其添加RosConnector组件。在Inspector面板中设置Ros IP Address为运行ROS的机器IP本地则为127.0.0.1Ros Port保持默认10000。在RosConnector下挂载两个脚本一个用于发布关节状态JointStatePublisher一个用于订阅并执行轨迹TrajectorySubscriber。Unity-Robotics-Hub的示例中通常提供了这些脚本的模板。3.3 ROS端启动与配置编写启动文件在ROS工作空间的src目录下创建一个新的包例如unity_panda_bridge。在其中创建启动文件start_moveit_for_unity.launch。launch !-- 启动ROS TCP Endpoint 服务端 -- node nameros_tcp_endpoint pkgros_tcp_endpoint typedefault_server_endpoint.py outputscreen param nametcp_port value10000/ param nametopic_list value[/joint_states, /object_pose]/ !-- 声明Unity会发布的话题 -- /node !-- 启动MoveIt with Panda -- include file$(find panda_moveit_config)/launch/panda_moveit.launch arg nameload_robot_description valuetrue/ arg namemoveit_controller_manager valuefake/ !-- 使用fake控制器因为我们用Unity执行轨迹 -- /include !-- 启动一个简单的抓取动作服务器节点Python示例 -- node namesimple_grasp_action_server pkgunity_panda_bridge typegrasp_action_server.py outputscreen/ /launch这个启动文件做了三件事启动TCP通信端点启动MoveIt使用fake控制器意味着MoveIt只规划不控制真实或仿真的电机启动一个我们自定义的动作服务器节点它将协调整个抓取流程。编写抓取动作服务器Python示例这是整个任务的“大脑”。它需要订阅来自Unity的物体位姿/object_pose。根据物体位姿计算几个关键的抓取位姿预抓取点物体上方、抓取点、抓取后提升点。调用MoveIt服务依次规划到这些位姿的路径。将规划好的关节轨迹发布给Unity执行。# grasp_action_server.py 简化逻辑 import rospy from moveit_commander import MoveGroupCommander, PlanningSceneInterface from geometry_msgs.msg import PoseStamped from trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint class GraspServer: def __init__(self): self.move_group MoveGroupCommander(panda_arm) self.planning_scene PlanningSceneInterface() self.object_pose None self.traj_pub rospy.Publisher(/planned_trajectory, JointTrajectory, queue_size10) rospy.Subscriber(/object_pose, PoseStamped, self.object_pose_cb) def object_pose_cb(self, msg): self.object_pose msg.pose # 保存物体位置 def plan_to_pose(self, target_pose): # 设置MoveGroup的目标位姿 self.move_group.set_pose_target(target_pose) # 进行规划 plan self.move_group.plan() if plan[0]: return plan[1].joint_trajectory # 返回规划出的轨迹 else: rospy.logwarn(Planning failed!) return None def execute_grasp_sequence(self): if self.object_pose is None: return # 1. 规划到预抓取点 (物体正上方10cm) pre_grasp PoseStamped() pre_grasp.pose self.object_pose pre_grasp.pose.position.z 0.1 traj1 self.plan_to_pose(pre_grasp.pose) self.publish_and_wait(traj1) # 2. 规划到抓取点 (物体中心) grasp_pose PoseStamped() grasp_pose.pose self.object_pose traj2 self.plan_to_pose(grasp_pose.pose) self.publish_and_wait(traj2) # 3. 在这里应该发送信号给Unity让其控制手爪闭合 rospy.loginfo(Close gripper now!) # 4. 规划到提升点 lift_pose PoseStamped() lift_pose.pose self.object_pose lift_pose.pose.position.z 0.2 traj3 self.plan_to_pose(lift_pose.pose) self.publish_and_wait(traj3) def publish_and_wait(self, trajectory): if trajectory: self.traj_pub.publish(trajectory) # 这里需要等待Unity执行完毕。一个简单方法是估算轨迹时间并sleep或者通过Unity反馈。 estimated_time trajectory.points[-1].time_from_start.to_sec() rospy.sleep(estimated_time 0.5) # 额外给点缓冲时间4. 运动规划核心环节与MoveIt集成将MoveIt集成进来核心是利用其解决两大问题运动学逆解和无碰撞路径规划。4.1 规划场景同步与碰撞检测MoveIt需要知道环境中的障碍物信息才能进行无碰撞规划。在我们的架构中障碍物信息主要是待抓取物体由Unity提供。静态障碍物添加在抓取动作服务器初始化时我们可以通过PlanningSceneInterface将桌面作为一个大的Box添加到MoveIt的规划场景中。这只需要做一次。def add_table_to_scene(self): table_pose PoseStamped() table_pose.header.frame_id world table_pose.pose.position.z -0.05 # 桌面略低于世界原点 table_pose.pose.orientation.w 1.0 self.planning_scene.add_box(table, table_pose, size(1.0, 1.0, 0.1))动态物体同步这是关键。我们需要将Unity中可移动的物体那个Cube实时同步到MoveIt的规划场景。通常有两种方式方式A作为动态障碍物在每次规划前通过PlanningSceneInterface的add_box或attach_mesh函数以物体的最新位姿来自/object_pose话题将其添加到场景。规划完成后可以将其移除或更新。这种方式简单但频繁添加/删除物体会带来一些开销。方式B通过规划请求中的allowed_collision_matrix(ACM) 和场景差异更高效的方式是在规划请求中指定规划场景的“差异”。你可以在请求中附带一个PlanningScene消息其中只包含相对于已知场景的变化部分如物体移动了。同时在MoveIt的SRDF配置中可以预先将手爪panda_hand与目标物体如target_object在allowed_collision_matrix中设置为允许碰撞这样在规划抓取路径时MoveIt不会把手爪靠近物体视为碰撞。实操心得对于简单的抓取任务方式A在开发初期更直观。但在复杂场景中方式B是更专业和高效的做法。务必在MoveIt的配置中调整好self_collision和scene_collision的检测参数过于敏感会导致规划失败过于宽松则规划出的路径可能在Unity中发生碰撞。4.2 抓取位姿生成与规划请求抓取的成功率很大程度上取决于抓取位姿Grasp Pose的生成。这里我们演示一个简单的基于规则的方法。确定抓取方式对于方块我们可以采用顶抓从上方垂直向下或侧抓。假设采用顶抓。计算抓取位姿位置通常是物体的中心object_pose.position。姿态这需要根据手爪的夹持方向决定。对于Panda的平行手爪夹持方向通常是绕Z轴旋转。一个简单的顶抓姿态可以是末端执行器的Z轴指向地面负世界Z轴Y轴或X轴与物体的某个边对齐。def compute_grasp_pose(self, object_pose): grasp Pose() grasp.position object_pose.position # 创建一个朝向地面的四元数 (绕X轴旋转180度) from tf.transformations import quaternion_from_euler import math q quaternion_from_euler(math.pi, 0, 0) # Roll180°, Pitch0, Yaw0 grasp.orientation.x q[0] grasp.orientation.y q[1] grasp.orientation.z q[2] grasp.orientation.w q[3] return grasp更高级的方法会使用抓取姿态分析库如GPD - Grasp Pose Detection或者从物体的点云/网格模型中采样生成多个候选抓取位姿并进行评分。发送规划请求使用MoveGroupCommander的set_pose_target()和plan()函数如前面代码所示。关键参数配置planning_time给规划器的最大时间对于复杂场景可以适当增加如5-10秒。num_planning_attempts规划尝试次数多次尝试可能得到不同结果。allowed_planning_time与planning_time类似。goal_tolerance目标位姿容忍度特别是角度容忍度goal_orientation_tolerance对于抓取很重要可以稍微放宽如0.1弧度。5. Unity端轨迹执行与物理交互规划出的轨迹只是一系列关节位置、速度、加速度的时间序列。如何在Unity中平滑、准确地执行它并处理抓取时的物理交互是仿真的最后一步也是最容易出问题的一步。5.1 轨迹订阅与解析在Unity中我们需要编写一个C#脚本例如TrajectoryExecutor来订阅ROS的轨迹话题。using RosMessageTypes.Trajectory; using UnityEngine; public class TrajectoryExecutor : MonoBehaviour { public RosSubscriberJointTrajectoryMsg trajectorySubscriber; public ArticulationBody[] jointArticulationBodies; // 对应机器人的每个关节ArticulationBody private JointTrajectoryMsg currentTrajectory; private float trajectoryStartTime; private bool isExecuting false; void Start() { trajectorySubscriber.Subscribe(OnTrajectoryReceived); } void OnTrajectoryReceived(JointTrajectoryMsg trajectory) { currentTrajectory trajectory; trajectoryStartTime Time.time; isExecuting true; Debug.Log($Received trajectory with {trajectory.points.Length} points.); } void Update() { if (!isExecuting || currentTrajectory null) return; float elapsed Time.time - trajectoryStartTime; // 找到当前时间对应的轨迹点简化线性插值 // 实际应使用更精确的插值并考虑速度、加速度约束 for (int i 0; i currentTrajectory.points.Length - 1; i) { var pointA currentTrajectory.points[i]; var pointB currentTrajectory.points[i 1]; float timeA (float)pointA.time_from_start.sec pointA.time_from_start.nanosec * 1e-9f; float timeB (float)pointB.time_from_start.sec pointB.time_from_start.nanisec * 1e-9f; if (elapsed timeA elapsed timeB) { float t (elapsed - timeA) / (timeB - timeA); for (int j 0; j jointArticulationBodies.Length; j) { float targetPos Mathf.Lerp((float)pointA.positions[j], (float)pointB.positions[j], t); // 设置关节目标位置使用ArticulationBody驱动 var drive jointArticulationBodies[j].xDrive; drive.target targetPos * Mathf.Rad2Deg; // 通常URDF使用弧度Unity Articulation使用度 jointArticulationBodies[j].xDrive drive; } break; } } // 检查轨迹是否执行完毕 float totalTime (float)currentTrajectory.points.Last().time_from_start.sec currentTrajectory.points.Last().time_from_start.nanosec * 1e-9f; if (elapsed totalTime) { isExecuting false; Debug.Log(Trajectory execution finished.); } } }注意事项这里使用了简单的线性插值。对于高精度需求应该实现样条插值并严格遵循轨迹点中给出的速度、加速度信息。ArticulationBody是Unity新的物理关节组件比旧的HingeJoint等更适用于机器人仿真它提供了与URDF中关节类型更好的映射。5.2 手爪控制与抓取物理抓取动作本身闭合手爪通常不由MoveIt规划而是作为一个独立的控制命令。手爪模型确保Panda的hand两个手指在URDF导入后每个手指关节都有对应的ArticulationBody并且关节类型如棱柱关节Prismatic设置正确。抓取指令在ROS的抓取动作服务器发送“闭合手爪”指令时可以通过一个单独的ROS话题如/gripper_command发布一个浮点数代表目标位置或力。Unity端订阅该话题并驱动两个手指关节向目标位置运动。物理抓取实现真正的“抓取”效果依赖于Unity的物理引擎。当手指关节运动并接触到物体时Rigidbody物体和ArticulationBody手指之间会产生碰撞和摩擦力。为了抓得更稳你需要调整物理材质为手指和物体设置合适的Physic Material增加静摩擦力和动摩擦力。施加夹持力一种更稳定的方法是在检测到手指与物体接触后除了位置控制还为手指关节施加一个额外的力通过ArticulationBody.AddForce模拟夹持电机输出的力。使用固定关节Fixed Joint一种常见的“取巧”方法是当检测到物体被手指充分包围时在物体和其中一个手指或手掌之间动态添加一个Fixed Joint组件将它们“粘”在一起。在需要释放时销毁这个关节。这种方法能保证抓取绝对稳定但物理上不够真实。实操心得纯物理驱动的抓取仅靠摩擦力和接触力在仿真中很难稳定尤其是对于光滑表面的物体。在实际项目中我通常会采用“混合模式”先尝试物理抓取如果检测到物体在提升过程中滑动或掉落则动态添加一个弱强度的Fixed Joint作为“保险”。同时在规划提升轨迹时采用较慢、平稳的速度减少惯性带来的扰动。6. 调试、优化与常见问题排查将这套系统跑通后你会遇到各种“坑”。下面是我在实践中总结的一些典型问题和解决思路。6.1 通信与同步问题问题现象可能原因排查步骤与解决方案Unity中收不到ROS话题1. ROS TCP Endpoint未启动或IP/端口错误。2. Unity中RosConnector的IP/端口配置错误。3. 防火墙阻止了TCP连接。1. 在ROS端运行rostopic list检查话题是否存在。运行netstat -tlnp查看10000端口是否被监听。2. 在Unity编辑器中查看RosConnector的日志输出确认连接状态。3. 暂时关闭防火墙或添加规则。确保Unity和ROS在同一网络或本机。MoveIt规划失败提示“Unable to sample any valid states”1. 起始状态Unity发布的关节状态与MoveIt内部状态不一致。2. 目标位姿超出工作空间或奇异点。3. 碰撞检测过于严格起始状态就被认为与环境碰撞。1. 在RViz中通过JointState插件查看MoveIt接收到的关节角度与Unity中模型的实际角度对比。检查URDF模型在Unity和MoveIt中是否一致特别是关节旋转方向。2. 打印目标位姿检查其合理性。尝试一个非常简单的、肯定可达的位姿进行测试。3. 在MoveIt的RViz插件中开启“Collision”显示查看机器人是否在起始位置就与场景中的物体如桌子发生了碰撞。可能是碰撞体尺寸定义过大。轨迹在Unity中执行时抖动或错位1. 关节驱动方式Position/ Velocity与轨迹插值方式不匹配。2. Unity的物理更新频率Fixed Timestep与轨迹时间戳不协调。3. 单位不统一弧度/度。1. 尝试在Unity中将关节驱动从位置控制改为速度或力控制看是否更平滑。调整ArticulationBody的stiffness刚度和damping阻尼参数。2. 确保Unity的Time.fixedDeltaTime稳定。轨迹插值代码中的时间计算要使用Time.time而非Time.deltaTime的累积。3.反复检查MoveIt规划出的关节位置单位是弧度而UnityArticulationBody的xDrive.target通常期望度。这是最常见的错误来源6.2 规划性能与成功率优化选择合适的规划器MoveIt默认集成OMPL其下有多种规划算法如RRTConnect, PRM, EST。对于机械臂抓取这类任务RRTConnect通常是速度和成功率比较均衡的选择。可以在move_group的ompl_planning.yaml配置文件中设置默认规划器。调整规划参数planning_time增加规划时间如从5秒到10秒能给规划器更多探索空间提高复杂场景下的成功率但会降低响应速度。goal_joint_tolerance/goal_position_tolerance/goal_orientation_tolerance适当放宽目标容忍度。对于抓取末端位置精度要求高但姿态特别是绕工具轴旋转可以适当放宽这能极大降低规划难度。allowed_collision_matrix (ACM)善用ACM。明确告诉规划器哪些部件之间允许碰撞如手指与待抓取物体可以避免大量无谓的碰撞检查。使用“Approach”和“Retreat”位姿不要直接规划从A点到精确抓取点B的路径。先规划到抓取点前方一段距离的“Approach”位姿直线接近再规划一小段直线运动到抓取点。同理抓取后先直线“Retreat”再规划到大路径。这符合实际作业流程也更容易规划。在Unity中实现“重规划”如果MoveIt规划失败不要直接让任务失败。可以在Unity端或ROS动作服务器中实现一个简单的重试逻辑比如随机给目标姿态添加一个微小的扰动或者切换到另一个备选的抓取位姿然后重新发起规划请求。6.3 物理仿真稳定性调优Unity的物理仿真步长和参数对抓取稳定性影响巨大。调整Fixed Timestep在Edit - Project Settings - Time中减小Fixed Timestep如从0.02s降到0.005s可以提高物理更新的频率使仿真更平滑稳定但会增加CPU负担。对于机器人仿真0.005s到0.01s是一个常用范围。调整Solver Iterations在Project Settings - Physics中增加Default Solver Iterations和Default Solver Velocity Iterations如从6增加到20-50。这会让物理引擎花更多计算资源去解决复杂的接触和关节约束显著提升抓取等接触密集型仿真的稳定性。合理设置碰撞体避免使用过于复杂的网格碰撞体。对于机器人连杆和简单物体使用基本的Box、Sphere、Capsule碰撞体组合能极大提升性能。在URDF导入时选择为碰撞体生成凸包Convex Hull而非原始网格。管理仿真速度在调试时可以使用Time.timeScale来放慢仿真速度仔细观察抓取瞬间的接触和力反馈情况。这套“Unity-Robotics-Hub MoveIt”的方案打通了高保真可视化仿真与专业运动规划算法之间的壁垒。它最大的价值在于提供了一个快速迭代的沙盒你可以在近乎真实的视觉和物理反馈中反复测试和调整你的抓取位姿生成算法、运动规划参数甚至手爪控制逻辑而无需担心损坏真实的机器人。将调试好的算法和参数迁移到真实机器人上时成功率会大大提高。整个搭建过程虽然涉及ROS、Unity、MoveIt多个环节但一旦跑通其带来的效率和可靠性提升是非常显著的。