《实时控制软件设计》第三周作业 一、不同实时操作系统的性能和比较 二、团队项目 三、编程实现定位运动轨迹生成器
1、Vxworks
Vxworks在实时操作系统行业内处于领先地位,拥有众多的用户。它支持32位、64位以及多核处理器,支持ARM、Intel、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 Time的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
",
(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.
",
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
",
(long)(now - previous) / 1000000,
(long)(now - previous) % 1000000);
*/
previous = now;
cycle_count = cycle_count + 1;
printf("cycle_count:%d
",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
");
/*
* 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!
");
rt_task_delete(&task_trajectory_generator);
rt_task_delete(&task_command_sender);
return 0;
}
代码运行效果:
显然,目前的代码利用全局变量进行任务间的通信,且一次只能发送一个定位命令。计划下一步使用Queue来进行通信,实现命令的缓冲和运动轨迹的平滑工作。