Скорость изменения угла отклонения первого маятника

Графики, полученные в результате расчёта

Скорость тележки

Положение тележки

Угол отклонения первого маятника

Скорость изменения угла отклонения первого маятника

Угол отклонения второго маятника

Скорость изменения угла отклонения второго маятника

Подключение визуальной модели к точкам входа Simulink для получения анимационной картинки


Simulink располагает средствами (библиотечный блок VR-Sink), позволяющими подключить любой сигнал, распространяющийся во время моделирования между блоками модели, к входу одного из параметров виртуальной трёхмерной сцены, представленной в формате VRML, таким образом управляя движением определённых фрагментов сцены. Соответствующий блок VR-Sink в качестве параметра принимает имя VRML-файла, в котором хранится сцена, и имеет столько входов, сколько именованных узлов создал пользователь в иерархическом дереве сцены во время её проектирования. На каждый такой вход можно подать сигнал Simulink-модели, что приведёт виртуальную сцену в движение во время моделирования в соответствии с изменением сигнала.

 

Использование внешних интерфейсов МАТЛАБ.
Построение внешней S-функции на языке C++ с использованием библиотеки OpenGL для отображения параметризованной картинки


S-функции применяются в МАТЛАБ-Simulink для конструирования новых блоков, реализующих функции, недоступные в библиотечных блоках. S-функция представляет собой алгоритм преобразования входного сигнала (входных сигналов) в выходной (выходные). Наряду с собственным языком системы МАТЛАБ, S-функции можно писать и на ряде других языков программирования при условии соблюдения определённых правил. Это дает возможность использовать графические средства других языков для выдачи на экран анимационной картинки. В частности, опробована возможность создания S-функции на языке C++ с использованием популярной графической библиотеки OpenGL. Подаваемые на вход S-функции характеристики модели в текущий момент времени используются для формирования средствами C++/OpenGL в отдельном окне картинки, а зрительный эффект движущегося изображения получается за счёт того, что статическая картинка перерисовывается многократно с большой частотой для меняющихся значений параметров. Заключительным этапом построения работающей S-функции является её преобразование из исходного кода в объектный, представляющий из себя библиотеку dll. Это можно сделать из командной строки МАТЛАБ при помощи утилиты mex, производящей сборку кода на C/C++ в dll. Ниже приводится исходный код S-функции, записанный на языке C++, командная строка для сборки dll, а также один из кадров движущейся сцены.

 

Объединение модели Simulink с S-функцией.
Подача выходов модели на входы S-функции


На этом этапе сигналы, отвечающие физическим характеристикам модели, влияющим на её положение в пространстве (положение тележки и маятников), соединяются с входами S-функции, которая способна их обрабатывать, то есть отобразить картинку в нужном месте экрана в зависимости от их значений. Сама внешняя S-функция встраивается в модель при помощи стандартного блока S-Function, который принимает в качестве параметра имя внешней S-функции.

Реализация анимирующей S-функции и кадр из анимационной сцены

Здесь в качестве примера рассматривается другая задача – аналогичная предыдущей. Необходимо стабилизировать в вертикальном положении маятник, свободным концом шарнирно прикреплённый к тележке.

 

 

Кадр из анимационной сцены

Исходный код S-функции, написанной на языке C++ с использованием библиотеки OpenGL для отображения анимационной картинки перемещения маятника на движущейся тележке

 

 

#include <stdio.h>

#include <math.h>

#include <windows.h>

#include <GL/gl.h>

#include <GL/glu.h>

 

#define S_FUNCTION_NAME test_vis

#define S_FUNCTION_LEVEL 2

 

#define IN_0_FRAME_BASED FRAME_NO

 

#define OUT_0_FRAME_BASED FRAME_NO

 

#define SAMPLE_TIME_0 INHERITED_SAMPLE_TIME

 

#define SFUNWIZ_GENERATE_TLC 1

#define SOURCEFILES ""

#define PANELINDEX 6

#define SFUNWIZ_REVISION 2.0

 

#include "simstruc.h"

 

static double x1 = 0,

x2 = 0,

x1_ = 0,

x2_ = 0,

X = 0;

static double l1,l2;

 

static int glInited = 0;

static HWND hGlWindow = NULL;

