5iMX宗旨:分享遥控模型兴趣爱好

5iMX.com 我爱模型 玩家论坛 ——专业遥控模型和无人机玩家论坛(玩模型就上我爱模型,创始于2003年)
查看: 8977|回复: 61
打印 上一主题 下一主题

单片机 实现6轴联动控制 ,最高输出频率200K 可能吗

[复制链接]
跳转到指定楼层
楼主
发表于 2012-4-6 00:37 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
:em04:
论坛为什么只能上传 195k的图片呀

[ 本帖最后由 scottmaxwell 于 2012-4-6 00:51 编辑 ]

无标题1.jpg (89.25 KB, 下载次数: 49)

无标题1.jpg

欢迎继续阅读楼主其他信息

62
发表于 2012-4-9 22:47 | 只看该作者
很好的帖子,如果能继续讨论就更好了。
61
发表于 2012-4-8 16:15 | 只看该作者
我就跟贴到这里了,坛子里懂单片机的大侠很多,大家自有判断,至于LZ的方向还是你自己决定
60
发表于 2012-4-8 16:10 | 只看该作者
从实用性角度考虑,LZ的PIC32方案只适合伺服电机,原因如下:

1 大部分DIY者使用步进电机,配合5MM滚珠丝杆和16细分驱动,200K的频率会运行在F18750,一般DIY的机器在这种速度下根本不能保持机械稳定性,由于惯性会严重抖动和摇晃
2 如果机器刚性很高,可以稳定运行F18750,这时每分钟前进18750mm,如果刀具也运行在1.8万转,那么对于双刃铣刀,每齿进给量是0.5mm,这么大的切削量,除了切泡沫塑料外,其他材料无一例外都会断刀。所以主轴至少要10万转级别的,这样的主轴什么价格?如果雕刻PCB,刀尖更小,10万转都不行
3 如果10万转主轴你刚好手头有一个,那么10万转运行带来的刀尖过热问题就出现了,要外加强制冷却
4 高速加工在转弯位置非常容易断刀,这个也很难处理

综合以上几点,LZ的200K对大部分DIY者没有实际意义,这是一个典型的木桶理论,电子部分拔得这么高,机械和材料都成为短板,结果电子部分的高性能无法发挥。倒不如用低成本的8位单片机做一个最高支持F3000到F5000的,成本35圆的6轴USB运动控制卡

用8位单片机的优点一个是成本低,另一个优点是这些芯片有DIP直插封装,项目开源之后,稍微懂点电子的人,都可以用万能板自己做一个
59
发表于 2012-4-8 15:31 | 只看该作者
这个是关键的插补部分,同样的原因删掉了部分,不过已经可以看出算法原理,这里巧妙的结合了32位运算和8位运算,最大限度发挥CPU的性能,你看懂了么?

1.JPG (58.75 KB, 下载次数: 11)

1.JPG
58
发表于 2012-4-8 15:27 | 只看该作者
这是6轴的32位变量和插补数据,算法经过改造,变量的含义和你的程序不一样的,为了避免部分人拿来即用,我把有关质数和奇偶数的处理删掉了,少了几个变量

1.JPG (55.73 KB, 下载次数: 8)

1.JPG
57
发表于 2012-4-8 15:23 | 只看该作者
这是中断程序

1.JPG (56.6 KB, 下载次数: 8)

1.JPG
56
发表于 2012-4-8 15:21 | 只看该作者
下面是部分程序的截图,用的是你看不上的Arduino,这是程序开始部分:

1.JPG (58.19 KB, 下载次数: 11)

1.JPG
55
发表于 2012-4-8 15:14 | 只看该作者
你觉得我象是在瞎扯吗?你的相关个人信息,纳米机器人,我大概了解,我的信息,你一无所知,我搞Z80的时候,你还没进幼儿园, 所以不要太自以为是,这样对你没任何好处。以你这种性格和悟性,拜我做老师,我都不会收的。

好了,无关的不说,回到正题

