运动控制卡初学(1)

ZMotion运动卡 学习笔记(一)

image-20230309012106513

第一步 使用Zmotion软件判断当前设备是否运行良好

  1. 打开ZDevelop软件

  2. 单击连接

    image-20230308182053930

    3.全新的轴卡设备默认Ip地址是192.168.0.11,需要手动修改下,然后单击连接。

    image-20230308182150884

    1. 成功以后会看到如下信息

      image-20230308182303803

      5.使用手动控制的方式进行控制

      image-20230308181944558

      image-20230308182359073

      我这里目前是单个轴的。轴类型一般写1(1 脉冲方向方式的步进或者伺服).

      image-20230308193217283

      脉冲当量(基本上可以理解为1mm对于的脉冲个数) 计算如下:

      细分:步进电机通过细分驱动器的驱动,其步距角变小。如驱动器工作在10细分状态时,其步距角只为“固定步距角”的十分之一,也就是:当驱动器工作在不细分的整步状态时,控制系统每发一个步进脉冲,电机转动1.8;而用细分驱动器工作在10细分状态时,电机只转动0.18度。细分功能完全是由驱动器靠精度控制电机的相电流所产生的,于电机无关。

      导程:是螺纹上任意一点沿同一条螺旋线转一周所移动的轴向距离。简单说就是。电机转动一圈以后,这个机器横向移动的距离。

​ 我手上的这款电机的步距角是1.8°,转动到360°需要200个脉冲。然后又因为驱动器的细分是8.所以转动一周实际需要200*8=1600个脉冲。已知旋转一周的导程是4mm,消耗1600个脉冲。那么求解1mm的对应脉冲数就是=1600/4=400个。所以当前脉冲当量设置为400.即让步进电机前进1mm。

  1. 测试办法一般就是使用下左和右运动即可。

第二步 开始软件功能设计

主要思考方向:(先单轴控制)

  1. 把界面控制与功能函数隔离
  2. 功能函数基本覆盖如下的功能:
    1. 板卡的基本操作:通断、连续运动、点动、绝对运动与相对运动,插补运动(相对位置、圆弧)与回零动作等
    2. 板卡的消息的处理:板卡的错误信息处理,获取板卡的实时运动与位置信息。

