Skip to content

4.7 轨迹控制

概述

Trajectory 模块提供离线轨迹执行、轨迹文件转换的接口,支持复杂轨迹的复现。

核心功能

  • 支持设置和执行离线轨迹文件
  • 支持以安全速度将机器人移动到离线轨迹起始点
  • 支持 CSV 到 trajectory 格式的文件转换
  • 支持查询轨迹文件转换状态

使用场景

  • 实现复杂轨迹的精确复现
  • 转换外部轨迹数据为机器人可执行格式

4.7.1 设置待执行的离线轨迹文件

方法名Trajectory.SetOffLineTrajectoryFile(string path )
描述设置待执行的离线轨迹文件
请求参数path : string 离线轨迹文件程序名 (格式说明见下方备注)
返回值StatusCode: 函数执行结果
备注A.trajectory 轨迹文件格式为文本文件:
- 第 1 行:6 代表 6 个轴,0.001 代表两点间隔 1 ms,8093 代表轨迹点数。
- 第 2 行:6 个轴的起始位置。
- 第 3-8095 行:轨迹点数据,包含 6 轴的位置 / 速度 / 加速度 / 力矩前馈 /do 端口 /do 端口值。
- do_port 取值范围 1~24;do_port 为 -1 表示该位置不触发 IO;do_port 为 1 且 do_state 为 1 表示 do1 端口触发 ON;do_port 为 1 且 do_state 为 0 表示 do1 端口触发 OFF。
用户通过 FileManager.upload 将离线文件发送到机器人控制器根目录,用 4.7.2 和 4.7.3 指令执行轨迹。
兼容的机器人软件版本协作 (Copper): v7.5.0.0+
工业 (Bronze): v7.5.0.0+

4.7.2 让机器人以安全速度移动到离线轨迹中的起始点

方法名Trajectory.PrepareOfflineTrajectory()
描述让机器人以安全速度移动到离线轨迹中的起始点
请求参数无参数
返回值StatusCode: 函数执行结果
兼容的机器人软件版本协作 (Copper): v7.5.0.0+
工业 (Bronze): v7.5.0.0+

4.7.3 让机器人开始执行离线轨迹文件

方法名Trajectory.ExecuteOfflineTrajectory()
描述让机器人开始执行离线轨迹程序
请求参数无参数
返回值StatusCode: 函数执行结果
兼容的机器人软件版本协作 (Copper): v7.5.0.0+
工业 (Bronze): v7.5.0.0+

4.7.4 轨迹文件转换功能

方法名Trajectory.TransformCsvToTrajectory(string fileName )
描述将轨迹 CSV 文件转换成 trajectory 格式的轨迹文件并存放在控制柜的轨迹文件目录上
请求参数fileName : string CSV 轨迹文件名 (无需 .csv 后缀)
返回值string:转换后轨迹文件路径
StatusCode: 函数执行结果
兼容的机器人软件版本协作 (Copper): v7.5.0.0+
工业 (Bronze): v7.5.0.0+

4.7.5 查询轨迹文件转换状态

方法名Trajectory.CheckTransformStatus(string fileName )
描述查询轨迹文件转换的当前状态
请求参数fileName : string 轨迹文件路径 ( TransformCsvToTrajectory 接口返回)
返回值TransformState: 转换状态
StatusCode: 函数执行结果
兼容的机器人软件版本协作 (Copper): v7.5.0.0+
工业 (Bronze): v7.5.0.0+

示例代码

Trajectory/OfflineTrajectory.cs
cs
using System.IO;
using Agilebot.IR;
using Agilebot.IR.Trajectory;
using Agilebot.IR.Types;

