Algorithm 一个巧妙的自制模块实现

Algorithm 一个巧妙的自制模块实现,algorithm,math,modulus,plc,Algorithm,Math,Modulus,Plc,我正在用一些遗留软件(RSLogix 500,不问)编程一个PLC,它本身不支持模数运算,但我需要一个。我没有访问:模、整数除法、局部变量、截断操作(尽管我可以通过舍入来解决)。此外,我可以使用的所有变量都在按数据类型排序的表中列出。最后,它应该适用于浮点小数,例如12345.678 MOD 10000=2345.678 如果我们建立方程: dividend / divisor = integer quotient, remainder 有两个明显的实现 实施1: 执行浮点除法:被除数/除数=

我正在用一些遗留软件(RSLogix 500,不问)编程一个PLC,它本身不支持模数运算,但我需要一个。我没有访问:模、整数除法、局部变量、截断操作(尽管我可以通过舍入来解决)。此外,我可以使用的所有变量都在按数据类型排序的表中列出。最后,它应该适用于浮点小数,例如
12345.678 MOD 10000=2345.678

如果我们建立方程:

dividend / divisor = integer quotient, remainder
有两个明显的实现

实施1: 执行浮点除法:
被除数/除数=十进制商
。然后拼凑一个截断操作,这样就可以找到
整数商
。将其乘以除数,然后找出被除数与除数之间的差值,从而得到余数

我不喜欢这个,因为它涉及一系列不同类型的变量。我不能将变量“传递”给子例程,所以我只需要分配位于多个不同变量表中的一些全局变量,这很难遵循。不幸的是,“难以遵循”的计数,因为它需要足够简单,以便维护人员处理

实施2: 创建一个循环,以便在
被除数>除数时
除数=被除数-除数
。这是非常干净的,但它违反了PLC编程的一个大规则,即永远不要使用循环,因为如果有人无意中修改了索引计数器,您可能会陷入无限循环,机器可能会发疯或出现无法修复的故障。Plus回路对于维护来说很难进行故障排除。另外,我甚至没有循环指令,我必须使用标签和跳转。哎哟


所以我想知道是否有人有比这两者更聪明的数学技巧或模的更聪明的实现。我可以访问+-*/、指数、sqrt、trig函数、log、abs值和/或/或/NOT/XOR。

您要处理多少位?你可以这样做:

if dividend > 32 * divisor  dividend -= 32 * divisor
if dividend > 16 * divisor  dividend -= 16 * divisor
if dividend > 8 * divisor  dividend -= 8 * divisor
if dividend > 4 * divisor  dividend -= 4 * divisor
if dividend > 2 * divisor  dividend -= 2 * divisor
if dividend > 1 * divisor  dividend -= 1 * divisor
quotient = dividend

只要将
红利中的位展开多少次即可。一定要小心那些溢出的倍数。这就像你的#2,只是它需要log(n)而不是n次迭代,所以完全展开是可行的。

这一切似乎完全过于复杂了。您有一个编码器索引,在10000处滚动,对象沿着直线滚动,您在任何给定点跟踪其位置。如果您需要沿直线向前投射停止点或动作点,只需添加所需的英寸数,如果目标结果大于10000,则立即减去10000

或者,或者另外,每次PLC扫描都会得到一个新的编码器值。如果当前值和最后一个值之间的差值为负值,则可以激活工作触点以标记包裹事件,并对该扫描上的任何计算进行适当更正。(**或增加一个辅助计数器,如下所示)

如果不了解实际问题,就很难提出更具体的解决方案,但肯定有更好的解决方案。我认为这里根本不需要MOD。此外,地板上的人会感谢你没有用模糊的巫师的东西填满机器

我引述:

最后,例如,它必须适用于浮点小数 12345.678模10000=2345.678

有一个出色的函数可以实现这一点,它是一个减法。为什么需要比这更复杂呢?如果您的输送线实际长度超过833英尺,则滚动第二个计数器,该计数器在主索引滚动上递增,直到您有足够的距离覆盖所需的地面

例如,如果您需要100000英寸的传送带内存,您可以拥有一个以10英寸滚动的辅助计数器。如上所述,可以很容易地检测到主编码器翻转,并且每次递增辅助计数器。那么,您的工作编码器位置是计数器值加上当前编码器值的10000倍。仅在扩展单元中工作,并以您需要的任何值进行二次计数器翻转,以避免丢失任何零件。问题再次简化为一个简单的减法(如上所述)

例如,我将此技术用于行星齿轮旋转零件保持架。我有一个编码器,每个主旋转旋转一次,而行星齿轮卫星部件(它们自身围绕定子齿轮旋转)需要43次主旋转才能返回到相同的起始方向。使用一个简单的计数器,在主编码器翻转点递增(或递减,取决于方向),可以完全绝对测量零件的位置。在这种情况下,辅助计数器在43处滚动

这将同样适用于线性输送机,唯一的区别是线性输送机可以进行无限距离的输送。然后,问题只需要由线路上最坏情况部分采用的最长线性路径来限制

需要注意的是,我从来没有使用过RSLogix,这里是总体思路(我在这里使用了通用符号,我的语法可能有点错误,但您应该明白这一点)

通过以上操作,您将得到一个值
ENC_EXT
,它基本上将您的编码器从10k英寸转换为100k英寸。我不知道你的传送带是否可以反转,如果可以,你还需要处理倒计时。如果程序的整个剩余部分只使用
ENC_EXT
值,那么您甚至不必担心编码器只能达到10k。现在它变为100k(或任何你想要的),并且可以用减法而不是模来处理环绕

后记:

可编程逻辑控制器是第一个
#include <stdio.h>
#include <limits.h>
#define NBIT (CHAR_BIT * sizeof (unsigned int))

unsigned modulo(unsigned dividend, unsigned divisor)
{
unsigned quotient, bit;

printf("%u / %u:", dividend, divisor);

for (bit = NBIT, quotient=0; bit-- && dividend >= divisor; ) {
        if (dividend < (1ul << bit) * divisor) continue;
        dividend -= (1ul << bit) * divisor;
        quotient += (1ul << bit);
        }
printf("%u, %u\n", quotient, dividend);
return dividend; // the remainder *is* the modulo
}

int main(void)
{
modulo( 13,5);
modulo( 33,11);
return 0;
}
atan(tan(( 12345.678 -5000)*pi/10000))*10000/pi+5000   =   2345.678