实时控制软件设计

一、不同实时操作系统的特性和相比较

1、Vxworks

Vxworks在实时操作系统行业内处于领先地位,拥有广大的用户。它协助32位、64位以及多核处理器,辅助ARM、AMD、PowerPC等系统。紧要利用于国防、航空航天、汽车、医疗和工业领域。它有着出色的付出条件和软件补助,可是昂贵的价钱让开发者望而却步。

2、QNX

QNX是一个分布式、嵌入式、可规模壮大、服从POSIX规范的类Unix微内核硬实时操作系统。紧要用以商用,目的市场重大是面向嵌入式系统。
其基础独立自处于一个被保障的地点空间,驱动程序、网络协议和应用程序处于程序空间中。内核仅提供4种服务:进程调度、进程间通信、底层网络通信和刹车处理。所有其他OS服务,都实现为合作的用户进程,在单独的地点空间运行。由此QNX内核非常小巧(QNX4.x大约为12Kb),运行速度极快;操作系统模块与根本互相独立,具有很高的可靠性。而且与UNIX具有莫大相似性,使得为数众多的安静成熟的UNIX、LINUX应用可以直接移植到QNX那些更加安定急迅的实时嵌入式平台上。

3、Xenomai

Xenomai是一个在Linux平台上创造起的通用实时框架的自由软件项目。
初期Xenomai是一种在行使双内核机制时对不可以用来强实时行使的Linux内核的扩张,其事先级高于Linux
内核。后来渐渐发展成一个老谋深算的实时Linux架构。
Xenomai
实时内核为开销强实时采纳提供了增长的职能,紧要包括实时线程调度与治本、用户空间实时任务帮助、线程同步服务、时钟服务、中断服务、动态内存申请和实时对象注册服务等。重要用于工业自动化行业。

4、Intime

INtime是与 Windows 操作系统共享硬件平台的实时操作系统。
Windows内核驱动程序管理用于周转基础和实时应用程序的内存,并且管理这六个系统里头的通讯接口。INtime
内核为实时虚拟机提供操作系统服务,包括一个按照优先级的领先式计划程序,该程序执行基于优先级的中断处理。内核计划程序已经进展优化,以拿到最佳的刹车性能。

4、Sylixos

SylixOS 是一款由中国人自主设计开发的大型嵌入式硬实时操作系统,匡助 SMP
多核,具有丰裕的文件系统、网络序列以及无数装置驱动援助,并提供完善的合一开发条件。经过长年累月的持续开发与改良,已经改成一个保险稳定,功能完善,易于开发调试的嵌入式实时系统开发平台。SylixOS的出生可以摆脱国内有的基点设备对外国嵌入式操作系统的倚重,为国内的嵌入式音信技术行业提供一个簇新的抉择。现已接纳于航空航天与国防导弹等领域。

5、ucos

UCOS
是一个得以遵照ROM运行的、可缩短的、抢占式、实时多任务内核,具有中度可移植性的嵌入式实时操作系统。特别契合于电脑和控制器,适合广大经贸操作系统。有着完整的网络连串和文件系统,并且开放源代码。功用支撑TCP/IP、USB、CAN总线、Modbus。具有一个有力的文件系统和图形用户界面。UCOS采纳可剥夺型实时多任务内核,其任务调度是截然按照任务优先级的抢占式调度,具有可靠及高速等风味。在大兴土木工业控制、医疗设施、航天系统等领域有广大的选用。

二、团队项目

完成一个团体项目,紧要成效是兑现一个两轴机械手的运动控制仿真,首要效率包括:

  • 用户接口任务:负责接收来自用户的请求,并发送运动指令给轨道插补任务。
  • 轨道插补任务:接收运动指令,实时总结各轴的职务和进度设定值。
  • 物理引擎接口:基于ODE开源物理引擎,创设一个两轴机械手及环境的大体模型,用轨迹插补任务输出的各轴地点和进度设定值控制模型的移位,并把实时状态汇报给轨道插补任务。
  • 图形化用户接口:可依照qt把上述效用集成到一个GUI界面。

自家对轨道插补任务和图形化用户接口比较感兴趣。我事先出席过部分嵌入式项目,有较强的编纂代码的力量。在《数控系统》的学习过程中,就对插补很感兴趣。此外,我也可望得以触发部分图形化的编程,学习一下这上头的学识。

