官方解读NANOJ自主程序功能的运作机制?

2018-06-16 15:00:38 研蓝自动化

使用NANOJ V2进行编程 - 应用示例LABELPRINTER

第二代集成编程语言的新技术

在第二代集成编程语言中,我们对技术进行了全面修改,以实现实时性和更高的执行速度。 尽管NanoJ版本1中的程序作为虚拟机中的字节代码与电机控制器并行运行,因此不受严格确定性时序的约束,但新版本现在包含确定性的1 ms周期。 这可以通过“合作多任务”来保证:

控制器编程


在每个1 ms的周期内,数据首先从对象目录中读出,其中包含作为中央数据库的控制器的所有设置和状态值。 然后执行“操作系统”,例如马达控制器和现场总线通信的高级功能。

实际控制器在32 kHz时运行速度更快。 操作系统执行完所有循环所需的操作后,会将执行转移到VMM(虚拟机监视器)中的用户程序。


用户程序现在可以修改对象字典中的值,执行计算等,但必须在1ms过期之前将控制权交还给操作系统。 出于这个原因,这个过程也被称为合作多任务。 在循环到期之前,用户程序的输出值将被写回到目标目录,并由马达控制器在下一个循环中进行处理。 例如,它们也可以通过现场总线再次读出。

用户程序的沙箱

VMM在固件中提供受保护的运行时环境。所谓的sand box将用户程序限制在一定的内存区域和某些系统资源,从而确保用户程序不会导致实际的控制器固件崩溃。


由于计算密集型用户程序,系统还提供了防止控制延迟的保护:如果用户程序不合作,即在循环到期之前没有将控制权交还给操作系统,则程序终止。出现错误信息,在此期间电机控制器将继续平稳无间断地运行。沙盒还启用了第二个主要新功能:代替字节码,现在直接执行明显更快的机器代码。


NanoJEasy编程环境

NanoJEasy编程环境专为在NanoJ中简单创建程序而设计。程序可以使用带语法高亮的集成编辑器进行开发和编译。对于带有以太网接口(如N5)的电机控制器,程序也可以直接传送到电机控制器并启动和停止。

nanoj

例如:标签分配器

标签印刷

下面,我们将分两步开发一个简单的程序,反映标签分配器的功能。

应用中其他电机的任务很容易实现。 用于卷绕和退绕的电机以扭矩模式运行,输送机处于速度模式,并且这两种电机均可作为标准操作模式而无需编程。


然而,分配器需要一个小程序。 我们的目标是首先将电机加速到与传送带相同的速度,然后在检测到标签边缘时移动确定的距离(标签长度)。 为了保持简单的例子,我们在这里只使用了一个触发器输入。


在实际应用中,至少应有一个启动电机的额外启动输入。 在了解了示例之后,您将很容易添加输入查询。


第一个简单的版本

以下程序是我们的第一个简单版本,它只激活定位模式并启动电机:

//Flagposition-Mode

//mapping of frequently used SDO's:

map U16 ControlWord as output 0x6040:00
map S16 ProfileVelocity as inout 0x6081:00
map S32 TargetPosition as inout 0x607A:00

#include "wrapper.h"

//starting the main-routine and settings

void user()
{
od_write(0x6060,0x00, 1);
InOut.ProfileVelocity = 200;
InOut.TargetPosition = 1000000000;

//boot-up the state-machine

Out.ControlWord = 0x6;
yield();
Out.ControlWord = 0x7;
yield();
Out.ControlWord = 0x4F;
yield();
Out.ControlWord = 0x5F;
yield();

while(true)
{
yield();
}
}

对象目录条目的映射

将程序中的变量分配给对象目录条目的映射发生在第5-7行。例如,第6行指定对象0x6081的内容应在每个周期的2字节带符号(S16)变量“ProfileVelocity”中采用,并应在周期结束时写回。对象地址或对象对应于CAN标准DS402; 6081因此是最大定位速度。


如果映射被声明为不是“输入”而是“输入”或“输出”,则该变量仅在循环开始时读取,或者在循环结束时写回,例如第6行中的控制字。