static HDC hDC = NULL;

static HGLRC hRC = NULL;

 

#define GL_CLASS_NAME "gl_class_name"

 

void CALLBACK resize ( int width, int height )

{

glViewport(0,0,width,height);

 

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

gluPerspective(60,float(width)/height,(l1+l2),5*(l1+l2));

}

 

#define Pi 3.1415

 

void CALLBACK display()

{

glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

 

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

glTranslated(X,0,-2*(l1+l2));

//glRotated(90,1,0,0);

glBegin(GL_QUADS);

glColor3f(1,1,1);

glVertex3d(-l1,-l1/15,-l1/5);

glVertex3d( l1,-l1/15,-l1/5);

glVertex3d( l1, l1/15,-l1/5);

glVertex3d(-l1, l1/15,-l1/5);

 

glVertex3d(-l1, l1/15, 0);

glVertex3d(-l1,-l1/15, 0);

glVertex3d( l1,-l1/15, 0);

glVertex3d( l1, l1/15, 0);

 

glVertex3d( l1,-l1/15,-l1/5);

glVertex3d( l1,-l1/15, 0);

glVertex3d( l1, l1/15, 0);

glVertex3d( l1, l1/15,-l1/5);

 

glVertex3d(-l1,-l1/15,-l1/5);

glVertex3d(-l1,-l1/15, 0);

glVertex3d(-l1, l1/15, 0);

glVertex3d(-l1, l1/15,-l1/5);

 

glColor3f(0.8,0.1,0);

glVertex3d(-l1, l1/15,-l1/5);

glVertex3d(-l1, l1/15, 0);

glVertex3d( l1, l1/15, 0);

glVertex3d( l1, l1/15,-l1/5);

 

glVertex3d(-l1,-l1/15,-l1/5);

glVertex3d(-l1,-l1/15, 0);

glVertex3d( l1,-l1/15, 0);

glVertex3d( l1,-l1/15,-l1/5);

glEnd();

 

glTranslated(0,l1/7,-l1/10);

glRotated(180*x1/Pi,0,0,1);

GLUquadricObj *qObj = gluNewQuadric();

glPushMatrix();

glRotated(-90,1,0,0);

glColor3f(1,0,0);

gluCylinder(qObj,l1/10,l1/10,2*l1/3,15,3);

gluSphere(qObj,l1/10,15,15);

glPushMatrix();

glTranslated(-l1/15,0,2*l1/3);

gluSphere(qObj,l1/7,15,15);

glPopMatrix();

glTranslated(l1/15,0,2*l1/3);

gluSphere(qObj,l1/7,15,15);

glPopMatrix();

gluDeleteQuadric(qObj);

 

SwapBuffers ( hDC );

}

 

static int getBitDepth ()

{

HDC hDC = GetDC ( NULL );

HBITMAP hBmp = CreateCompatibleBitmap ( hDC, 1, 1 );

BITMAPINFO * bmi = (LPBITMAPINFO) malloc ( sizeof (BITMAPINFOHEADER) + 4 * sizeof (RGBQUAD) );

int res, bitCount;

 

bmi -> bmiHeader.biBitCount = (WORD) GetDeviceCaps ( hDC, BITSPIXEL );

bmi -> bmiHeader.biCompression = BI_BITFIELDS;

bmi -> bmiHeader.biSize = sizeof ( BITMAPINFOHEADER );

bmi -> bmiHeader.biWidth = 1;

bmi -> bmiHeader.biHeight = 1;

bmi -> bmiHeader.biClrUsed = 0;

bmi -> bmiHeader.biPlanes = 1;

 

res = GetDIBits ( hDC, hBmp, 0, 1, NULL, bmi, DIB_RGB_COLORS );

bitCount = bmi->bmiHeader.biBitCount;

 

DeleteObject ( hBmp );

ReleaseDC ( NULL, hDC );

DeleteObject ( hBmp );

free ( bmi );

 

return bitCount;

}

 

static int createOpenGlWindow ( int x, int y, int width, int height, int depthBits )

