Скорость изменения угла отклонения первого маятника
Графики, полученные в результате расчёта
Скорость тележки
Положение тележки
Угол отклонения первого маятника
Скорость изменения угла отклонения первого маятника
Угол отклонения второго маятника
Скорость изменения угла отклонения второго маятника
Подключение визуальной модели к точкам входа 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++ в объектный