大致需求如上:那么编写Zmotion.cs文件的功能基本如下:

  1. OperationResult 存放操作结果:返回成功还是失败的结果

  2. Zmotion类存放直接与轴卡交互的功能函数:轴卡初始化与轴卡的运动控制。当需要向上层传递操作结果的时候使用OperationResult 的实例对象。

  3. UI层界面可以获取到轴卡的运动状态,通过定时器读取Zmotion类中的方法,更新到界面控件上。

  4. 在设计的时候,如果考虑后续的复合型,比如操作其他厂家的轴卡设备。那么最好将功能操作提取成父类,实际需要使用的动作解释作为延伸类,UI层选择需要调用设备的协议,那么解释层通过协议来改变需要使用的厂家设备。但是考虑到时间成本的问题,目前就不进行如下的代码重构了。

    OperationResult 代码如下(这里考虑到不能返回结果都生成或者绑定新的对象,所以使用了一个静态方法返回错误失败信息):

        public class OperationResult
        {
            /// <summary>
            /// 是否成功
            /// </summary>
            public bool IsSuccess { get; set; }
    
            /// <summary>
            /// 错误信息
            /// </summary>
            public string ErrorMsg { get; set; }
    
            public static OperationResult CreateSuccessResult()
            {
                return new OperationResult()
                {
                    IsSuccess = true,
                    ErrorMsg = "Success"
                };
            }
    
            public static OperationResult CreateFailResult()
            {
                return new OperationResult()
                {
                    IsSuccess = false,
                    ErrorMsg = "Fail"
                };
            }
    
        }
    

    zMotion代码中的轴卡的通断

            /// <summary>
            /// 初始化板卡
            /// </summary>
            /// <returns></returns>
            public OperationResult InitCard()
            {
                int error = zmcaux.ZAux_OpenEth(this.IPAddress, out Handle);
                InitedOK = true;
                if (error == 0 && Handle != IntPtr.Zero)
                {
                    InitedOK = true;
                    return OperationResult.CreateSuccessResult();
                }
                else
                {
                    InitedOK = false;
                    return OperationResult.CreateFailResult();
                }
            }
    
            /// <summary>
            /// 关闭板卡
            /// </summary>
            /// <returns></returns>
            public OperationResult CloseCard()
            {
    
                if (zmcaux.ZAux_Close(Handle) == 0)
                {
                    Handle = IntPtr.Zero;
                    InitedOK = false;
                    return OperationResult.CreateSuccessResult();
                }
                return OperationResult.CreateFailResult();
            }
    
    

    在 进行运动操作之前先判断当前轴卡是否处于连接状态,如果指定轴没有处于运动状态,那么抛出错误:

    		/// <summary>
            /// 通用运动验证
            /// </summary>
            /// <param name="axis">轴号</param>
            /// <returns>操作结果</returns>
            private OperationResult CommonMotionValidate(short axis)
            {
                OperationResult result = CommonInitedValidate();
                //判断是否已经初始化
                if (!result.IsSuccess) return result;
    
                //判断是否正在运行
                if (IsMoving(axis))
                {
                    result.IsSuccess = false;
                    result.ErrorMsg = "轴正在运行";
                    return result;
                }
                return OperationResult.CreateSuccessResult();
            }
    
    
            /// <summary>
            /// 通用初始化验证
            /// </summary>
            /// <returns></returns>
            private OperationResult CommonInitedValidate()
            {
                OperationResult result = new OperationResult();
                //判断是否已经初始化
                if (!InitedOK)
                {
                    result.IsSuccess = false;
                    result.ErrorMsg = "控制器未初始化";
                    return result;
                }
                return OperationResult.CreateSuccessResult();
            }
    
            /// <summary>
            /// 判断某个轴是否正在运行
            /// </summary>
            /// <param name="axis"></param>
            /// <returns></returns>
            public bool IsMoving(short axis)
            {
                OperationResult result = CommonInitedValidate();
                //判断是否已经初始化
                if (!result.IsSuccess) return false;
                //定义运行状态
                int runstate = -1;
                //定义错误码
                int error = 0;
                try
                {
                    //获取轴状态
                    error = zmcaux.ZAux_Direct_GetIfIdle(Handle, axis, ref runstate);
                    //错误码验证
                    ErrorHandler("ZAux_Direct_GetIfIdle", error);
                    return runstate == 0;
                }
                catch (Exception)
                {
                    return true;
                }
            }
    

    操作后返回错误码信息处理,让程序直接抛出异常,这个目前基本够用,但是不够好:

            /// <summary>
            /// 错误处理
            /// </summary>
            /// <param name="command">执行命令</param>
            /// <param name="error">错误码</param>
            private void ErrorHandler(string command, int error)
            {
                string result = string.Empty;
                switch (error)
                {
                    case 0: break;
                    default:
                        result = string.Format("{0}" + "指令执行错误,错误码为{1}", command, error);
                        break;
                }
                if (result.Length > 0)
                {
                    throw new Exception(result);
                }
            }
    

    连续运动函数的编写(思路上:首先确定使用运动验证程序,判断轴的状态。然后若是异常,调用错误处理程序;正常,则写入当前轴号等参数信息并执行运动。如果没有触发异常,那么返回执行成功的信息)

        /// <summary>
        /// 连续运动
        /// </summary>
        /// <param name="axis">轴号</param>
        /// <param name="vel">运行速度</param>
        /// <param name="dir">方向</param>
        /// <param name="velMin">最小速度</param>
        /// <param name="acc">加速度</param>
        /// <param name="dec">减速度</param>
        /// <param name="sramp">S曲线时间</param>
        /// <returns>操作结果</returns>
        public OperationResult VMove(short axis, float vel, bool dir, float velMin, float acc, float dec, float sramp)
        {
            // 判断是否满足运动条件
            var result = CommonMotionValidate(axis);

            if (!result.IsSuccess) return result;

            //创建错误码
            int error = 0;

            try
            {
                //设置轴类型

                /*
                 Atype类型 描述
                0 虚拟轴。
                1 脉冲方向方式的步进或伺服 。
                2 模拟信号控制方式的伺服 。
                3 正交编码器 。
                4 步进+编码器 。
                6 脉冲方向方式的编码器,可用于手轮输入。
                7 脉冲方向方式步进或伺服+EZ信号输入。
                8 ZCAN扩展脉冲方向方式步进或伺服 。
                9 ZCAN扩展正交编码器。
                10 ZCAN扩展脉冲方向方式的编码器。
                 */
                error = zmcaux.ZAux_Direct_SetAtype(Handle, axis, 1);
                ErrorHandler("ZAux_Direct_SetAtype", error);

                //设置脉冲当量
                switch (axis)
                {
                    case 0:
                        error = zmcaux.ZAux_Direct_SetUnits(Handle, axis, unit0);
                        ErrorHandler("ZAux_Direct_SetUnits", error);
                        break;
                    case 1:
                        error = zmcaux.ZAux_Direct_SetUnits(Handle, axis, unit1);
                        ErrorHandler("ZAux_Direct_SetUnits", error);
                        break;
                    case 2:
                        error = zmcaux.ZAux_Direct_SetUnits(Handle, axis, unit2);
                        ErrorHandler("ZAux_Direct_SetUnits", error);
                        break;
                    case 3:
                        error = zmcaux.ZAux_Direct_SetUnits(Handle, axis, unit3);
                        ErrorHandler("ZAux_Direct_SetUnits", error);
                        break;
                    default:
                        break;
                }

                //设置最小速度
                error = zmcaux.ZAux_Direct_SetLspeed(Handle, axis, velMin);
                ErrorHandler("ZAux_Direct_SetLspeed", error);

                //设置运行速度
                error = zmcaux.ZAux_Direct_SetSpeed(Handle, axis, vel);
                ErrorHandler("ZAux_Direct_SetSpeed", error);

                //设置加速度
                error = zmcaux.ZAux_Direct_SetAccel(Handle, axis, acc);
                ErrorHandler("ZAux_Direct_SetAccel", error);

                //设置减速度
                error = zmcaux.ZAux_Direct_SetDecel(Handle, axis, dec);
                ErrorHandler("ZAux_Direct_SetDecel", error);

                //设置S曲线
                error = zmcaux.ZAux_Direct_SetSramp(Handle, axis, sramp);
                ErrorHandler("ZAux_Direct_SetSramp", error);

                //设置方向并运动
                error = zmcaux.ZAux_Direct_Single_Vmove(Handle, axis, dir ? 1 : -1);
                ErrorHandler("ZAux_Direct_Single_Vmove", error);
            }
            catch (Exception ex)
            {
                result.IsSuccess = false;
                result.ErrorMsg = ex.Message;
                return result;
            }
            return OperationResult.CreateSuccessResult();
        }

