课程设计 - 运动控制卡(云服务器)

课程设计 - 运动控制卡(云服务器)

2021.06.18,今天课设答辩,昨晚肝到凌晨1:48左右。现在越来越对晚睡没什么概念了,特别是当熄灯权完全掌握在自己手上之后。

  • 本笔记是对我的课设——《运动控制卡》 的总结。包含制作流程、教程、思路等。

在这里插入图片描述

一、任务要求

  • 课设要求使用单片机制作运动控制卡,需要使用网线(以太网)接口通讯,实现功能是控制多轴步进电机,实现加减速走直线、弧线(含直线插补、圆弧插补)。同时需要预留限位开关等输入。

  • 而我最后完成的有:网口通讯,而且侧重是联网,老师原本初意是想向工业用法看齐;控制多轴步进电机,实现加减速直线,预留限位输入开关。而插补是一点都没做。

  • 其实运动控制的重点应该是插补计算,但我把重心放在了单片机联网通讯方面,还特意选了没用过的esp32作为主控芯片。所以如果专门想看运动控制、插补知识的人可以略过了,我这个设计并未用到插补知识,运动控制也是简单的恒定加减速。

  • 我会尽可能的写详细思路。如果你是毫无头绪的小白,应该能帮到你。祝你阅读愉快。

在这里插入图片描述

二、准备了解

1. 主控选择

  • 没有特别要求的同学其实可以选择传统的c51和stm32,但是c51可能单片机外设资源会不够,最近stm32又涨价疯狂(2021年6月18日)。(如果你不是和我一样选esp32,那下面关于单片机的部分都可以跳过了)我纯粹是因为想学一下没用过的esp芯片,经常在小破站看到一些人用esp芯片做小发明(小玩意)。

  • 当我深入了解esp后,发现原来官方示例工程里已经自带了FreeRTOS系统,API库直接支持以太网协议,调用函数就可以用了。所以刚好切合了课设的题目,使用以太网通讯。另一个位同课题的同学则是用stm32F4制作,为了使用以太网接口,还移植了正点原子的例程。所以,如果题目要求中有联网/通讯等要求的话,还是推荐esp32的,毕竟esp32本身就专注无线开发方面(大概)。

在这里插入图片描述

2. 步进电机

  • 之前只接触过直流小电机,对步进电机完全没有概念,不知道怎么控制,还以为这种高电压(相对于直流的5v)电机使用时有什么特殊要求,后来查阅资料后才没那么“ 迷茫害怕 ”。在控制方面,(习惯上)直流电机的变速为保持频率改变占空比,而步进电机的变速为保持占空比改变频率。改变占空比也可以令步进电机转得更加快,但是那不符合控制理念,正常还是应该选择改变频率。

  • 因为步进电机是根据脉冲个数转动的,即收到一个脉冲就转一个角度。某种程度上,粗略(能有0.01以上的精度,对普通人来说应该算精准了)的运动是可以连编码器都省去了,做到在一定行程内做点对点的来回运动。如果是直流电机,就一定需要编码器我才知道它转动了多少,直接给pwm得到的转速很不稳定,影响因素太多了。

推荐教程视频: 脉冲输出指令PLSY,讲述怎么使用PLC控制步进电机,加深对于收到一个脉冲就转一个角度的理解。再者,收到是速度就决定了转动的速度。

在这里插入图片描述

  • 注意步进电机上写着1.8°代表的就是一个脉冲转动的角度,所以就可以得到360/1.8 = 200,需要200个脉冲就可以转动一圈,再经过测量得到丝杠的螺距是5mm,所以一圈就是5mm。再换算一下就是1脉冲等于0.025mm。虽然实际会有误差积累,不过用起来感觉还是挺准的。而且每次到限位都算一次误差清零,所以在实际应用中步进电机就比较少带编码器。如果预设充足要精准,往往选择伺服电机。(老师说的)

3. 步进电机驱动器

推荐教程视频:步进驱动器,讲述驱动器怎么接线,对比直流电机,控制线从“双脉冲”变成了“脉冲+方向”,虽然直流电机也可以“脉冲+方向”。

1)使/失能引脚