{

PIXELFORMATDESCRIPTOR pfd =

{

sizeof (PIXELFORMATDESCRIPTOR), // size of this Pixel Format Descriptor

1, // version number

PFD_DRAW_TO_WINDOW | // format must support Window

PFD_SUPPORT_OPENGL | // format must support OpenGL

PFD_DOUBLEBUFFER, // must support double buffering

PFD_TYPE_RGBA, // request an RGBA format

0, // select color depth

0, 0, 0, 0, 0, 0, // color bits

0, // no alpha buffer

0, // shift bit ignored

0, // no accumulation cuffer

0, 0, 0, 0, // accumulation bits ignored

0, // Z-Buffer depth

8, // 8-bit stencil buffer

0, // no auxiliary buffer

PFD_MAIN_PLANE, // main drawing layer

0, // reserved

0, 0, 0 // layer masks ignored

};

 

GLuint pixelFormat; // will hold the selected pixel format

 

DWORD windowStyle = WS_OVERLAPPEDWINDOW; // set window style

DWORD windowExtendedStyle = WS_EX_APPWINDOW; // set window extended style

RECT windowRect; // define window coordinates

int bpp;

 

windowRect.left = x;

windowRect.top = y;

windowRect.right = x+width;

windowRect.bottom = y+height;

pfd.cColorBits = getBitDepth();

pfd.cDepthBits = depthBits;

 

windowExtendedStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // window xxtended style

windowStyle = WS_OVERLAPPEDWINDOW; // window style

// adjust window, Account for window worders

AdjustWindowRectEx ( &windowRect, windowStyle, 0, windowExtendedStyle );

 

// create the OpenGL window

hGlWindow = CreateWindowEx ( windowExtendedStyle, // extended style

GL_CLASS_NAME, // class name

"Test", // window title

windowStyle, // window style

windowRect.left, // window (X,Y) position

windowRect.top,

windowRect.right - windowRect.left, // window width

windowRect.bottom - windowRect.top, // window height

HWND_DESKTOP, // desktop is window's parent

0, // no nenu

GetModuleHandle ( NULL ), // pass the window instance

NULL );

 

if ( hGlWindow == NULL ) // was window creation a success?

return 0; // if not then return false

 

hDC = GetDC ( hGlWindow ); // get device context for this window

 

if ( hDC == NULL ) // did we get a device context?

return 0; //done ();

 

bpp = getBitDepth (); // bit depth

 

pixelFormat = ChoosePixelFormat ( hDC, &pfd ); // find a compatible pixel format

 

if ( pixelFormat == 0 ) // did we find a compatible format?

return 0; // failed

 

if ( SetPixelFormat ( hDC, pixelFormat, &pfd ) == FALSE ) // try to set pixel format

return 0; // failed

 

hRC = wglCreateContext ( hDC ); // try to get a rendering context

 

if ( hRC == NULL ) // did we get a rendering context?

return 0; // failed

 

// make the rendering context our current rendering context

if ( wglMakeCurrent ( hDC, hRC ) == FALSE )

return 0; // failed

 

resize ( width, height ); // reshape our GL window

 

ShowWindow ( hGlWindow, SW_SHOW );

 

return 1; // window creating was successful

// further initialization will be done in WM_CREATE

}

 

static int destroyOpenGlWindow ()

{

wglMakeCurrent ( NULL, NULL );

 

if ( hRC != NULL )

wglDeleteContext ( hRC ); // delete The Rendering Context

 

if ( hDC != NULL )

ReleaseDC ( hGlWindow, hDC ); // release Our Device Context

 

if ( hGlWindow != NULL )

DestroyWindow ( hGlWindow ); // destroy the window

 

hGlWindow = NULL; // zero the window handle

hDC = NULL; // zero the device context

hRC = NULL; // zero the rendering context

 

return 1;

}

 

static LRESULT CALLBACK windowProc ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )

{

switch ( msg ) // evaluate window message

{

case WM_SIZE: // resizing action has taken place

resize (LOWORD (lParam), HIWORD (lParam)); // resize window

if (glInited) display ();

 

return 0;

}

 

return DefWindowProc ( hWnd, msg, wParam, lParam ); // pass nnhandled messages to DefWindowProc

}

 

static void initGL ()