相同的思路,编写 相对运动

        /// <summary>
        /// 相对运动
        /// </summary>
        /// <param name="axis"></param>
        /// <param name="vel"></param>
        /// <param name="distance"></param>
        /// <param name="velMin"></param>
        /// <param name="acc"></param>
        /// <param name="dec"></param>
        /// <param name="sramp"></param>
        /// <returns></returns>
        public OperationResult MoveRelative(short axis, float vel, float distance, float velMin, float acc, float dec, float sramp)
        {
            // 判断是否满足运动条件
            var result = CommonMotionValidate(axis);

            if (!result.IsSuccess) return result;

            //创建错误码
            int error = 0;

            try
            {
                //设置轴类型

                /*
                 Atype类型 描述
                0 虚拟轴。
                1 脉冲方向方式的步进或伺服 。
                2 模拟信号控制方式的伺服 。
                3 正交编码器 。
                4 步进+编码器 。
                6 脉冲方向方式的编码器,可用于手轮输入。
                7 脉冲方向方式步进或伺服+EZ信号输入。
                8 ZCAN扩展脉冲方向方式步进或伺服 。
                9 ZCAN扩展正交编码器。
                10 ZCAN扩展脉冲方向方式的编码器。
                 */
                error = zmcaux.ZAux_Direct_SetAtype(Handle, axis, 1);
                ErrorHandler("ZAux_Direct_SetAtype", error);

                //设置脉冲当量
                switch (axis)
                {
                    case 0:
                        error = zmcaux.ZAux_Direct_SetUnits(Handle, axis, unit0);
                        ErrorHandler("ZAux_Direct_SetUnits", error);
                        break;
                    case 1:
                        error = zmcaux.ZAux_Direct_SetUnits(Handle, axis, unit1);
                        ErrorHandler("ZAux_Direct_SetUnits", error);
                        break;
                    case 2:
                        error = zmcaux.ZAux_Direct_SetUnits(Handle, axis, unit2);
                        ErrorHandler("ZAux_Direct_SetUnits", error);
                        break;
                    case 3:
                        error = zmcaux.ZAux_Direct_SetUnits(Handle, axis, unit3);
                        ErrorHandler("ZAux_Direct_SetUnits", error);
                        break;
                    default:
                        break;
                }

                //设置最小速度
                error = zmcaux.ZAux_Direct_SetLspeed(Handle, axis, velMin);
                ErrorHandler("ZAux_Direct_SetLspeed", error);

                //设置运行速度
                error = zmcaux.ZAux_Direct_SetSpeed(Handle, axis, vel);
                ErrorHandler("ZAux_Direct_SetSpeed", error);

                //设置加速度
                error = zmcaux.ZAux_Direct_SetAccel(Handle, axis, acc);
                ErrorHandler("ZAux_Direct_SetAccel", error);

                //设置减速度
                error = zmcaux.ZAux_Direct_SetDecel(Handle, axis, dec);
                ErrorHandler("ZAux_Direct_SetDecel", error);

                //设置S曲线
                error = zmcaux.ZAux_Direct_SetSramp(Handle, axis, sramp);
                ErrorHandler("ZAux_Direct_SetSramp", error);

                //设置方向并运动
                error = zmcaux.ZAux_Direct_Single_Move(Handle, axis, distance);
                ErrorHandler("ZAux_Direct_Single_Move", error);
            }
            catch (Exception ex)
            {
                result.IsSuccess = false;
                result.ErrorMsg = ex.Message;
                return result;
            }
            return OperationResult.CreateSuccessResult();
        }