第9行中的预处理器指令“include”wrapper.h“”对我们来说没有更多的兴趣。它只需在每个NanoJ程序中作为编译器的指令。


主程序

在第13行中,主程序以“user()”函数开始,该函数对应于NanoJ中C或Java中的“main”函数,并始终作为第一个函数执行。在第15行中,我们遇到了除映射之外访问对象目录的第二种可能性,即“od_write”命令。通过此命令,可以在程序序列中更改或读入“od_read”,因此可以更改或读入每个周期内很少或仅需要一次且因此不需要读入的对象。因此,通过访问CAN对象0x6060,“操作模式”来激活定位模式。


在接下来的两行中,指定了映射速度和目标位置。由于映射为“inout”,所以在这里为变量赋值就足够了;不需要显式写入命令。最后,通过映射的控制字,DS402最终状态机切换到“准备打开”状态。


然后下一行继续执行第一个“yield()”,我们的程序将控制权返回给操作系统,从而关闭1 ms周期。如后面的行所示,由于每个状态都需要在控制器中运行,所以在最终状态机的每次转换之后都会出现“yield()”。如果第22行中没有“yield()”,程序将从控制器的角度直接切换到“开启”状态。这是CAN标准所不允许的,并且不会因为这个原因而起作用。


在第五个“yield()”之后,因此在5ms之后,马达控制器激活并且马达加速到200步/秒的速度。单位取决于对象0x2060-0x2062中的设置;在我们的例子中,我们假设这些对象包含默认设置。


因此,我们已经达到了第一个程序版本的结尾。在最后一行中,无限循环会阻止程序进入结束状态,因为它会在下一个循环中重新启动。


触发和模拟输入

我们现在缺少的是触发输入的反应,它启动了预定义的路径。我们的第一个程序版本以恒定的速度(几乎)无限地运行,因为选择的目标位置实际上可能永远不会达到。这通常是速度模式的应用;然而,在这里不使用这种模式,我们将通过指定不可达的位置目标值来模拟它。这个技巧为我们节省了下一步在两种操作模式之间切换的努力。作为对输入的反应,重置目标位置就足够了。另外,模拟输入用于轻松设置标签长度。


要查看程序中输入的变化,我们首先需要映射数字输入和模拟输入的对象(第8-9行)。

map S32 TargetPosition as inout 0x607A:00
map U32 Inputs as input 0x60FD:00
map S32 Actual Position as input 0x6064:00
map S32 AnalogInput as input 0x6401:01

我们现在使用输入查询的代码和目标位置的设置扩展我们第一个版本的无限循环:

由于第37行数字输入对象的“&”(逻辑“和”)链接,位掩码为0x10000,因此只监视输入1。 当输入改变时,目标位置和速度改变。


目标位置由模拟量输入(0至1023)的值乘以10来计算。设置输入后,当前位置被读出,由模拟值计算出的目标位置被加上,绝对值 定位在线43中被激活。这确保直到在线47之后在控制器中采用2ms的延迟不再影响确定的目标位置。

最后的循环用于等待输入再次改变其状态。 随后,电动机现在停止,每次输入开关时,电动机将再次移动由模拟输入指定的距离。

while(true) 
{
if((In.Inputs / 0x10000) == 0x10000) //check if Input 1 is active
{
InOut.TargePosition = InActualPosition + (In.AnalogInput * 10); //calculate new target position
InOut.ProfileVelocity = 50; //set new target velocity
yield();
Out.ControlWord = 0x2F; //operation mode enable abs. positioning
yield();
Out.ControlWord = 0x3F; //start
yield();
while((In.Inputs & 0x10000) == 0x10000) //loop while Input 1 is still acive
{
yield();
}

}
yield();
}
BufferedWriter out = null;try {
out = new BufferedWriter(new FileWriter(”filename”, true));
out.write(”aString”);} catch (IOException e) {
// error processing code} finally {
if (out != null) {
out.close();
}}

这里说明

StringBuffer stringBuffer = new StringBuffer(“Hello World”);
System.out.println(stringBuffer.length());
System.out.println(stringBuffer.capacity());

这里有一个描述