- 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>
344 lines
12 KiB
C#
344 lines
12 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace UnityMoveIt2.Core
|
|
{
|
|
/// <summary>
|
|
/// 可视化引擎 - 轨迹可视化部分 / Visualization Engine - Trajectory Visualization Part
|
|
/// </summary>
|
|
public partial class VisualizationEngine
|
|
{
|
|
#region Trajectory Fields
|
|
|
|
[Header("轨迹可视化设置 / Trajectory Visualization Settings")]
|
|
[SerializeField]
|
|
private Color trajectoryColor = Color.green;
|
|
|
|
[SerializeField]
|
|
private Color executingColor = Color.blue;
|
|
|
|
[SerializeField]
|
|
private Color completedColor = Color.gray;
|
|
|
|
[SerializeField]
|
|
[Tooltip("轨迹线宽度 / Trajectory Line Width")]
|
|
private float lineWidth = 0.02f;
|
|
|
|
[SerializeField]
|
|
[Tooltip("显示路径点 / Show Waypoints")]
|
|
private bool showWaypoints = true;
|
|
|
|
[SerializeField]
|
|
[Tooltip("路径点大小 / Waypoint Size")]
|
|
private float waypointSize = 0.03f;
|
|
|
|
private LineRenderer trajectoryLineRenderer;
|
|
private List<Vector3> trajectoryPoints;
|
|
private List<GameObject> waypointMarkers;
|
|
private float trajectoryProgress = 0f;
|
|
|
|
#endregion
|
|
|
|
#region Trajectory Initialization
|
|
|
|
private void InitializeTrajectoryVisualization()
|
|
{
|
|
trajectoryPoints = new List<Vector3>();
|
|
waypointMarkers = new List<GameObject>();
|
|
|
|
// 创建LineRenderer / Create LineRenderer
|
|
GameObject lineObj = GetOrCreateVisualizationObject("TrajectoryLine", trajectoryContainer.transform);
|
|
trajectoryLineRenderer = lineObj.GetComponent<LineRenderer>();
|
|
|
|
if (trajectoryLineRenderer == null)
|
|
{
|
|
trajectoryLineRenderer = lineObj.AddComponent<LineRenderer>();
|
|
}
|
|
|
|
// 配置LineRenderer / Configure LineRenderer
|
|
trajectoryLineRenderer.startWidth = lineWidth;
|
|
trajectoryLineRenderer.endWidth = lineWidth;
|
|
trajectoryLineRenderer.material = new Material(Shader.Find("Sprites/Default"));
|
|
trajectoryLineRenderer.startColor = trajectoryColor;
|
|
trajectoryLineRenderer.endColor = trajectoryColor;
|
|
trajectoryLineRenderer.positionCount = 0;
|
|
trajectoryLineRenderer.useWorldSpace = true;
|
|
|
|
Debug.Log("[VisualizationEngine] 轨迹可视化已初始化 / Trajectory visualization initialized");
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Trajectory Update
|
|
|
|
private void UpdateTrajectoryVisualization()
|
|
{
|
|
if (trajectoryLineRenderer == null || trajectoryPoints.Count == 0)
|
|
return;
|
|
|
|
// 更新LineRenderer位置 / Update LineRenderer positions
|
|
trajectoryLineRenderer.positionCount = trajectoryPoints.Count;
|
|
trajectoryLineRenderer.SetPositions(trajectoryPoints.ToArray());
|
|
|
|
// 更新颜色渐变以显示执行进度 / Update color gradient to show execution progress
|
|
if (trajectoryProgress > 0f)
|
|
{
|
|
UpdateTrajectoryGradient();
|
|
}
|
|
|
|
// 更新路径点标记 / Update waypoint markers
|
|
if (showWaypoints)
|
|
{
|
|
UpdateWaypointMarkers();
|
|
}
|
|
}
|
|
|
|
private void UpdateTrajectoryGradient()
|
|
{
|
|
Gradient gradient = new Gradient();
|
|
GradientColorKey[] colorKeys = new GradientColorKey[3];
|
|
GradientAlphaKey[] alphaKeys = new GradientAlphaKey[2];
|
|
|
|
// 已完成部分 / Completed part
|
|
colorKeys[0] = new GradientColorKey(completedColor, 0.0f);
|
|
|
|
// 当前执行点 / Current execution point
|
|
colorKeys[1] = new GradientColorKey(executingColor, trajectoryProgress);
|
|
|
|
// 未执行部分 / Remaining part
|
|
colorKeys[2] = new GradientColorKey(trajectoryColor, 1.0f);
|
|
|
|
alphaKeys[0] = new GradientAlphaKey(1.0f, 0.0f);
|
|
alphaKeys[1] = new GradientAlphaKey(1.0f, 1.0f);
|
|
|
|
gradient.SetKeys(colorKeys, alphaKeys);
|
|
trajectoryLineRenderer.colorGradient = gradient;
|
|
}
|
|
|
|
private void UpdateWaypointMarkers()
|
|
{
|
|
// 确保标记数量匹配路径点数量 / Ensure marker count matches waypoint count
|
|
while (waypointMarkers.Count < trajectoryPoints.Count)
|
|
{
|
|
CreateWaypointMarker(waypointMarkers.Count);
|
|
}
|
|
|
|
while (waypointMarkers.Count > trajectoryPoints.Count)
|
|
{
|
|
int lastIndex = waypointMarkers.Count - 1;
|
|
if (waypointMarkers[lastIndex] != null)
|
|
{
|
|
Destroy(waypointMarkers[lastIndex]);
|
|
}
|
|
waypointMarkers.RemoveAt(lastIndex);
|
|
}
|
|
|
|
// 更新标记位置 / Update marker positions
|
|
for (int i = 0; i < trajectoryPoints.Count; i++)
|
|
{
|
|
if (waypointMarkers[i] != null)
|
|
{
|
|
waypointMarkers[i].transform.position = trajectoryPoints[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CreateWaypointMarker(int index)
|
|
{
|
|
GameObject marker = GameObject.CreatePrimitive(PrimitiveType.Sphere);
|
|
marker.name = $"Waypoint_{index}";
|
|
marker.transform.SetParent(trajectoryContainer.transform);
|
|
marker.transform.localScale = Vector3.one * waypointSize;
|
|
|
|
// 配置材质 / Configure material
|
|
MeshRenderer renderer = marker.GetComponent<MeshRenderer>();
|
|
Material mat = new Material(Shader.Find("Standard"));
|
|
mat.color = trajectoryColor;
|
|
renderer.material = mat;
|
|
|
|
// 移除碰撞器 / Remove collider
|
|
Collider collider = marker.GetComponent<Collider>();
|
|
if (collider != null)
|
|
{
|
|
Destroy(collider);
|
|
}
|
|
|
|
waypointMarkers.Add(marker);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Trajectory Methods
|
|
|
|
/// <summary>
|
|
/// 可视化轨迹 / Visualize Trajectory
|
|
/// </summary>
|
|
public void VisualizeTrajectory(List<Pose> waypoints)
|
|
{
|
|
if (waypoints == null || waypoints.Count == 0)
|
|
{
|
|
Debug.LogWarning("[VisualizationEngine] 轨迹为空 / Trajectory is empty");
|
|
return;
|
|
}
|
|
|
|
trajectoryPoints.Clear();
|
|
|
|
foreach (var waypoint in waypoints)
|
|
{
|
|
trajectoryPoints.Add(waypoint.position);
|
|
}
|
|
|
|
// 限制点数 / Limit point count
|
|
if (trajectoryPoints.Count > maxTrajectoryPoints)
|
|
{
|
|
trajectoryPoints = DownsampleTrajectory(trajectoryPoints, maxTrajectoryPoints);
|
|
}
|
|
|
|
trajectoryProgress = 0f;
|
|
UpdateTrajectoryVisualization();
|
|
|
|
Debug.Log($"[VisualizationEngine] 可视化轨迹: {trajectoryPoints.Count} 个点 / Visualized trajectory: {trajectoryPoints.Count} points");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 从关节轨迹生成末端执行器轨迹可视化 / Generate End Effector Trajectory from Joint Trajectory
|
|
/// </summary>
|
|
public void VisualizeJointTrajectory(List<float[]> jointTrajectory)
|
|
{
|
|
if (jointTrajectory == null || jointTrajectory.Count == 0 || robotAdapter == null)
|
|
{
|
|
Debug.LogWarning("[VisualizationEngine] 无法从关节轨迹生成可视化 / Cannot generate visualization from joint trajectory");
|
|
return;
|
|
}
|
|
|
|
List<Pose> endEffectorPoses = new List<Pose>();
|
|
|
|
// 保存当前关节状态 / Save current joint state
|
|
float[] originalPositions = robotAdapter.GetCurrentJointPositions();
|
|
|
|
// 对每个关节状态计算末端执行器位姿 / Calculate end effector pose for each joint state
|
|
foreach (var jointPositions in jointTrajectory)
|
|
{
|
|
robotAdapter.SetJointPositions(jointPositions);
|
|
Pose endEffectorPose = robotAdapter.GetEndEffectorPose();
|
|
endEffectorPoses.Add(endEffectorPose);
|
|
}
|
|
|
|
// 恢复原始关节状态 / Restore original joint state
|
|
robotAdapter.SetJointPositions(originalPositions);
|
|
|
|
// 可视化 / Visualize
|
|
VisualizeTrajectory(endEffectorPoses);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 更新轨迹执行进度 / Update Trajectory Execution Progress
|
|
/// </summary>
|
|
public void UpdateTrajectoryProgress(float progress)
|
|
{
|
|
trajectoryProgress = Mathf.Clamp01(progress);
|
|
UpdateTrajectoryVisualization();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 清除轨迹可视化 / Clear Trajectory Visualization
|
|
/// </summary>
|
|
public void ClearTrajectoryVisualization()
|
|
{
|
|
trajectoryPoints.Clear();
|
|
|
|
if (trajectoryLineRenderer != null)
|
|
{
|
|
trajectoryLineRenderer.positionCount = 0;
|
|
}
|
|
|
|
foreach (var marker in waypointMarkers)
|
|
{
|
|
if (marker != null)
|
|
{
|
|
Destroy(marker);
|
|
}
|
|
}
|
|
waypointMarkers.Clear();
|
|
|
|
trajectoryProgress = 0f;
|
|
|
|
Debug.Log("[VisualizationEngine] 已清除轨迹可视化 / Cleared trajectory visualization");
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Trajectory Utilities
|
|
|
|
/// <summary>
|
|
/// 下采样轨迹点 / Downsample Trajectory Points
|
|
/// </summary>
|
|
private List<Vector3> DownsampleTrajectory(List<Vector3> points, int targetCount)
|
|
{
|
|
if (points.Count <= targetCount)
|
|
return points;
|
|
|
|
List<Vector3> downsampled = new List<Vector3>();
|
|
float step = (float)(points.Count - 1) / (targetCount - 1);
|
|
|
|
for (int i = 0; i < targetCount; i++)
|
|
{
|
|
int index = Mathf.RoundToInt(i * step);
|
|
downsampled.Add(points[index]);
|
|
}
|
|
|
|
return downsampled;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 计算轨迹总长度 / Calculate Total Trajectory Length
|
|
/// </summary>
|
|
public float GetTrajectoryLength()
|
|
{
|
|
if (trajectoryPoints.Count < 2)
|
|
return 0f;
|
|
|
|
float totalLength = 0f;
|
|
for (int i = 1; i < trajectoryPoints.Count; i++)
|
|
{
|
|
totalLength += Vector3.Distance(trajectoryPoints[i - 1], trajectoryPoints[i]);
|
|
}
|
|
|
|
return totalLength;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取轨迹上指定进度的位置 / Get Position at Specified Progress on Trajectory
|
|
/// </summary>
|
|
public Vector3 GetTrajectoryPositionAtProgress(float progress)
|
|
{
|
|
progress = Mathf.Clamp01(progress);
|
|
|
|
if (trajectoryPoints.Count == 0)
|
|
return Vector3.zero;
|
|
|
|
if (trajectoryPoints.Count == 1)
|
|
return trajectoryPoints[0];
|
|
|
|
float totalLength = GetTrajectoryLength();
|
|
float targetLength = totalLength * progress;
|
|
float currentLength = 0f;
|
|
|
|
for (int i = 1; i < trajectoryPoints.Count; i++)
|
|
{
|
|
float segmentLength = Vector3.Distance(trajectoryPoints[i - 1], trajectoryPoints[i]);
|
|
if (currentLength + segmentLength >= targetLength)
|
|
{
|
|
float t = (targetLength - currentLength) / segmentLength;
|
|
return Vector3.Lerp(trajectoryPoints[i - 1], trajectoryPoints[i], t);
|
|
}
|
|
currentLength += segmentLength;
|
|
}
|
|
|
|
return trajectoryPoints[trajectoryPoints.Count - 1];
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|