绝对运动,编写思路

我自己就是查询手册,自己改写。看到函数手册。文末会给到百度云连接。

第一步:在手册中全文搜索绝对运动

image-20230308232038505

第二步:找到绝对运动函数。

image-20230308232113197

第三步:用搜索到的相关函数进行全文搜索

image-20230308232157624

找到一个带有代码示例的程序

image-20230308232255583

第四步:分析示例程序里面下达的配置命令。

image-20230308232448162

image-20230308232455818

发现所有代码中就只有最后的执行代码不同,那么就按照相对运动的代码进行改写。所以代码如下:

		/// <summary>
        /// 绝对运动
        /// </summary>
        /// <param name="axis"></param>
        /// <param name="vel"></param>
        /// <param name="pos"></param>
        /// <param name="velMin"></param>
        /// <param name="acc"></param>
        /// <param name="dec"></param>
        /// <param name="sramp"></param>
        /// <returns></returns>
        public OperationResult MoveAbs(short axis, float vel, float pos, float velMin, float acc, float dec, float sramp)
        {
            // 判断是否满足运动条件
            var result = CommonMotionValidate(axis);

            if (!result.IsSuccess) return result;

            //创建错误码
            int error = 0;

            try
            {
                //设置轴类型

                /*
                 Atype类型 描述
                0 虚拟轴。
                1 脉冲方向方式的步进或伺服 。
                2 模拟信号控制方式的伺服 。
                3 正交编码器 。
                4 步进+编码器 。
                6 脉冲方向方式的编码器,可用于手轮输入。
                7 脉冲方向方式步进或伺服+EZ信号输入。
                8 ZCAN扩展脉冲方向方式步进或伺服 。
                9 ZCAN扩展正交编码器。
                10 ZCAN扩展脉冲方向方式的编码器。
                 */
                error = zmcaux.ZAux_Direct_SetAtype(Handle, axis, 1);
                ErrorHandler("ZAux_Direct_SetAtype", error);

                //设置脉冲当量
                switch (axis)
                {
                    case 0:
                        error = zmcaux.ZAux_Direct_SetUnits(Handle, axis, unit0);
                        ErrorHandler("ZAux_Direct_SetUnits", error);
                        break;
                    case 1:
                        error = zmcaux.ZAux_Direct_SetUnits(Handle, axis, unit1);
                        ErrorHandler("ZAux_Direct_SetUnits", error);
                        break;
                    case 2:
                        error = zmcaux.ZAux_Direct_SetUnits(Handle, axis, unit2);
                        ErrorHandler("ZAux_Direct_SetUnits", error);
                        break;
                    case 3:
                        error = zmcaux.ZAux_Direct_SetUnits(Handle, axis, unit3);
                        ErrorHandler("ZAux_Direct_SetUnits", error);
                        break;
                    default:
                        break;
                }

                //设置最小速度
                error = zmcaux.ZAux_Direct_SetLspeed(Handle, axis, velMin);
                ErrorHandler("ZAux_Direct_SetLspeed", error);

                //设置运行速度
                error = zmcaux.ZAux_Direct_SetSpeed(Handle, axis, vel);
                ErrorHandler("ZAux_Direct_SetSpeed", error);

                //设置加速度
                error = zmcaux.ZAux_Direct_SetAccel(Handle, axis, acc);
                ErrorHandler("ZAux_Direct_SetAccel", error);

                //设置减速度
                error = zmcaux.ZAux_Direct_SetDecel(Handle, axis, dec);
                ErrorHandler("ZAux_Direct_SetDecel", error);

                //设置S曲线
                error = zmcaux.ZAux_Direct_SetSramp(Handle, axis, sramp);
                ErrorHandler("ZAux_Direct_SetSramp", error);

                //设置方向并运动
                error = zmcaux.ZAux_Direct_Single_MoveAbs(Handle, axis, pos);
                ErrorHandler("ZAux_Direct_Single_MoveAbs", error);
            }
            catch (Exception ex)
            {
                result.IsSuccess = false;
                result.ErrorMsg = ex.Message;
                return result;
            }
            return OperationResult.CreateSuccessResult();

相同写法的简单代码:

  1. 获取轴速度、轴实时位置

      public float GetVel(short axis)
            {
                //判断是否满足初始化条件
                var result = CommonInitedValidate();
    
                if (!result.IsSuccess) return 0.0f;
    
                //定义速度
                float vel = 0.0f;
    
                //定义错误码
                int error = 0;
    
                try
                {
                    error = zmcaux.ZAux_Direct_GetVpSpeed(Handle, axis, ref vel);
    
                    ErrorHandler("ZAux_Direct_GetVpSpeed", error);
    
                    return vel;
    
                }
                catch (Exception)
                {
                    return 0.0f;
                }
            }
            
    
            /// <summary>
            /// 获取实时位置
            /// </summary>
            /// <param name="axis"></param>
            /// <returns></returns>
            public float GetPos(short axis)
            {
    
                //判断是否满足初始化条件
                var result = CommonInitedValidate();
    
                if (!result.IsSuccess) return 0.0f;
    
                //定义位置
                float pos = 0.0f;
    
                //定义错误码
                int error = 0;
    
                try
                {
    
                    error = zmcaux.ZAux_Direct_GetMpos(Handle, axis, ref pos);
    
                    ErrorHandler("ZAux_Direct_GetMpos", error);
    
                    return pos;
    
                }
                catch (Exception)
                {
                    return 0.0f;
                }
            }
    

    2.所有轴停止和轴归零

     /// <summary>
            /// 停止所有轴
            /// </summary>
            /// <returns></returns>
            public OperationResult StopAllAxis()
            {
                var result = CommonInitedValidate();
    
                if (!result.IsSuccess) return result;
    
                //错误码
                int error = 0;
    
                try
                {
                    /*         
                        0取消当前运动 。
                        1取消缓冲的运动 。
                        2取消当前运动和缓冲运动 。
                        3 立即停止。
    
                     */
    
                    error = zmcaux.ZAux_Direct_CancelAxisList(Handle, 4, new int[] { 0, 1, 2, 3 }, 3);
                    ErrorHandler("ZAux_Direct_CancelAxisList", error);
                }
                catch (Exception ex)
                {
                    result.IsSuccess = false;
                    result.ErrorMsg = ex.Message;
                    return result;
                }
                return OperationResult.CreateSuccessResult();
    
            }
    

    回零模式在书中的案例讲解如下:

    image-20230309002237795

    再看书中的代码案例:

    image-20230308234230448

    image-20230308234158824

    先说一下,这种模式需要运行前获取当前滑块在原点的左右位置,然后反向靠近原点(这就需要做出一定的判断)。所以我们换一种思路:

    1. 强制向着负相位运行距离(大于正负限位的距离),那么滑块必然运行到负限位的地方,然后停止下来。

    2. (剩下是模式4的方法)到达负限位的地方以后,向着正限位的距离移动(稍微大于一点负限位距离原点的距离)。

    3. 然后向着负相位反向运行找原点,遇到原点以后停下。

      个人感觉这种方法有点不对,后续会再重新思考

 		/// <summary>
        /// 通用回零操作
        /// </summary>
        /// <param name="axis">轴号</param>
        /// <param name="vel">速度</param>
        /// <param name="creep">爬行速度</param>
        /// <param name="homeio">HomeIO</param>
        /// <param name="distance">原限距离</param>
        /// <param name="velMin">最小速度</param>
        /// <param name="acc">加速度</param>
        /// <param name="dec">减速度</param>
        /// <param name="sramp">s曲线时间</param>
        /// <param name="homemode">回零模式</param>
        /// <returns>操作结果</returns>
        public OperationResult ZeroAxis(short axis, float vel, float creep, int homeio, float distance, float velMin, float acc, float dec, float sramp, int homemode = 4)
        {
            // 判断是否满足运动条件
            var result = CommonMotionValidate(axis);

            if (!result.IsSuccess) return result;

            //先往原点方向一直走,直到到达限位
            result = MoveRelative(axis, vel, -500, velMin, acc, dec, sramp);
            if (!result.IsSuccess) return result;

            //等待停止
            result = WaitStop(axis);
            if (!result.IsSuccess) return result;

            //位置
            result = ZeroPos(axis);
            if (!result.IsSuccess) return result;

            //往反向走一段距离,超过原点和限位的距离
            result = MoveRelative(axis, vel, distance, velMin, acc, dec, sramp);
            if (!result.IsSuccess) return result;

            //等待停止
            result = WaitStop(axis);
            if (!result.IsSuccess) return result;

            //直接回零
            result = DirectZeroAxis(axis, vel, creep, homeio, velMin, acc, dec, homemode);
            if (!result.IsSuccess) return result;

            //等待回零停止
            result = WaitHomeStop(axis);
            if (!result.IsSuccess) return result;

            //返回成功
            return OperationResult.CreateSuccessResult();

        }
posted @ 2023-03-09 01:25  聆听微风  阅读(1013)  评论(1编辑  收藏  举报