三、编程实现稳定运动轨迹生成器

在task_trajectory_generator任务中加进代码来拍卖来自task_command_sender的定位运动命令new_cmd,遵照该命令中付出的职务、速度、加速度、减速度,实现一个梯形加减速(原理见数控技术教材)的位移轨迹生成器,当达到目的地点时,把new_cmd.Done设为true,主程序检测到new_cmd.Done为true时将截至运行。
编程要求:

  • 轨道生成器的代码结构能充裕显示出基于状态机的编程方法。
  • 把第一个可以运行的本子宣布到温馨的github账号上,并不停优化和提交。
  • 创制一个TODO.md文档写下自己的编程思路和下一步要做的干活。

为了收缩运行时刻和改进输出的效率,我改变了new_cmd命令中的Position值,注释掉了Task
提姆e的printf部分,并将task_trajectory_generator的职责周期改为0.1s。
V1.0代码已宣布到GitHub上

https://github.com/liqi120150/simple_motion.git

如下:

#include <stdio.h>
#include <math.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>

#include <native/task.h>
#include <native/timer.h>

// Data Type

typedef struct
{
    bool Request;
    bool Response;
    bool Done;
    double Position;
    double Velocity;
    double Acceleration;
    double Deceleration;
    double Jerk;
} tPosCmd;

typedef struct
{
    double Position;
    double Velocity;
} tAxisSetpoint;

//Global variables

RT_TASK task_trajectory_generator;
RT_TASK task_command_sender;

tPosCmd new_cmd;
tAxisSetpoint axis1_setpoint;

int cycle_count = 0;

/*****************************************************
 * status machine
 * 
*****************************************************/
typedef enum
{
    Idel = 0,
    Accelerate,
    Decelerate,
    UniformVelocity 
}trajectoryStatusDef;

#define Position_DeadSize   10
#define Velocity_DeadSize   10
void task_trajectory_generator_proc(void *arg)
{
    RTIME now, previous;
    trajectoryStatusDef Status = Idel;

    /*
     * Arguments: &task (NULL=self),
     *            start time,
     *            period (here: 0.1 s)
     */
    rt_task_set_periodic(NULL, TM_NOW, 100000000);
    previous = rt_timer_read();

    axis1_setpoint.Position = 0;
    axis1_setpoint.Velocity = 0;

    int temp;

    float delta_T;
    while (1) {
        rt_task_wait_period(NULL);
        now = rt_timer_read();

        /*
         * NOTE: printf may have unexpected impact on the timing of
         *       your program. It is used here in the critical loop
         *       only for demonstration purposes.
         */
        /*
        printf("Task A Time since last turn: %ld.%06ld ms\n",
               (long)(now - previous) / 1000000,
               (long)(now - previous) % 1000000);
        */


        //  Add your code
        delta_T = ((double)(now - previous) / 1000000000);
        previous = now;

        if(new_cmd.Request)
        {
            printf("Position:%10.3f,Velocity:%10.3f at time:%10.3f s.\r\n", 
                axis1_setpoint.Position,
                axis1_setpoint.Velocity,
                (double)now / 1000000000);
            if(fabs(new_cmd.Position - axis1_setpoint.Position) > Position_DeadSize)
            {
                if(fabs(new_cmd.Velocity - axis1_setpoint.Velocity) > Velocity_DeadSize)
                {
                    if(fabs((axis1_setpoint.Velocity * axis1_setpoint.Velocity) / (2*new_cmd.Deceleration)) >= fabs(new_cmd.Position - axis1_setpoint.Position))
                    {
                        if(axis1_setpoint.Velocity > 0)
                            Status = Decelerate;
                        else
                            Status = Accelerate;
                    }
                    else if(new_cmd.Velocity > axis1_setpoint.Velocity)
                        Status = Accelerate;
                    else
                        Status = Decelerate;
                }
                else
                {
                    if(fabs((axis1_setpoint.Velocity * axis1_setpoint.Velocity) / (2*new_cmd.Deceleration)) >= fabs(new_cmd.Position - axis1_setpoint.Position))
                    {
                        if(axis1_setpoint.Velocity > 0)
                            Status = Decelerate;
                        else
                            Status = Accelerate;
                    }
                    else
                        Status = UniformVelocity;
                }
            }
            else
            {
                Status = Idel;
                new_cmd.Response = true;
                new_cmd.Done = true;
            }
            switch(Status)
            {
                case Accelerate:
                    //s = s0 + v0*delta_T + 0.5*acce*(delta_T^2)
                    axis1_setpoint.Position += axis1_setpoint.Velocity*delta_T + 0.5*new_cmd.Acceleration * (delta_T * delta_T);
                    //v = v0 + acce*delta_T
                    axis1_setpoint.Velocity += new_cmd.Acceleration*delta_T;
                    break;
                case Decelerate:
                    //s = s0 + v0*delta_T - 0.5*Deceleration*(delta_T^2)
                    axis1_setpoint.Position += axis1_setpoint.Velocity*delta_T - 0.5*new_cmd.Deceleration * (delta_T * delta_T);
                    //v = v0 - Deceleration*delta_T
                    axis1_setpoint.Velocity -= new_cmd.Deceleration*delta_T;    
                    break;
                case UniformVelocity:
                    //s = s0 + v0*delta_T;
                    axis1_setpoint.Position += axis1_setpoint.Velocity*delta_T;
                    break;
                case Idel:
                    break;
                default:
                    break;
            }
        }


    }
}