在这里插入图片描述

  • 除了方向引脚DR脉冲引脚PU,还有使能引脚EN(虽然是上图写的是MF),或应该称为失能引脚。因为我用过两款驱动器都属于:EN引脚给 高电平/有效电平 时,电机不动,给低电平/无效电平时才动。(有/无效电平只是一种相对来说,为了避免混淆还是说高/低电平比较好)

  • 控制器的引脚内部一般是类似三极管的结构,所以原理图以三极管示意代表引脚IO。如果如果单片机(控制器)的工作电压(引脚的输出电压)相比驱动器来说比较小,为了避免对单片机造成影响,一般都会加上一个限流电阻。同样也是为了保护驱动器内部光耦。

  • 一开始我还不懂为什么驱动器的输入会有PU+PU-之分,看了原理图,知道它内部有光耦后就瞬间明白了。为了谨慎,我选择再单片机和驱动器之间再接一个光耦。可以兼容单片机引脚的输入输出情况(当然还是要靠跳冒切换啦

2)细分 与 Pulse/rev

在这里插入图片描述

  • 我一开始以为电机上标定的Pulse/rev就是指细分的意思,后来才知道细分 = Pulse ÷ 多少脉冲/圈(原电机),比如我设定Pulse = 6400,如果之前计算得到我用的步进电机是200脉冲/圈,那么细分就等于32。这么一想这个细分好像只是个定义名词,在我实际应用好像并不需要特别在意。因为Pulse就等于多少脉冲/转了,我编程的时候也只在意这个。

  • 至于电机驱动如何做到改变电机“脉冲/转”,将一份步进量1.8°细分32份的,理论一搜一大把。我只需要知道这个特性是由驱动器决定的,和步进电机无关。还有就是,细分越多误差越大,理解,毕竟白嫖了精度。

4. 运动控制卡

在这里插入图片描述

  • 运动控制卡,就是指进行运动控制的一张卡状电路板。早期发展时运动控制卡是PCIE接口,类似显卡似的插在电脑的PCIE槽。不过最近几年貌似开始逐渐出现其他接口,比如以太网等。比起PCIE更加方便、耐用。毕竟现在不是每个人的电脑都是服务器电脑,有那么多pcie槽。而且pc主机发展越来越个性化,pcie被砍得只剩一两个了。所以以后这种非必须的pcie接口的设备应该会被逐渐替代。

在这里插入图片描述

5. 功能或作用

  • 运动控制,专门指电机的运动控制。不同小车上的轮胎直流电机,是需要精准控制进给量的步进电机。普通人一般能想到的,应该就是工业上的机械手之类、或切割机器需要精准坐标那种,是不是呢?我也不知道……

在这里插入图片描述

  • 我搜索到的直接应用,其实是用来控制线性模组,即直线导轨+步进电机。比如:各种机床平台、3D打印、雕刻机、写字机等,只有空间直线运动,没有空间旋转运动的。可能是我搜索方式不对。

写字机:你还在买一千多的写字机?三百多块DIY一个又快又稳的它不香吗?小白也能自己做!

雕刻机:看咸鱼佬如何自制一台数控雕刻机

3D打印机:800块做一台又大又快又稳的CoreXY结构3D打印机

6. 开关电源 与 网线接口

在这里插入图片描述

  • 普通的24伏直流开关电源,给驱动器供电。原本设想也给主控板供电的。我想着直接用一个AMS2940五伏稳压芯片来接开关电源。直接把稳压当降压用,因为看手册上写着这款芯片最高可输入60伏电压的。我测试时它也确实可以稳出5伏电压。但,我忽略了发热问题……如果AMS2940稳压直连开关电源,在一分钟内就发热到手都不能摸的程度了。

在这里插入图片描述

三、总体设计

1. 主控设计

  • 首先是重要的光电隔离部分,为了保护主控,所有输入输出信号口我都预留了光耦的接线。这些光耦没有直接连接单片机,为了可自主选择使用 跳冒 + 排针 的形式。

在这里插入图片描述

  • 然后剩下的就是电源部分的稳压、交互部分的按键和屏幕、还有esp32模块以太网接口模块