这个程序刚经过优化,测试可以稳定输出66.7K脉冲,全32位6轴插补,当然它只管输出脉冲,用的16M主频的8位AVR M1280,搬到M8上没有任何问题,计算能力一样的

上面说到的这些不必要的计算,如何优化算法才能去掉呢?先用一个生活中的例子来说明一下。

假设有一个不透明的盒子,里面有100颗黄豆和30颗黑豆,现在的任务是要把30颗黑豆挑出来,有2种做法:

做法1:在盒子上开一个小孔,每次出一颗豆子,如果是黑豆则拿走,全部豆子都处理完后,30棵黑豆就挑出来了。这个方法要对每颗豆子做判断,速度很慢

做法2:把盒子里的豆子全部倒出来,把黑豆拿走,剩下的自然全部是黄豆。这个方法需要的判断很少,速度很快

你的程序其实就是第1种方法,因为每次中断的计算完全独立,无法利用前后脉冲的数据相关性,在每次中断时都需要判断各轴当前的位置,根据判断结果决定是否输出脉冲,计算量很大。如果不改变方法,确实如你所说,这段程序“没有一句是可以省略的”,如果按下面的说明改写程序,你会发现你发上来的整个程序"都是可以省略的

我改进后的算法是第2种方法,在主程序中进行插补计算,通俗一点说,就是先把上面的画面全部填为白色,然后在一个循环中根据每轴的插补数据把需要脉冲的位置填为蓝色,这个处理过程判断很少,没有脉冲的位置自然会保留成白色,脉冲间隔越大,蓝色位置越少,循环次数越少,计算越快。把这个脉冲图准备好后,在中断程序里只需要按顺序输出就可以了,中断程序变得很小。剩下的问题就是,内存有限,要把全部脉冲都准备好显然不可能,所以要准备2个缓冲区,我现在是用了2个100字节的缓冲区,一个用于中断输出,同时在另一个里准备数据,这样搭配好后,程序就可以工作了。实际运行结果是这样的:如果关闭中断,只做数据准备,可以达到156K的6轴脉冲填充率,如果打开中断输出脉冲,可以达到66.7K的脉冲填充率和输出速度,插补使用32位整数运算,和你的一样。圆弧插补不支持,需要在PC端用驱动程序处理,这样就把PC机的计算能力和单片机的实时能力结合在一起

[ 本帖最后由 3dbuild 于 2012-4-8 19:13 编辑 ]
54
 楼主| 发表于 2012-4-8 14:28 | 只看该作者