void task_command_sender_proc(void *arg)
{
    RTIME now, previous;

    /*
     * Arguments: &task (NULL=self),
     *            start time,
     *            period (here: 2 s)
     */
    rt_task_set_periodic(NULL, TM_NOW, 2000000000);
    previous = rt_timer_read();

        new_cmd.Request = false;
        new_cmd.Response = false;
        new_cmd.Done = false;
        new_cmd.Position = 0;
        new_cmd.Velocity = 0;
        new_cmd.Acceleration = 0;
        new_cmd.Deceleration = 0;
        new_cmd.Jerk = 0;

    while (1) {
        rt_task_wait_period(NULL);
        now = rt_timer_read();

        /*
         * NOTE: printf may have unexpected impact on the timing of
         *       your program. It is used here in the critical loop
         *       only for demonstration purposes.
         */
         /*
        printf("Task B Time since last turn: %ld.%06ld ms\n",
               (long)(now - previous) / 1000000,
               (long)(now - previous) % 1000000);
        */
               previous = now;
                cycle_count = cycle_count + 1;
                printf("cycle_count:%d\n",cycle_count);

                if(cycle_count == 5)
                {
                    new_cmd.Request = true;
                    new_cmd.Response = false;
                    new_cmd.Done = false;
                    new_cmd.Position = 20000;
                    new_cmd.Velocity = 1000;
                    new_cmd.Acceleration = 50;
                    new_cmd.Deceleration = 50;
                    new_cmd.Jerk = 0;

                }

    }
}

void catch_signal(int sig)
{

}


int main(int argc, char* argv[])
{
    signal(SIGTERM, catch_signal);
    signal(SIGINT, catch_signal);

    /* Avoids memory swapping for this program */
    mlockall(MCL_CURRENT|MCL_FUTURE);

        printf("A simple motion control demo\n");
    /*
     * Arguments: &task,
     *            name,
     *            stack size (0=default),
     *            priority,
     *            mode (FPU, start suspended, ...)
     */
    rt_task_create(&task_trajectory_generator, "rttask_trajectory_generator", 0, 99, 0);
    rt_task_create(&task_command_sender, "rttask_command_sender", 0, 98, 0);

    /*
     * Arguments: &task,
     *            task function,
     *            function argument
     */
    rt_task_start(&task_trajectory_generator, &task_trajectory_generator_proc, NULL);
    rt_task_start(&task_command_sender, &task_command_sender_proc, NULL);

        while(!new_cmd.Done);
        printf("End! \n");
    rt_task_delete(&task_trajectory_generator);
    rt_task_delete(&task_command_sender);
    return 0;
}

代码运行效果:
图片 1

明确,如今的代码应用全局变量举办任务间的通信,且三遍只好发送一个恒定命令。计划下一步使用Queue来开展通信,实现命令的缓冲和移动轨迹的坦荡工作。

发表评论

电子邮件地址不会被公开。 必填项已用*标注