public class OfflineTrajectory
{
    public static StatusCode Run(string controllerIP, bool useLocalProxy = true)
    {
        // [ZH] 初始化捷勃特机器人
        // [EN] Initialize the Agilebot robot
        Arm controller = new Arm(controllerIP, useLocalProxy);

        // [ZH] 连接捷勃特机器人
        // [EN] Connect to the Agilebot robot
        StatusCode code = controller.ConnectSync();
        Console.WriteLine(code != StatusCode.OK ? code.GetDescription() : "连接成功/Successfully connected.");

        if (code != StatusCode.OK)
        {
            return code;
        }

        try
        {
            // [ZH] 获取机器人模式
            // [EN] Get robot mode
            (UserOpMode opMode, StatusCode opCode) = controller.GetOpMode();
            if (opCode == StatusCode.OK)
            {
                Console.WriteLine($"当前机器人模式/Current robot mode: {opMode}");
                if (opMode != UserOpMode.AUTO)
                {
                    Console.WriteLine(
                        $"离线轨迹执行必须在机器人自动模式下/Offline trajectory execution must be in automatic mode"
                    );
                    return StatusCode.OtherReason;
                }
            }
            else
            {
                Console.WriteLine($"获取机器人模式失败/Failed to get robot mode: {opCode.GetDescription()}");
            }

            // [ZH] 添加程序文件到机器人中
            // [EN] Add program file to robot
            string file_user_program = GetTestFilePath("test.csv");
            StatusCode ret_code = controller.FileManager.Upload(file_user_program, FileType.TmpFile, true);
            if (ret_code != StatusCode.OK)
            {
                Console.WriteLine($"上传文件失败/Upload file failed: {ret_code.GetDescription()}");
                return ret_code;
            }
            Console.WriteLine("文件上传成功/File upload success");

            // [ZH] 测试CSV转换为轨迹文件功能
            // [EN] Test CSV to trajectory file conversion functionality
            string csvFilename = "test.csv";
            (string trajFileName, StatusCode transformCode) = controller.Trajectory.TransformCsvToTrajectory(
                csvFilename
            );

            if (transformCode != StatusCode.OK)
            {
                Console.WriteLine($"CSV转换失败/CSV conversion failed: {transformCode.GetDescription()}");
                return transformCode;
            }
            Console.WriteLine($"CSV转换成功/CSV conversion success, trajectory file: {trajFileName}");

            // [ZH] 检查转换状态
            // [EN] Check conversion status
            var startTime = System.DateTime.Now;
            TransformState state;
            StatusCode statusCode;
            do
            {
                (state, statusCode) = controller.Trajectory.CheckTransformStatus(
                    System.IO.Path.GetFileName(trajFileName)
                );
                if (statusCode != StatusCode.OK)
                {
                    Console.WriteLine($"检查转换状态失败/Check transform status failed: {statusCode.GetDescription()}");
                    return statusCode;
                }

                Console.WriteLine($"转换状态/Transform state: {state}");
                Thread.Sleep(2000); // 等待2秒

                if (System.DateTime.Now - startTime > System.TimeSpan.FromSeconds(60))
                {
                    Console.WriteLine("转换状态检查超时/Transform status check timeout");
                    break;
                }
            } while (state != TransformState.TRANSFORM_SUCCESS && state != TransformState.TRANSFORM_FAILED);

            if (state == TransformState.TRANSFORM_FAILED)
            {
                Console.WriteLine("CSV转换失败/CSV conversion failed");
                return StatusCode.OtherReason;
            }

            // [ZH] 转换任务成功并进行了结果查询后 服务端不会继续保存转换任务的状态
            // [EN] After the conversion task is successful and the result is queried, the server will not continue to save the conversion task status
            (TransformState finalState, StatusCode finalCode) = controller.Trajectory.CheckTransformStatus(
                System.IO.Path.GetFileName(trajFileName)
            );
            if (finalCode != StatusCode.OK)
            {
                Console.WriteLine($"最终状态检查失败/Final status check failed: {finalCode.GetDescription()}");
                return finalCode;
            }
            Console.WriteLine($"最终转换状态/Final transform state: {finalState}");

            // [ZH] 设置轨迹文件
            // [EN] Set trajectory file
            code = controller.Trajectory.SetOffLineTrajectoryFile("test_torque.trajectory");
            if (code != StatusCode.OK)
            {
                Console.WriteLine($"设置轨迹文件失败/Set trajectory file failed: {code.GetDescription()}");
                return code;
            }
            Console.WriteLine("设置轨迹文件成功/Set trajectory file success");

            // [ZH] 准备离线轨迹
            // [EN] Prepare offline trajectory
            code = controller.Trajectory.PrepareOfflineTrajectory();
            if (code != StatusCode.OK)
            {
                Console.WriteLine($"准备离线轨迹失败/Prepare offline trajectory failed: {code.GetDescription()}");
                return code;
            }
            Console.WriteLine("准备离线轨迹成功/Prepare offline trajectory success");

            // [ZH] 等待机器人和伺服器空闲
            // [EN] Wait for robot and servo to be idle
            startTime = System.DateTime.Now;
            RobotState robotStatus;
            ServoState servoStatus;
            StatusCode robotStatusCode;
            StatusCode servoStatusCode;

            do
            {
                (robotStatus, robotStatusCode) = controller.GetRobotState();
                if (robotStatusCode != StatusCode.OK)
                {
                    Console.WriteLine($"获取机器人状态失败/Get robot state failed: {robotStatusCode.GetDescription()}");
                    return robotStatusCode;
                }

                (servoStatus, servoStatusCode) = controller.GetServoState();
                if (servoStatusCode != StatusCode.OK)
                {
                    Console.WriteLine($"获取伺服状态失败/Get servo state failed: {servoStatusCode.GetDescription()}");
                    return servoStatusCode;
                }

                Console.WriteLine($"机器人状态/Robot state: {robotStatus}, 伺服状态/Servo state: {servoStatus}");

                if (robotStatus == RobotState.ROBOT_IDLE && servoStatus == ServoState.SERVO_IDLE)
                {
                    Console.WriteLine("机器人和伺服器已空闲/Robot and servo are idle");
                    break;
                }

                Thread.Sleep(2000); // 等待2秒

                if (System.DateTime.Now - startTime > System.TimeSpan.FromSeconds(60))
                {
                    Console.WriteLine("等待机器人和伺服器空闲超时/Waiting for robot and servo idle timeout");
                    break;
                }
            } while (true);

            // [ZH] 执行离线轨迹
            // [EN] Execute offline trajectory
            code = controller.Trajectory.ExecuteOfflineTrajectory();
            if (code == StatusCode.OK)
            {
                Console.WriteLine("执行离线轨迹成功/Execute offline trajectory success");
                Console.WriteLine("机器人开始执行轨迹程序/Robot started executing trajectory program");
            }
            else
            {
                Console.WriteLine($"执行离线轨迹失败/Execute offline trajectory failed: {code.GetDescription()}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(
                $"执行过程中发生异常/Exception occurred during execution/Exception occurred during execution: {ex.Message}"
            );
            code = StatusCode.OtherReason;
        }
        finally
        {
            // [ZH] 关闭连接
            // [EN] Close the connection
            StatusCode disconnectCode = controller.Disconnect();
            if (disconnectCode != StatusCode.OK)
            {
                Console.WriteLine(disconnectCode.GetDescription());
                if (code == StatusCode.OK)
                    code = disconnectCode;
            }
        }

        return code;
    }

    /// <summary>
    /// 获取test_files文件夹中文件的路径示例方法
    /// 展示如何获取当前程序目录下的test_files文件夹中的文件路径
    /// </summary>
    private static string GetTestFilePath(string fileName)
    {
        // 获取当前程序集的目录
        string? codeFilePath = new System.Diagnostics.StackTrace(true).GetFrame(0)?.GetFileName();
        if (string.IsNullOrEmpty(codeFilePath))
        {
            throw new InvalidOperationException("无法获取当前文件路径/Cannot get current file path");
        }

        string? codeDirectory = Path.GetDirectoryName(codeFilePath);
        if (string.IsNullOrEmpty(codeDirectory))
        {
            throw new InvalidOperationException("无法获取当前目录路径/Cannot get current directory path");
        }

        // 构建test_files文件夹路径
        string testFilesDirectory = Path.Combine(codeDirectory, "test_files");
        // 构建文件完整路径
        string filePath = Path.Combine(testFilesDirectory, fileName);
        return filePath;
    }
}