原帖由 3dbuild 于 2012-4-8 12:37 发表
从图上可以看出,有大量的空白的无脉冲区域,你的中断程序在每个白色的小方格里,都要运行一段类似这样的操作

          Sum_X_ASIX +=X_ASIX;
        if(Sum_X_ASIX >= ASIX_Maxpulses)
        {
          ...

你瞎扯什么呀,看懂什么意思了吗,这里没有一句是可以省略的,唯一方法就使使用嵌入式汇编优化,我现在最新写的代码就是嵌入式代码.
如果你可以使用你的双AVR 写出 25KHZ 4 轴  插补的,我拜你做老师.
至少要可以 运行 G1 X1000 Y1000 Z1000 A 1000  以内任意插补  还有平面内的圆插补 支持输出脉冲宽度 1us -10us
我又要去忙新项目了,基本上不会上论坛了,等你的好消息.
  最后提醒你的 方案中 i2c通讯是不合适的,建议你使用SPI

[ 本帖最后由 scottmaxwell 于 2012-4-8 15:03 编辑 ]
53
 楼主| 发表于 2012-4-8 14:24 | 只看该作者
原帖由 3dbuild 于 2012-4-7 23:14 发表
从你发上来这个编译结果看,你没有把Step_out_mask设置为寄存器变量,所以改为Step_out_mask ^= Step_out_mask后,反而需要更多的内存读写,你现在的程序里,Step_out_mask只是一个存放在内存里的普通的自动变量,对 ...


你以为编译器会按你想像的给你编译吗,这样的话,那没有人会继续使用汇编了,不要纸上谈兵了.编译器的优化是有限制的. 还有你不用教我什么是寄存器变量,内存变量,这是做代码优化基本指示高密集算法是还可以使用 定点DSP,取两个内存变量,在相乘,最后累加.操作偏移地址,一条指令完成
还有看的的回复,你就根本没有学过pic32或者没有深入学习,连PIC32高速指令缓存怎么使用你都不知道

[ 本帖最后由 scottmaxwell 于 2012-4-8 14:42 编辑 ]
52
发表于 2012-4-8 12:37 | 只看该作者
从图上可以看出,有大量的空白的无脉冲区域,你的中断程序在每个白色的小方格里,都要运行一段类似这样的操作

          Sum_X_ASIX +=X_ASIX;
        if(Sum_X_ASIX >= ASIX_Maxpulses)
        {
           Sum_X_ASIX -= ASIX_Maxpulses;
           AllSteps --;   
           X_POS += x_dir;
           Step_out_mask = 0x02;
        }

对于PC机来说,速度够快,这么做没什么问题,但是单片机计算能力有限,这样做就太浪费了,结果就是中断程序运行时间太长,所以这里必须改进,避免这些不必要的计算
51
发表于 2012-4-8 12:32 | 只看该作者
画个图来说明一下,这个是根据你贴上来的一个脉冲图画的,横向是时钟中断次数,纵向是6轴对应的脉冲,有脉冲的填为蓝色,其他是空白,结果是这样的

1.JPG (32.01 KB, 下载次数: 9)

1.JPG
50
发表于 2012-4-7 23:59 | 只看该作者
如此牛贴 来膜拜下各位大牛
49
发表于 2012-4-7 23:14 | 只看该作者
从你发上来这个编译结果看,你没有把Step_out_mask设置为寄存器变量,所以改为Step_out_mask ^= Step_out_mask后,反而需要更多的内存读写,你现在的程序里,Step_out_mask只是一个存放在内存里的普通的自动变量,对这个变量操作是很慢的,即使你原来的程序只有1行编译代码。如果Step_out_mask是一个寄存器变量,例如V1,那么可以用一句XOR V1,V1把它清零,不需要操作内存,速度很快,我没搞过PIC32,不清楚它有没有这样的指令,不过在很古老的Z80里,都有XOR AL,AL这样的指令,现在这么“先进”的32位PIC32,如果没有类似的指令会让人很奇怪。

建议1:如果你找到了这样的指令,而不知道怎么把Step_out_mask设置为寄存器变量,也没关系,你可以用嵌入式汇编,直接把V1用了就可以

建议2:PIC32有很强的位运算指令,你的程序里有大量的Step_out_mask |= 0x20之类的语句,你最好检查一下编译器有没有把它编译成位运算指令,比|=指令更快

建议3:PIC32对程序和数据都有高速缓存,这个是PIC32架构的优点,你的程序里有大量的Sum_n_ASIX变量,你有考虑过利用这个高速缓存来提高内存操作速度了吗?怎么让CPU在处理Sum_X_ASIX时,已经把Sum_Y_ASIX预读进了高速缓存?怎么实现的?还是没有考虑过?

从你的程序看,可能你对程序优化只有初步的认识,你太依赖C编译器的优化,只会用inline。程序优化是一门很有意思的学问,包括算法级优化和CPU架构级优化,以上3个只是CPU架构级优化,通常是放在最后做的,效果也很有限,最有效果的是算法级优化,通过分析算法的瓶颈,找到改进的方法。

按你的说法,你这个6轴插补程序,在80M的32位PIC32上能输出200K已经觉得很不错了,不过我要告诉你,通过算法级优化,我在16M的8位AVR M168上,在最差的条件下,即每轴每个时钟中断都发出一个脉冲的条件下,测试程序实现了6轴58.8K的输出,换成20M的晶振就可以达到73.5K,而且每个轴每个脉冲的时间误差都不超过一个时钟中断的时间,也就是1/58.8K秒。实际上大部分G代码都不会有这种情况,各轴在每个时钟中断时,大部分时候只有1到2个轴经常有脉冲,其他的很多都是空白,你的程序就是把CPU浪费在这些空白的重复判断和处理上

迟点发个图给你说明一下,你的算法完全要改写,改写后不要说200K,以你80M的主频,应该可以飞上天
48
 楼主| 发表于 2012-4-7 11:51 | 只看该作者
原帖由 3dbuild 于 2012-4-7 09:48 发表
高手就不必了


我现在用的是比较旧的G3硬件的RepRap,使用3977驱动步进电机,修改固件可以选择细分,最新的硬件已经默认使用8细分,研究程序的话,可以看看它的最新版,5D的插补程序没怎么优化


你贴上来这个 ...


165:                 //6轴插补
166:                 extern inline void __attribute__((always_inline))  Six_ASIX(void)
167:                 {
168:                     //Step_out_mask = 0;  //端口输出掩码
169:                     Step_out_mask ^= Step_out_mask;
9D00046C  9384806C   LBU A0, -32660(GP)
9D000470  9382806C   LBU V0, -32660(GP)
9D000478  00821826   XOR V1, A0, V0
9D00047C  A383806C   SB V1, -32660(GP)
170:                     if(AllSteps>0)

165:                 //6轴插补
166:                 extern inline void __attribute__((always_inline))  Six_ASIX(void)
167:                 {
168:                     Step_out_mask = 0;  //端口输出掩码
9D000470  A380806C   SB ZERO, -32660(GP)
169:                     //Step_out_mask ^= Step_out_mask;
170:                     if(AllSteps>0)
47
发表于 2012-4-7 09:48 | 只看该作者
高手就不必了


我现在用的是比较旧的G3硬件的RepRap,使用3977驱动步进电机,修改固件可以选择细分,最新的硬件已经默认使用8细分,研究程序的话,可以看看它的最新版,5D的插补程序没怎么优化


你贴上来这个2年前的程序,如果真心要请人给你优化,请把哪些变量是32位,哪些是8位写清楚,虽然我知道哪些应该是32位,还有就是你用的是什么单片机,这些变量哪些是寄存器变量类型,这些都没说,这么贴一个只有2行注释的程序出来,好象没有太大意义,所以关于指令集和CPU架构方面的优化就直接PASS掉了,只能在通用的算法层面,给你提点建议


程序里有6个类似的语句:X_POS += x_dir 没理解错的话,这个是单个脉冲输出后,记录当前位置的作用,x_dir的取值只有2种可能:+1 / -1,你现在的写法可以改为X_POS_DELTA ++,就是说在这个速度敏感的程序段,只记录运行了多少步,不管方向,在需要得到X_POS的程序段,用X_POS + X_POS_DELTA*x_dir的方法获得,再优化一下,可以把乘法都省掉。这样一改,少了6次内存读取,或者是释放了6个类似x_dir的寄存器变量(如果x_dir你设置为寄存器变量的话),对C程序优化器有一定帮助


在程序开头的 Step_out_mask = 0;  //端口输出掩码
可以改为 Step_out_mask ^= Step_out_mask,如果Step_out_mask是寄存器变量的话,编译后一般只有1条汇编指令,比你现在取一个立即数又快了一些

[ 本帖最后由 3dbuild 于 2012-4-7 09:50 编辑 ]
46
发表于 2012-4-7 06:53 | 只看该作者
厉害。。。没玩过。。。加油。。。硬件成本是一个方面 。。。研发成本倒是不低。。。。。。

祝早日研发完成。。。好欣赏一下。。。
45
 楼主| 发表于 2012-4-7 02:49 | 只看该作者
原帖由 3dbuild 于 2012-4-6 23:35 发表


从这段话来看,觉得你要么没有对AVR的处理器做深入的了解,要么没理解我的算法。实际上不需要置位和清零寄存器,也不需要读8个IO口的状态,CPU只是输出,不用管它原来是什么状态,原来的状态是通过运算得到。例如 ...

高手 麻烦你在优化一下我的算法,这个是2年前写的6轴插补
这个算法 在 80M  32位单片机上需要 最长耗时 4.75us,这里面有好几个变量是32位的

//6轴插补
extern inline void __attribute__((always_inline))  Six_ASIX(void)
{
    Step_out_mask = 0;  //端口输出掩码
    if(AllSteps>0)
    {
        Sum_X_ASIX +=X_ASIX;
        if(Sum_X_ASIX >= ASIX_Maxpulses)
        {
           Sum_X_ASIX -= ASIX_Maxpulses;
           AllSteps --;   
           X_POS += x_dir;
           Step_out_mask = 0x02;
        }
        Sum_Y_ASIX +=Y_ASIX;
        if(Sum_Y_ASIX >= ASIX_Maxpulses)
        {
           Sum_Y_ASIX -= ASIX_Maxpulses;
           AllSteps --;
           Y_POS += y_dir;
           Step_out_mask |= 0x04;
        }
        
        Sum_Z_ASIX +=Z_ASIX;
        if(Sum_Z_ASIX >= ASIX_Maxpulses)
        {
           Sum_Z_ASIX -= ASIX_Maxpulses;
           AllSteps --;  
           Z_POS +=z_dir;
           Step_out_mask |= 0x08;
        }
        Sum_A_ASIX +=A_ASIX;
        if(Sum_A_ASIX >= ASIX_Maxpulses)
        {
           Sum_A_ASIX -= ASIX_Maxpulses;
           AllSteps --;
           A_POS += a_dir;
           Step_out_mask |= 0x10;
        }
        Sum_B_ASIX +=B_ASIX;
        if(Sum_B_ASIX >= ASIX_Maxpulses)
        {
           Sum_B_ASIX -= ASIX_Maxpulses;
           AllSteps --;
           B_POS +=b_dir;
           Step_out_mask |= 0x20;
        }
        Sum_C_ASIX +=C_ASIX;
        if(Sum_C_ASIX >= ASIX_Maxpulses)
        {
           Sum_C_ASIX -= ASIX_Maxpulses;
           AllSteps --;
           C_POS += c_dir;
           Step_out_mask |= 0x40;
        }
    }
}

[ 本帖最后由 scottmaxwell 于 2012-4-7 02:59 编辑 ]
44
 楼主| 发表于 2012-4-7 00:59 | 只看该作者
原帖由 cjseng 于 2012-4-7 00:46 发表
我在想,6个轴,如果记为X、Y、Z、A、B、C的话,假设为了实现一条曲线,X频率200K,Y轴179K,Z轴127K,A轴121K,B轴89K,C轴77K,那么,请问这时候单片机的定时精度需要多少K?

G91
G1 X200 Y179 Z127 A121 B89  C77
这个不是定时器精度的问题   插补肯定有误差的 因为电机控制是离散的,一个一个脉冲给的  所以画的线 和理想的线  在一个最小步误差之间摆动
事实上你只要可以最高那个轴频率的中断就可以了,通过它来计算 各轴是否要输出脉冲,200KHZ 中断 就是  5us  就是说要在这个时间内完成计算
1 DMIPS/MHz 的单片机  时钟频率为20MHZ  它5us 连续运算能力最高是 执行 5*20条指令,但是如果有条件跳转指令的话,是达不到这个速度的,因为事实上一条指令都需要4个时钟周期,由于4条指令流水线 ,连续工作就是 每个时钟执行一条指令,但是对于没有DSP核的单片机,一条加法 至少需要4条指令,分别从内存取出2个相加的数据  再相加  再保存,有硬件乘法器的,乘法同样是4条指令.运算能力还要看是几位单片机.

[ 本帖最后由 scottmaxwell 于 2012-4-7 01:22 编辑 ]
您需要登录后才可以回帖 登录 | 我要加入

本版积分规则

关闭

【站内推荐】上一条 /1 下一条

快速回复 返回顶部 返回列表