{

float pos[4] = {1,1,0,1};

float dir[3] = {-1,-1,1};

 

GLfloat mat_specular[] = {1,1,1,1};

// create window class

WNDCLASSEX windowClass;

 

ZeroMemory ( &windowClass, sizeof (WNDCLASSEX) ); // clear the memory

 

windowClass.cbSize = sizeof (WNDCLASSEX); // size of the windowClass structure

windowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // redraws the window on any movement / resizing

windowClass.lpfnWndProc = (WNDPROC) windowProc; // windowProc Handles Messages

windowClass.hInstance = GetModuleHandle ( NULL ); // set the instance

windowClass.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE; // class background brush color

windowClass.hCursor = LoadCursor ( NULL, IDC_ARROW ); // load the arrow pointer

windowClass.lpszClassName = GL_CLASS_NAME; // sets the classname

windowClass.hIcon = NULL;

 

if ( RegisterClassEx ( &windowClass ) == 0 )

return;

 

createOpenGlWindow ( 50, 10, 500, 500, 32 );

 

glEnable(GL_DEPTH_TEST);

 

glEnable(GL_COLOR_MATERIAL);

 

glEnable(GL_LIGHTING);

glEnable(GL_LIGHT0);

 

glLightfv(GL_LIGHT0, GL_POSITION, pos);

glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, dir);

 

float amb[4] = {0.5,0.5,0.5,1};

glLightModelfv(GL_LIGHT_MODEL_AMBIENT,amb);

 

glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);

glMaterialf(GL_FRONT, GL_SHININESS, 128.0);

}

 

 

static void mdlInitializeSizes(SimStruct *S)

{

char buf[100];

l1 = mxGetString(ssGetSFcnParam(S,0),buf,100);

sscanf(buf,"%lf",&l1);

l2 = mxGetString(ssGetSFcnParam(S,1),buf,100);

sscanf(buf,"%lf",&l2);

 

if (!ssSetNumInputPorts(S, 1)) return;

ssSetInputPortVectorDimension(S, 0, 3);

ssSetInputPortRequiredContiguous(S, 0, 1);

ssSetInputPortDirectFeedThrough(S, 0, 1);

if (!ssSetNumOutputPorts(S, 1)) return;

ssSetOutputPortVectorDimension(S, 0, 3);

 

ssSetNumContStates(S, 0);

ssSetNumDiscStates(S, 0);

 

ssSetNumSFcnParams(S, 2);

 

ssSetNumSampleTimes(S, 1);

 

ssSetNumRWork(S, 0);

ssSetNumIWork(S, 0);

ssSetNumPWork(S, 0);

ssSetNumModes(S, 0);

 

ssSetNumNonsampledZCs(S, 0);

 

ssSetOptions(S, (SS_OPTION_EXCEPTION_FREE_CODE |

SS_OPTION_USE_TLC_WITH_ACCELERATOR |

SS_OPTION_WORKS_WITH_CODE_REUSE));

 

if (!glInited)

{

initGL();

glInited = 1;

}

}

 

static void mdlStart(SimStruct *S)

{

}

 

static void mdlInitializeSampleTimes(SimStruct *S)

{

ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);

ssSetOffsetTime(S, 0, FIXED_IN_MINOR_STEP_OFFSET);

}

 

static void mdlOutputs(SimStruct *S, int_T tid)

{

const double *u = (const double*)ssGetInputPortSignal(S,0);

double *y = (double*)ssGetOutputPortRealSignal(S,0);

 

l1 = 0;

char buf[100];

l1 = mxGetScalar(ssGetSFcnParam(S,0));

l2 = 0;

l2 = mxGetScalar(ssGetSFcnParam(S,1));

 

x1 = u[0];

x1_ = u[1];

X = u[2];

 

y[0] = u[0];

y[1] = u[1];

y[2] = u[2];

 

display();

 

Sleep(30);

}

 

static void mdlTerminate(SimStruct *S)

{

destroyOpenGlWindow ();

if (UnregisterClass(GL_CLASS_NAME, GetModuleHandle(NULL))!=0)

glInited = 0;

}

 

#ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */

#include "simulink.c" /* MEX-file interface mechanism */

#else

#include "cg_sfun.h" /* Code generation registration function */

#endif

Пример командной строки МАТЛАБ для преобразования исходного кода C/C++ в объектный