- Unity frontend with ROS-TCP-Connector for ROS2 communication - Docker-based ROS2 Jazzy backend with MoveIt2 integration - Support for 1-9 DOF manipulators - UR5 robot configuration and URDF files - Assembly task feasibility analysis tools - Comprehensive documentation and deployment guides 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
301 lines
10 KiB
C#
301 lines
10 KiB
C#
using UnityEngine;
|
||
|
||
namespace UnityMoveIt2.Core
|
||
{
|
||
/// <summary>
|
||
/// 机器人自动配置器 / Robot Auto Configurator
|
||
/// 运行时自动检测并配置机器人组件 / Auto-detect and configure robot components at runtime
|
||
/// </summary>
|
||
[RequireComponent(typeof(UniversalRobotAdapter))]
|
||
[ExecuteInEditMode]
|
||
public class RobotAutoConfigurator : MonoBehaviour
|
||
{
|
||
[Header("自动配置设置 / Auto Configuration Settings")]
|
||
[SerializeField]
|
||
[Tooltip("是否在编辑器模式下自动配置")]
|
||
private bool autoConfigureInEditor = true;
|
||
|
||
[SerializeField]
|
||
[Tooltip("是否在Play模式下自动配置")]
|
||
private bool autoConfigureAtRuntime = true;
|
||
|
||
[SerializeField]
|
||
[Tooltip("基座链接名称 / Base Link Name")]
|
||
private string baseLinkName = "base_link";
|
||
|
||
[SerializeField]
|
||
[Tooltip("末端执行器名称 / End Effector Name")]
|
||
private string endEffectorName = "tool0";
|
||
|
||
[Header("配置状态 / Configuration Status")]
|
||
[SerializeField]
|
||
private bool isConfigured = false;
|
||
|
||
private UniversalRobotAdapter robotAdapter;
|
||
|
||
#region Unity Lifecycle
|
||
|
||
private void Awake()
|
||
{
|
||
robotAdapter = GetComponent<UniversalRobotAdapter>();
|
||
|
||
if (Application.isPlaying && autoConfigureAtRuntime && !isConfigured)
|
||
{
|
||
ConfigureRobot();
|
||
}
|
||
}
|
||
|
||
private void OnValidate()
|
||
{
|
||
if (!Application.isPlaying && autoConfigureInEditor)
|
||
{
|
||
// 在编辑器模式下延迟配置
|
||
UnityEditor.EditorApplication.delayCall += () =>
|
||
{
|
||
if (this != null)
|
||
ConfigureRobot();
|
||
};
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Auto Configuration
|
||
|
||
/// <summary>
|
||
/// 配置机器人 / Configure Robot
|
||
/// </summary>
|
||
[ContextMenu("自动配置机器人 / Auto Configure Robot")]
|
||
public void ConfigureRobot()
|
||
{
|
||
if (robotAdapter == null)
|
||
{
|
||
robotAdapter = GetComponent<UniversalRobotAdapter>();
|
||
if (robotAdapter == null)
|
||
{
|
||
Debug.LogError("[RobotAutoConfigurator] 未找到 UniversalRobotAdapter 组件");
|
||
return;
|
||
}
|
||
}
|
||
|
||
Debug.Log("[RobotAutoConfigurator] 开始自动配置机器人...");
|
||
|
||
bool success = true;
|
||
|
||
// 查找并设置 Base Link
|
||
if (!ConfigureBaseLink())
|
||
{
|
||
success = false;
|
||
}
|
||
|
||
// 查找并设置 End Effector
|
||
if (!ConfigureEndEffector())
|
||
{
|
||
success = false;
|
||
}
|
||
|
||
// 配置其他参数
|
||
ConfigureAdditionalParameters();
|
||
|
||
if (success)
|
||
{
|
||
isConfigured = true;
|
||
Debug.Log("[RobotAutoConfigurator] ✓ 机器人自动配置成功!");
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("[RobotAutoConfigurator] 机器人配置不完整,请检查Hierarchy结构");
|
||
}
|
||
|
||
// 标记为已修改(仅编辑器模式)
|
||
if (!Application.isPlaying)
|
||
{
|
||
#if UNITY_EDITOR
|
||
UnityEditor.EditorUtility.SetDirty(robotAdapter);
|
||
UnityEditor.EditorUtility.SetDirty(this);
|
||
#endif
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 配置基座链接 / Configure Base Link
|
||
/// </summary>
|
||
private bool ConfigureBaseLink()
|
||
{
|
||
Transform baseLink = FindChildRecursive(transform, baseLinkName);
|
||
|
||
if (baseLink != null)
|
||
{
|
||
// 使用反射设置私有字段
|
||
var baseLinkField = typeof(UniversalRobotAdapter).GetField("baseLink",
|
||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||
|
||
if (baseLinkField != null)
|
||
{
|
||
baseLinkField.SetValue(robotAdapter, baseLink);
|
||
Debug.Log($"[RobotAutoConfigurator] ✓ 设置 Base Link: {baseLink.name}");
|
||
return true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning($"[RobotAutoConfigurator] ✗ 未找到 Base Link: {baseLinkName}");
|
||
Debug.LogWarning("[RobotAutoConfigurator] 提示: 请确保机器人结构中包含名为 'base_link' 的对象");
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 配置末端执行器 / Configure End Effector
|
||
/// </summary>
|
||
private bool ConfigureEndEffector()
|
||
{
|
||
Transform endEffector = FindChildRecursive(transform, endEffectorName);
|
||
|
||
if (endEffector != null)
|
||
{
|
||
// 使用反射设置私有字段
|
||
var endEffectorField = typeof(UniversalRobotAdapter).GetField("endEffector",
|
||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||
|
||
if (endEffectorField != null)
|
||
{
|
||
endEffectorField.SetValue(robotAdapter, endEffector);
|
||
Debug.Log($"[RobotAutoConfigurator] ✓ 设置 End Effector: {endEffector.name}");
|
||
return true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning($"[RobotAutoConfigurator] ✗ 未找到 End Effector: {endEffectorName}");
|
||
Debug.LogWarning("[RobotAutoConfigurator] 提示: 请确保机器人结构中包含名为 'tool0' 的对象");
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 配置其他参数 / Configure Additional Parameters
|
||
/// </summary>
|
||
private void ConfigureAdditionalParameters()
|
||
{
|
||
// 统计关节数量
|
||
ArticulationBody[] articulationBodies = GetComponentsInChildren<ArticulationBody>();
|
||
int jointCount = articulationBodies.Length - 1; // 减去root body
|
||
|
||
// 使用反射设置DOF
|
||
var dofField = typeof(UniversalRobotAdapter).GetField("degreesOfFreedom",
|
||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||
|
||
if (dofField != null && jointCount > 0 && jointCount <= 9)
|
||
{
|
||
dofField.SetValue(robotAdapter, jointCount);
|
||
Debug.Log($"[RobotAutoConfigurator] ✓ 检测到 {jointCount} 个关节");
|
||
}
|
||
|
||
// 设置机器人名称
|
||
var robotNameField = typeof(UniversalRobotAdapter).GetField("robotName",
|
||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||
|
||
if (robotNameField != null)
|
||
{
|
||
robotNameField.SetValue(robotAdapter, gameObject.name);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Helper Methods
|
||
|
||
/// <summary>
|
||
/// 递归查找子对象 / Find Child Recursively
|
||
/// </summary>
|
||
private Transform FindChildRecursive(Transform parent, string childName)
|
||
{
|
||
// 先检查直接子对象
|
||
Transform result = parent.Find(childName);
|
||
if (result != null)
|
||
return result;
|
||
|
||
// 递归查找所有子对象
|
||
foreach (Transform child in parent)
|
||
{
|
||
result = FindChildRecursive(child, childName);
|
||
if (result != null)
|
||
return result;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证配置 / Validate Configuration
|
||
/// </summary>
|
||
[ContextMenu("验证配置 / Validate Configuration")]
|
||
public void ValidateConfiguration()
|
||
{
|
||
Debug.Log("========== 机器人配置验证 ==========");
|
||
|
||
// 检查Base Link
|
||
Transform baseLink = FindChildRecursive(transform, baseLinkName);
|
||
if (baseLink != null)
|
||
Debug.Log($"✓ Base Link 存在: {baseLink.name}");
|
||
else
|
||
Debug.LogError($"✗ Base Link 不存在: {baseLinkName}");
|
||
|
||
// 检查End Effector
|
||
Transform endEffector = FindChildRecursive(transform, endEffectorName);
|
||
if (endEffector != null)
|
||
Debug.Log($"✓ End Effector 存在: {endEffector.name}");
|
||
else
|
||
Debug.LogError($"✗ End Effector 不存在: {endEffectorName}");
|
||
|
||
// 统计关节
|
||
ArticulationBody[] joints = GetComponentsInChildren<ArticulationBody>();
|
||
Debug.Log($"✓ 找到 {joints.Length} 个 Articulation Body");
|
||
|
||
// 检查Adapter
|
||
if (robotAdapter != null)
|
||
Debug.Log("✓ UniversalRobotAdapter 组件存在");
|
||
else
|
||
Debug.LogError("✗ UniversalRobotAdapter 组件不存在");
|
||
|
||
Debug.Log("====================================");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 显示层级结构 / Show Hierarchy
|
||
/// </summary>
|
||
[ContextMenu("显示层级结构 / Show Hierarchy")]
|
||
public void ShowHierarchy()
|
||
{
|
||
Debug.Log("========== 机器人层级结构 ==========");
|
||
PrintHierarchy(transform, 0);
|
||
Debug.Log("====================================");
|
||
}
|
||
|
||
private void PrintHierarchy(Transform parent, int level)
|
||
{
|
||
string indent = new string(' ', level * 2);
|
||
string components = "";
|
||
|
||
if (parent.GetComponent<ArticulationBody>() != null)
|
||
components += "[AB] ";
|
||
if (parent.GetComponent<MeshRenderer>() != null)
|
||
components += "[MR] ";
|
||
if (parent.GetComponent<Collider>() != null)
|
||
components += "[Col] ";
|
||
|
||
Debug.Log($"{indent}└─ {parent.name} {components}");
|
||
|
||
foreach (Transform child in parent)
|
||
{
|
||
PrintHierarchy(child, level + 1);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
}
|