在这里插入图片描述

  • 原理图比较简单,本身就没多少内容,输入有:网口、按键、限位开关(预留,跳冒连接)、ADC(预留);输出有:网口、PWM、屏幕(预留)、蓝牙(预留)。还有稳压和光耦。最后实物如下图。

在这里插入图片描述

  • 接线说明:,右上角的黑色线是共地负极,因为主控板采取额外供电而不是和驱动共用开关电源。然后下面2个黑红组合分别是两电机的脉冲PU+方向DR组合。上面2个黄色则是电机的失能EN。(下图中,中间是电源共地,左右两个是电机接口。安利一下,这种接口很方便,省去扭螺丝插座的麻烦。)

在这里插入图片描述

在这里插入图片描述

2. 注意事项

  1. 我多加个一个USB2.0接口只是为了方便供电,没有信号功能。
  2. 引脚IO12属于Strapping 管脚中的MTDI引脚,该引脚在上电时不能是高电平,一定要拉低。不然下载程序会一直不成功,如果不下载程序好像还是正常用(我测试的结果)?在手册:ESP32 技术规格书 (PDF)中的2.4 Strapping 管脚有详细原因描述。我之前不知道所以画板时用作了按键输入,外接了上拉电阻……回来拆了就又可以正常下载程序了,如果不拆,而是选择每次下载程序都按下IO12按键使引脚接地下拉,应该也可以……
  3. IO32IO33只能作输入引脚,我一开始不知道就把它们预留作为PWM输出引脚了。回来在调试程序初始化引脚时,通过监视器看到程序报错了,提示该引脚不能作为输出,只能作为输入……监视器牛批……
  4. 关于屏幕的使用,同样是在esp32笔记中:ESP32 单片机学习笔记 - 02 - 软件IIC&硬件SPI,不过我没用到,因为初始化不成功……大概率就是IO32IO33的锅。而且因为这款esp32引出引脚令人抓急的少,所以屏幕其实都被拿去做电机输出了,按键也只上下3个可用。

3. 线性模组

  • 大体如下图,大伙先看看。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 这套线性模组是有xy轴方向可移动,每轴上还配备3个限位开关传感器。我一开始以为这传感器是数字式的,即只会输出高低电平。后来听另一位调试的同学说是模拟式的,应该是运动平台挡住多少就会输出多少电压,电压在一个范围内变化。因为我的主控设计时没有考虑到比较器电路,直连单片机又怕烧坏,所以就没用到上面的限位开关。

在这里插入图片描述

  • 比较特别的是这套线性模组居然是带编码器的,不过它的编码器是直接接到了驱动上,实现内闭环?(也不知道这样用对不对,是另一位同学接好的,我也没改。)还有一点,编码器的接口居然是用VGI接口,但是实际上也还是只用到了几根线而已。这接口就给我年代感十足。
  • 有些驱动器上会要选择电机的启动电流,之前用过一款黑色的驱动器上是有的。上图中蓝色的这款没有。注意,如果有但没设置好,可能会驱动不了电机,找半天找不到问题,为什么电机不转之类的问题……
  • 线接好,无误后,控制驱动器的信号引脚,就可以动啦~~简单粗暴。
  • 注意,(我这套线性模组和驱动器的搭配下)如果工作平台移动过头,撞击边缘,驱动器会自动停止输出,然后亮红灯(进入保护)。退出保护的方式就是要将驱动器的失能引脚EN拉高(拉高是电机不动,拉低是电机动)。其实就是:电机运动时进入保护,关闭电机运动后就退出保护了。

4. 程序设计

  1. 累计电机的运动距离,相当于把起点作为原点建立坐标系,这样我就能实现指哪走哪的目标了。累计距离 = 角速度/每秒 * 中断时间,角速度的数据是我换算过的,所以最后计算得到的累计距离就是电机转动的圈数。我这套平台的单轴运动行程大概是20多转就到顶了。
  2. 计算下一次期望坐标,如果是走一定轨迹的话,就需要根据当前坐标,代入轨迹的运动方程中,计算下一次期望坐标(目标点坐标)。
  3. 判断运动状态,如果当前坐标距离目标坐标很近,又或是已经达到或超过了,就停止运动。反之就是判断需要正转还是反转。
  4. 判断下一次角速度输出,这是一个修bug的操作,是为了不要让电动机在目标点附近震动。但是这个写法还是很不理想(不起作用),应该使用插补来解决。(?)
  5. 最后电机输出
