using UnityEngine;
namespace UnityMoveIt2.Core
{
///
/// UR5 手动配置工具 / UR5 Manual Setup Tool
/// 用于在Unity中手动创建UR5机器人结构(无需URDF Importer包)
///
public class UR5ManualSetup : MonoBehaviour
{
[Header("UR5 配置参数 / UR5 Configuration")]
[SerializeField]
[Tooltip("是否创建碰撞体")]
private bool createColliders = true;
[SerializeField]
[Tooltip("是否使用重力")]
private bool useGravity = false;
[SerializeField]
[Tooltip("关节刚度 / Joint Stiffness")]
private float jointStiffness = 10000f;
[SerializeField]
[Tooltip("关节阻尼 / Joint Damping")]
private float jointDamping = 100f;
[SerializeField]
[Tooltip("最大关节力 / Max Joint Force")]
private float maxJointForce = 150f;
///
/// UR5的DH参数和几何数据
///
private readonly float[] linkLengths = { 0.089159f, 0.13585f, 0.425f, 0.39225f, 0.10915f, 0.09465f, 0.0823f };
private readonly string[] linkNames = { "base_link", "shoulder_link", "upper_arm_link", "forearm_link", "wrist_1_link", "wrist_2_link", "wrist_3_link", "tool0" };
///
/// 创建完整的UR5机器人结构
///
[ContextMenu("创建 UR5 结构 / Create UR5 Structure")]
public void CreateUR5Structure()
{
Debug.Log("[UR5ManualSetup] 开始创建UR5机器人结构...");
// 清理现有子对象
ClearChildren();
// 创建根Articulation Body
ArticulationBody rootBody = GetOrAddComponent();
rootBody.immovable = true;
rootBody.useGravity = useGravity;
// 创建base_link(作为根的第一个子对象)
GameObject baseLink = CreateLink("base_link", Vector3.zero, Quaternion.identity, transform);
// 注意:只有根Articulation Body可以设置immovable
// base_link作为子对象不能设置immovable
// 创建肩部关节 (shoulder_pan_joint)
GameObject shoulderLink = CreateRevoluteJoint(
"shoulder_link",
baseLink.transform,
new Vector3(0, 0, linkLengths[0]),
Vector3.forward, // Z轴旋转
-Mathf.PI, Mathf.PI
);
// 创建肩部抬升关节 (shoulder_lift_joint)
GameObject upperArmLink = CreateRevoluteJoint(
"upper_arm_link",
shoulderLink.transform,
new Vector3(0, linkLengths[1], 0),
Vector3.up, // Y轴旋转
-Mathf.PI, Mathf.PI
);
// 创建肘关节 (elbow_joint)
GameObject forearmLink = CreateRevoluteJoint(
"forearm_link",
upperArmLink.transform,
new Vector3(0, -0.1197f, linkLengths[2]),
Vector3.up,
-Mathf.PI, Mathf.PI
);
// 创建腕关节1 (wrist_1_joint)
GameObject wrist1Link = CreateRevoluteJoint(
"wrist_1_link",
forearmLink.transform,
new Vector3(0, 0, linkLengths[3]),
Vector3.up,
-Mathf.PI, Mathf.PI
);
// 创建腕关节2 (wrist_2_joint)
GameObject wrist2Link = CreateRevoluteJoint(
"wrist_2_link",
wrist1Link.transform,
new Vector3(0, linkLengths[4], 0),
Vector3.forward, // Z轴旋转
-Mathf.PI, Mathf.PI
);
// 创建腕关节3 (wrist_3_joint)
GameObject wrist3Link = CreateRevoluteJoint(
"wrist_3_link",
wrist2Link.transform,
new Vector3(0, 0, linkLengths[5]),
Vector3.up,
-Mathf.PI, Mathf.PI
);
// 创建tool0(末端执行器)
GameObject tool0 = new GameObject("tool0");
tool0.transform.SetParent(wrist3Link.transform);
tool0.transform.localPosition = new Vector3(0, linkLengths[6], 0);
tool0.transform.localRotation = Quaternion.identity;
// 添加可视化标记
AddVisualMarker(tool0.transform, Color.red, 0.02f);
// 配置机器人适配器
ConfigureRobotAdapter(baseLink.transform, tool0.transform);
Debug.Log("[UR5ManualSetup] ✓ UR5结构创建完成!");
Debug.LogWarning("[UR5ManualSetup] 下一步:请在Assets/UR5/meshes目录下手动为每个link添加mesh模型");
}
///
/// 创建旋转关节
///
private GameObject CreateRevoluteJoint(string name, Transform parent, Vector3 localPos, Vector3 axis, float lowerLimit, float upperLimit)
{
GameObject link = CreateLink(name, localPos, Quaternion.identity, parent);
ArticulationBody body = link.GetComponent();
// 配置为旋转关节
body.jointType = ArticulationJointType.RevoluteJoint;
// 设置关节轴
body.anchorRotation = GetAxisRotation(axis);
// 配置关节驱动
ArticulationDrive drive = body.xDrive;
drive.lowerLimit = lowerLimit * Mathf.Rad2Deg;
drive.upperLimit = upperLimit * Mathf.Rad2Deg;
drive.stiffness = jointStiffness;
drive.damping = jointDamping;
drive.forceLimit = maxJointForce;
drive.target = 0;
body.xDrive = drive;
return link;
}
///
/// 创建连杆
///
private GameObject CreateLink(string name, Vector3 localPos, Quaternion localRot, Transform parent)
{
GameObject link = new GameObject(name);
link.transform.SetParent(parent);
link.transform.localPosition = localPos;
link.transform.localRotation = localRot;
// 添加Articulation Body
ArticulationBody body = link.AddComponent();
body.useGravity = useGravity;
// 添加占位可视化(临时)
AddVisualMarker(link.transform, Color.cyan, 0.03f);
// 添加碰撞体
if (createColliders && name != "tool0")
{
SphereCollider collider = link.AddComponent();
collider.radius = 0.05f;
}
return link;
}
///
/// 添加可视化标记
///
private void AddVisualMarker(Transform parent, Color color, float size)
{
GameObject marker = GameObject.CreatePrimitive(PrimitiveType.Sphere);
marker.name = "Marker";
marker.transform.SetParent(parent);
marker.transform.localPosition = Vector3.zero;
marker.transform.localScale = Vector3.one * size;
Renderer renderer = marker.GetComponent();
Material mat = new Material(Shader.Find("Standard"));
mat.color = color;
renderer.material = mat;
// 移除碰撞体(仅用于可视化)
// 根据是否在编辑器模式使用不同的销毁方法
Collider collider = marker.GetComponent();
if (collider != null)
{
if (Application.isPlaying)
Destroy(collider);
else
DestroyImmediate(collider);
}
}
///
/// 配置机器人适配器
///
private void ConfigureRobotAdapter(Transform baseLink, Transform tool0)
{
UniversalRobotAdapter adapter = GetOrAddComponent();
// 使用反射自动设置私有字段
var baseLinkField = typeof(UniversalRobotAdapter).GetField("baseLink",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var endEffectorField = typeof(UniversalRobotAdapter).GetField("endEffector",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var dofField = typeof(UniversalRobotAdapter).GetField("degreesOfFreedom",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var robotNameField = typeof(UniversalRobotAdapter).GetField("robotName",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (baseLinkField != null && baseLink != null)
{
baseLinkField.SetValue(adapter, baseLink);
Debug.Log($"[UR5ManualSetup] ✓ 自动设置 Base Link: {baseLink.name}");
}
if (endEffectorField != null && tool0 != null)
{
endEffectorField.SetValue(adapter, tool0);
Debug.Log($"[UR5ManualSetup] ✓ 自动设置 End Effector: {tool0.name}");
}
if (dofField != null)
{
dofField.SetValue(adapter, 6);
Debug.Log("[UR5ManualSetup] ✓ 自动设置 DOF: 6");
}
if (robotNameField != null)
{
robotNameField.SetValue(adapter, "UR5");
Debug.Log("[UR5ManualSetup] ✓ 自动设置 Robot Name: UR5");
}
Debug.Log("[UR5ManualSetup] ✓ UniversalRobotAdapter 已自动配置完成!");
}
///
/// 获取关节轴旋转
///
private Quaternion GetAxisRotation(Vector3 axis)
{
if (axis == Vector3.up)
return Quaternion.Euler(0, 90, 0);
else if (axis == Vector3.forward)
return Quaternion.identity;
else if (axis == Vector3.right)
return Quaternion.Euler(90, 0, 0);
else
return Quaternion.identity;
}
///
/// 清理子对象
///
private void ClearChildren()
{
while (transform.childCount > 0)
{
DestroyImmediate(transform.GetChild(0).gameObject);
}
}
///
/// 获取或添加组件
///
private T GetOrAddComponent() where T : Component
{
T component = GetComponent();
if (component == null)
component = gameObject.AddComponent();
return component;
}
///
/// 在Scene视图中绘制辅助线
///
private void OnDrawGizmos()
{
if (transform.childCount > 0)
{
DrawJointHierarchy(transform);
}
}
private void DrawJointHierarchy(Transform parent)
{
foreach (Transform child in parent)
{
Gizmos.color = Color.yellow;
Gizmos.DrawLine(parent.position, child.position);
Gizmos.color = Color.green;
Gizmos.DrawWireSphere(child.position, 0.02f);
DrawJointHierarchy(child);
}
}
}
}