아래 내용은 마이크로소프트웨어 2008년 4월호에 기사로 나간 내용입니다. 물론 초안이라서 책에 나간 내용과는 약간 차이가 있습니다. 저작권 문제가 있을 수 있으므로, 복사하시지는 말고 링크만 걸어 주시기 바랍니다. 잡지 기사를 pdf로 보실려면 iMaso 홈페이지에 가셔서 구매하실 수 있습니다.
Stateflow를 활용한 실전 예제
요 약
Embedded MATLAB을 소개하며, 지금까지 배운 State machine, flow graph등을 바탕으로 실제 예제를 만들어 보자. 복잡한 내용은 하기가 힘들겠지만, 간단한 내용을 가지고 Stateflow의 기능을 활용해 보자.제 1 절 온도에 따른 팬 제어
이제 Stateflow에서 제공하는 대부분의 기능을 알았으니, 이런 기능들을 활용해서 하나의 예제를 모델링해보자.
1.1 두 개의 팬을 동작시키기 위한 요구 사항
두 개의 팬으로 케이스 내부의 온도를 유지하는 시스템이 있다. 여기서 온도에 따라서 한 개의 팬이 동작하던가 아니면 두 개의 팬이 동작 된다. 그리고 팬을 동작 시킬것인지에 대한 전체적인 On/Off 스위치가 있다. 자세한 요구 사항은 아래와 같다.
- Stateflow로 들어 오는 인풋은 케이스 내부의 온도 이다.
- Stateflow에 팬 전체를 On/Off 하는 이벤트가 들어 온다.
- 제어는 1초 단위로 한다.
- 온도가 50도 이상일 경우 전체 스위치가 On 상태이면 팬1을 On 시킨다.
- 온도가 65도 이상일 경우 전체 스위치가 On 상태이면 팬1과 팬2를 On 시킨다.
- 팬의 On/Off 여부는 Stateflow의 아웃풋 변수에 0, 1, 2로 표시한다. 즉 0이면 두 개의 팬 모두 Off 상태이다. 1이면 팬1이 On 상태이다. 2이면 두 개의 팬 모두 On 상태이다.
제 2 절 요구 사항에 따른 State Machine
먼저 요구 사항을 분석했을때 첫번째로 고려해야 할 사항이 팬의 전체적인 동작을 On/Off 시킬 수 있어야 한다. 이를 위해서 {PowerOn}, {PowerOff}두개의큰 state를 가진다. 두 state 사이의 transition은 SWITCH라는 이벤트에 의해서 동작하며, SWITCH 이벤트는 Stateflow에 인풋으로 들어 오며, either edge로 이벤트가 전달된다. 그림 1에서와 같이 Stateflow에서 모델링할 수 있으며, 그림 2와 같은 윈도우에서 SWITCH 이벤트에 대해서 셋팅할 수 있다. 여기서 trigger를 either로 했으므로 rising edge와 falling edge에서 이벤트가 발생하게 된다.
![]()
|
![]()
|
2.1 팬1과 팬2의 제어를 위한 State
팬1과 팬2의 제어를 위한 state의 경우 서로 독립적으로 On/Off가 가능해야 한다. 따라서 팬1과 팬2의 경우 parallel state로 모델링하면 된다. 그림 3와 같이 parallel state를 만들면 된다.
![]()
|
팬1과 팬2 각각의 state에는 팬의 On/Off가 되는 state를 모델링 해야 한다. 즉 온도가 50도 이상인 경우 팬1이 {On}이며, 50도 미만이면 {Off}로 transition이 발생하게 된다. 그리고 온도가 65도 이상이면, 팬2가 {On}이며, 65도 미만이면 {Off}로 transition이 발생한다. 이를 표시하면 그림 4와 같이 모델링할 수 있다.
![]()
|
2.2 팬1과 팬2의 동작 여부 모델링
그림 4는 팬 각각의 On/Off를 보여 주고 있다. 이를 체크하여 외부로 0, 1, 2를 내보내여야 하므로 이를 모델링을 해야 한다. 이때 사용하는 것이 Stateflow에서 제공하는 in() 함수이다. in() 함수의 인풋은 state 이름이며, 인폿으로 주어진 state가 활성화되어 있으면 리턴 값이 1이며, 비활성화되어 있으면 리턴 값이 0이다. 이러한 값을 계산하는 부분은 {FAN1}과{FAN2}의동작이완전히끝난후에계산해야하므로 parallel state를 하나 더 포함해야 한다. 그림 5를 보면 {FAN_Output}의 during action에서 in()함수를 이용하여 아웃풋 값인 control_fan에 {FAN1.On}과{FAN2.On}의활성화여부로값을대입하고있다.
![]()
|
2.3 모델의 문제점
그림 5를 보면 한 가지 문제가 있다. Parallel state인 {FAN1}, {FAN2}, {FAN_Output}들을보면우측 위에 숫자가적혀있다. 이 숫자가 각 parallel state의 실행 순서이다. {FAN1}의경우내부에{On}, {Off}가있으므로둘중에하나의 state가 활성화 되어야 하는데, 현재로서는 알 수가 없다. 따라서 지금 모델의 경우 문제가 있으므로 첫번째로 활성화되는 state를 표시하기 위해서 default transition으로 표시하게 된다. 그림 1에서도 마찬가지로 {PowerOn}과{PowerOff}두개의 state중에서 어느 state가 먼저 활성화가 되어야 하는지 알려 주는 default transition이 있어야 한다. 그림 6는 모든 것을 포함한 전체 모델이다. Stateflow로 모델링하다 보면 default transiton을 빠트리고 모델링하는 경우가 상당히 많다. 때문에 꼭 포함 시키기 바란다.
![]()
|
2.4 모델의 해석
그림 6이 전체 모델이므로 한번 해석을 해보자.
- 처음 state chart가 업데이트 될때 default transition을 따라서 {PowerOff}가활성화된다.
- SWITCH 이벤트가 발생하면 transition을 따라서 {PowerOn}이활성화되고, {FAN1}이활성화되며, default transition을 따라서 {Off}가활성화된다. 그 다음으로 parallel state인 {FAN2}가활성화되며, {Off}가활성화된다. 그 뒤 또 다른 {FAN_Output}이활성화된다.
- 다시 차트가 업데이트 되었을때, {FAN1.Off}에서조건이맞으면{FAN1.On}으로 transition이 일어 난다. 그리고 {FAN2.Off}에서조건에따라 {FAN2.On}으로 transition이 일어 난다. {FAN_Output}은 during action이 있으므로 {FAN1.On}이 활성화 되었을 경우 in()함수에 의해서 1이 리턴되고, {FAN2.On}이 활성화 되었을 경우 in() 함수에 의해서 1이 리턴된다. 그러므로 control_fan의 값이 결정된다.
{PowerOn.FAN_Output}을보면{PowerOn.FAN1}과{PowerOn.FAN2}와 같은 parallel state이지만 순서가 세번째 인것을 알 수 있다. 이는 항상 차트가 한번 업데이트 됐을때 팬1과 팬2의 On/Off가 이미 결정된 이후에 그 결과를 계산하기 때문이다. 만약 parallel state의 실행 순서가 바뀌어서 {PowerOn.FAN_Output}가첫번째로실행되면팬의 On/Off가 현재 업데이트된 결과가 아니고, 바로 이전에 차트가 업데이트 됐을때 결정된 팬의 On/Off를 현재 시점에 결정하여 내보내는 것이다.
모델링한 것만 보면 알 수 없지만, state chart는 외부에서 이벤트가 들어 오게 되면 이벤트가 발생했을때만 차트가 업데이트되서 실행되게 된다. 지금까지 설명한 것으로만 보면 SWITCH 이벤트가 발생했을때에만 차트가 업데이트 되므로 처음 SWITCH ON 했을때와 SWITCH OFF 했을때, 즉 두번만 차트가 업데이트 된다. 요구 사항을 보면 1초 단위로 차트가 업데이트되어서 팬을 제어해야 하므로, 이럴 경우에는 외부에서 1초 단위로 이벤트를 발생시켜서 그 이벤트를 인풋으로 받으면 된다. 보통 이런 이벤트는 내부에서 사용하지 않을 경우도 많이 있다. 즉 단순히 차트를 업데이트 하는데만 사용하는 것이다.
2.5 시물레이션을 해보자
Simulink로 모델링된 외부 내용과 state chart를 합쳐서 실제로 원하는 방식으로 동작하는지 확인해 보도록 하자. 그림 7는 제어 로직을 테스트 하기 위한 Simulink 모델이며, 그림 8와 그림 9는 Signal Builder를 이용하여 인풋을 생성한 것이다. 그림 8에서와 같이 SWITCH 이벤트가 100초와 500초에 각각 한 번씩 발생하게 된다. 또한 CLOCK 이벤트를 좀 더 확대해서 보면 그림 9와 같이 1초에 한 번씩 rising edge가 있다. 그래서 차트에서 CLOCK 이벤트를 받을때 rising edge 로 이벤트를 받으면 차트가 1초에 한 번씩 업데이트 되게 된다.
![]()
|
![]()
|
![]()
|
그림 10는 앞에서 만든 차트로 제어를 했을 경우 온도 변화를 본 것이다. 결과를 보면 알겠듯이 50도를 거의 넘지 않게 잘 제어가 되고 있음을 알 수 있다.
![]()
|
2.6 Graphical function을 써 보자
팬의 On/Off 를 결정하는 조건문을 보면 비슷한 형태이므로 graphical function으로 변경이 가능하다. 물론 실제로 이 모델은 복잡한 모델이 아니므로 바꾸지 않아도 괜찮지만, 이때까지 배운 것을 활용한다는 입장에서 한번 바꾸어 보자. 그림 11를 보면 graphical function을 정의하고 활용하고 있는 부분을 알 수 있다.
![]()
|
2.7 Embedded MATLAB을 써 보자
Stateflow가 실행되기 위해서는 Simulink가 있어야 하며, Simulink를 실행 시키기 위해서는 MATLAB이 있어야 한다. MATLAB은 자체적인 프로그래밍 언어가 있으며, 수학적인 계산은 행렬 연산을 기본으로 한다. 즉 덧셈, 뺄셈, 곱셈, 나눗셈의 경우 행렬 연산을 기본으로 하므로 공학적인 계산을 할 때는 정말 편리하게 사용할 수 있다.
Stateflow 내에서 복잡한 action의 경우 flow graph등으로 하기가 어렵다. 조건이 많으면 flow graph의 패턴등을 이용할 수 있지만, 그래프의 크기가 많이 커지면 너무 복잡해 보일 수 있다. 이와 같이 복잡한 조건문, 반복문의 경우와 수학적인 계산을 많이 해야 할 경우 MATLAB의 기능을 쓰면 쉽게 구현할 수 있다. 하지만 MATLAB의 기능을 쓰게 되면 나중에 자동 코드 생성시에는 코드가 생성되지 않는다. 때문에 자동 코드 생성이 가능하면서 MATLAB의 기본적인 수학 함수를 사용할 수 있게 해주는 것이 Embedded MATLAB 함수이다.
그림 12를 보면 그림 11에서 graphical function으로 만든 부부을 Embedded MATLAB으로 만든 것이다. Embedded MATLAB으로 함수를 구현하기 위해서는 그림 11에서 왼쪽에 보이는 아이콘 중에서 Embedded MATLAB 아이콘을 클릭하고 차트 에디터의 공간에서 왼쪽 클릭을 하면 그림 12와 같이 함수 이름을 적을 수 있는 사각형 박스가 나온다. 여기에 함수 이름을 적고 마우스로 박스 내부를 더블 클릭하게 되면 그림 12에서와 같이 Embedded MATLAB Editor가 나와서 여기에 프로그래밍을 하게 된다.
Embedded MATLAB으로 함수를 구현하게 되면 에니메이션으로 어떤 경로로 움직이는지를 볼 수 없는 단점이 있지만, 프로그래밍에 익숙한 사람들에게는 편한 인터페이스이다.
![]()
|
2.8 팬의 On/Off에 대한 제약을 주는 모델
실제 팬을 그림 10와 같이 On/Off를 하게 되면 너무 빈번하게 On/Off가 이루어지므로 하드웨어에 좋지 않은 영향을 줄 수 있다. 이럴 경우 한번 Off가 되면 특정 시간만큼은 무조건 Off 상태로 있다가 특정 시간이 지난 이후에야 On으로 움직이도록 제약을 주는 경우가 많다. Stateflow에서는 이와 같이 횟수나 시간에 관련된 함수를 많이 제공하는 데 이를 Temporal Logic 이라고 한다. 아래는 대표적인 Temporal Logic 들이다.
- after
- after(n, E) 형태로 사용하며, 이벤트 E가 n번 일어난 후에 참이 되거나 이벤트가 발생하게 된다.
- before
- before(n, E) 형태로 사용하며, 이벤트 E가 n번 일어 나기 전에 참이 되거나 이벤트가 발생한다.
- at
- at(n, E) 형태로 사용하며, 이벤트 E가 n번 일어났을때 참이 되거나 이벤트가 발생한다.
- every
- every(n, E) 형태로 사용하며, 이벤트 E가 n번씩 일어 났을때 참이 되거나 이벤트가 발생한다.
이런 Teporal Logic은 조건문안에 들어 가도 되고 이벤트 처럼 활용할 수 있다. 무슨 말인가 하면, 위에 적은 것처럼 transiton 위에서 조건문인 […] 안에 Temporal Logic이 있으면 참과 거짓으로 결과를 넘겨 주고, transition 위에서 이벤트 위치에 Temporal Logic이 있으면 정확하게 이벤트가 발생한 것으로 알려 준다.
지금까지 팬 제어를 위한 모델을 일단 {PowerOn.FAN1.Off}이활성화되고나서최소 100초는 지나서 조건이 맞으면 {PowerOn.FAN1.On}으로 transition이 일어 나도록 모델링을 해보자. 앞에서 언급한 Temporal Logic중에서 after()를 사용하는게 제일 적합하므로 그림 13와 같이 {FAN1}에서는 after()를 이벤트로 사용하였고, {FAN2}에서는 after()를 조건문 안에 사용하였다. 어떤 방식을 사용해도 상관 없지만, 하나 고려해야 할 것은 이벤트이냐 조건문 안에 사용되었느냐에 따라서 transition의 우선 순위가 달라진다. 앞 연재에서 언급했듯이 이벤트로 사용할 경우가 우선 순위가 조건문 안에 사용할 때보다 높다는 것을 기억하자.
![]()
|
그림 14를 보면 그림 10와 다르게 팬의 On/Off가 그렇게 자주 일어 나지는 않는 것을 알 수 있다.
![]()
|
제 3 절 기존의 C코드를 사용하자
이미 검증된 c 코드가 있는데, 이를 Stateflow에서 쓰도록 다시 작성하는 것은 시간과 비용 측면에서 좋지 않기 때문에, Stateflow에서는 기존의 이미 검증되어진 C코드를 간단하게 불러 쓸 수 있게 되어 있다. 이런것을 셋팅할때 사용하는 메뉴가 Target에 관련된 메뉴이다. 즉 Simulation Target에 포함을 하면 되는 것이다. 앞에서와 마찬가지로 여기서도 지금까지의 모델을 수정하여 기존의 C코드로 작성한 함수를 부르게 만들어 보자.온도 조건을 검사해서 transition이 발생하게 하는 부분을 아래와 같이 C로 작성했다고 생각하자. 이 파일의 이름은 custom_comp.c 라고 하면, custom_comp.h 파일은 함수의 선언만을 가지고 있다고 가정하자.
double isOK(double cTemp, double mTemp)
{
double y;
if ( cTemp > mTemp )
{
y = 1.0;
}
else
{
y = 0.0;
}
return y;
}
차트에디터에서 Tools → Open Simulation Target 메뉴를 선택하게 되면 그림 15와 같은 창이 뜨며, 여기서 “Include Code”와 “Source Files”에 각각 코드에 대한 내용을 적어 주면 된다. 즉 “Include Code”에는 C에서 헤더 파일 포함시킬때 처럼 하면 되고, “Source Files”에는 C 파일의 이름을 적어 주면 된다.
![]()
|
그림 15와 같은 윈도우에 셋팅을 하고 나서 실제로 사용은 일반적인 함수처럼 사용하면 된다. 그림 16에서 transition 부분을 주의 깊게 보면 isOK()라는 함수를 쓰고 있는 것을 알 수 있다.
![]()
|
3.1 기존 C 코드 사용의 주의점
앞에서 본 것처럼 기존의 C 코드를 함수 형태로 불러서 사용하는 것은 크게 힘들지 않지만, 몇 가지 주의 해야 할 점이 있다. 여기서 그 내용을 간략하게 살펴 보도록 하자.
3.1.1 C 수학 라이브러리 함수를 사용할 때
기본적으로 사용할 수 있는 수학 함수는 아래와 같으며, 한가지 특이한 점은 abs()함수가 Stateflow에서는 C와 다르게 동작한다. abs()함수의 경우 C에서는 abs, labs, fabs등 총 세 가지 형태가 있다. Stateflow내에서는 abs()함수 하나만 사용해도 abs()함수의 인풋 인자의 데이타 타입에 따라서 내부적으로 알맞은 함수가 불리운다.
|
|
또한 매크로 함수로 min()과 max() 함수가 Stateflow 내부에 존재하므로, 사용이 가능하다. 실제 매크로 함수는 아래와 같이 정의되어 있다.
#define min(x1, x2) ((x1) > (x2)) ? (x2) : (x1)
#define max(x1, x2) ((x1) > (x2)) ? (x1) : (x2)
3.1.2 기존 C 코드 사용상 주의점
- 문자열이 인풋 인자로 넘겨 줄때 C에서는 스트링 처리를 “…”로 감싸서 넘겨 주지만, Stateflow 내에서 이런 함수를 불러서 사용할 때는 ‘…’로 감싸서 넘겨 준다.
- C 함수에서는 call by value가 일반적인지만, call by reference가 필요할 경우가 있다. 이럴 경우 C에서와 같이 f(&c) 로 사용하면 된다.
- call by reference로 인풋 인자를 넘겨 줄때는 만약 함수의 인풋으로 들어 가는 변수가 차트의 인풋, 즉 Simulink로 부터 들어 오는 값이면 call by reference로 넘겨 줄 수 없다. 이럴 경우 차트의 내부 변수를 만들어서 값을 대입하고 내부 변수를 call by reference로 넘겨 주면 된다.
- 배열을 함수의 인풋 인자로 넘겨 줄 경우에는 무조건 f(&(x[0]))와 같이 써야 한다. C의 경우 f(x)만 해도 되지만 Stateflow 내에서는 이렇게 사용하면 안된다.
제 4 절 Stateflow 모델의 자동 C 코드 생성
이상으로 Stateflow를 활용하여 이벤트에 따라서 동작하도록 하는 모델을 시물레이션하는 방법을 보았다. Stateflow로 복잡한 모델등을 시물레이션으로 실제 하드웨어 없이 동작을 검증하고 나면, 실제로 실시간으로 테스트를 해야 한다. 실시간으로 테스트를 할 때는 C나 어셈블리 코드를 작성해야 하는데, 시물레이션을 한 모델을 다시 C로 재코딩하는 것은 많은 비용과 시간을 소모해야 한다. 이런 문제점을 해결하기 위해서 Stateflow Coder라는 것을 이용하면, 모델로부터 자동으로 C 코드를 생성할 수 있게 된다. 이렇게 C 코드를 자동으로 생성하게 되면, 모델로 시물레이션하고 다시 C로 코딩하고, 코딩된 프로그램이 모델과 같은지 테스트하는데 걸리는 시간등이 전혀 필요없게 된다. 이미 NASA나 Toyota 등과 같은 많은 회사들이 자동 코드 생성 기능을 사용하고 있다.



















글
댓글을 달아 주세요
댓글 RSS 주소 : http://www.cipher.pe.kr/tt/cipher/rss/comment/175댓글 ATOM 주소 : http://www.cipher.pe.kr/tt/cipher/atom/comment/175
기고기사와 님의 책으로 MATLAB을 배우고 있는 넋나간 엔지니어입니다.
보다 쉽게 MATLAB의 세계에 빠져들수 있도록 중급자와 실무에 근접한 내용의 책과 글 부탁드려봅니다.
도움 많이 받고있습니다.
감사합니다~
도움이 되신다니 감사합니다.
더 다양한 내용으로 사용자들에게 도움이 될 수 있도록 구상하고 있습니다.
감사합니다.