/*==============================================================
 * @brief
 *          void.点对点运动,无加减速
 * @Note
 *          void.指定期望运动坐标和电机。
 *==============================================================*/
void Motor_coordinates(void)
{
    /* 保存运动状态 */
    int motor_run_o= motor_stop;

    /* 累加圈数/累计脉冲,绝对坐标,不会清零 */
    M.actual[0].c += M.actual[0].w * TIMER_PERIOD_MS / 1000;
    M.actual[1].c += M.actual[1].w * TIMER_PERIOD_MS / 1000;
    M.actual[0].c = FLOAT_DECIMAL(M.actual[0].c, 1000.0); // 保留3位小数
    M.actual[1].c = FLOAT_DECIMAL(M.actual[1].c, 1000.0);

    /* 计算下一次期望位置 */
    if (M.mods_cf == mods_b) // 只有在该标志位处于模式b时才计算
        Circle_coordinates();

    /* 判断运动状态 */
    for (int motor_n = 0; motor_n < 2; motor_n++)
    {
        /* 加个限定差大小条件,为了避免单片机数据精度问题 */
        if (M.expect[motor_n].c > M.actual[motor_n].c && 
        fabs(M.expect[motor_n].c - M.actual[motor_n].c) > 0.001) // 假设 角速度 不会小于 0.1,导致角度小于0.0001.
            motor_run_o = motor_run_H;
        else if (M.expect[motor_n].c < M.actual[motor_n].c && 
        fabs(M.expect[motor_n].c - M.actual[motor_n].c) > 0.001)
            motor_run_o = motor_run_L;
        else
            motor_run_o = motor_stop;
        
        /* 判断下一次角速度输出 */
        if (motor_run_o == motor_run_H)
        {
            M.actual[motor_n].w = 1; //添加判断,剩下的距离是否能满足速度要求,不能就按着距离算速度
            if ((M.expect[motor_n].c - M.actual[motor_n].c) * 1000 / TIMER_PERIOD_MS < M.actual[motor_n].w )
            {
                M.actual[motor_n].w  = (M.expect[motor_n].c - M.actual[motor_n].c) * 1000 / TIMER_PERIOD_MS;
            }
        }
        else if (motor_run_o == motor_run_L)
        {
            M.actual[motor_n].w = -1;
            if ((M.expect[motor_n].c - M.actual[motor_n].c) * 1000 / TIMER_PERIOD_MS > M.actual[motor_n].w )
            {
                M.actual[motor_n].w  = (M.expect[motor_n].c - M.actual[motor_n].c) * 1000 / TIMER_PERIOD_MS;
            }
        }
        else if (motor_run_o == motor_stop)
            M.actual[motor_n].w = 0;
    }

    /* 两轴电机输出:上下 */
    mcpwm_set_hz_a(M.actual[0].w);
    mcpwm_set_hz_b(M.actual[1].w);
}
  • 其他程序内容不那么重要,就不细说了,都是写简单的单片机功能实现。我会把工程分享到github上,感兴趣的自己看看吧。(不建议)

分享个我找了好久的bug:使用浮点数时,一定一定不能判断是否等于,就算截取小数位也没用。因小数点的精度已经不满足。只能判断范围大小,如果要用判断就必须判断整数。避免精度问题。

四、总结

  • 两个星期的课设,前一个半星期都学esp32和搭建云平台,最后2天不到的时间才开始写电机的运动控制,所以我就没实现插补。之后有机会再了解吧,现在比较对无线、联网方面感兴趣一点。
  • Github项目链接:lovely-him/Motion_control_card_server,包含pcb工程、云平台工程、esp32代码,全部资料。还有些其他笔记分布在我的专栏里。有兴趣的自行查找,感谢阅读。
  • 6
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 21
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值