<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>라이다맨 시원의 연구개발 라이프</title>
    <link>https://lidarmansiwon.tistory.com/</link>
    <description>lidarmansiwon 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Wed, 24 Jun 2026 16:50:56 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>lidarmansiwon</managingEditor>
    <image>
      <title>라이다맨 시원의 연구개발 라이프</title>
      <url>https://tistory1.daumcdn.net/tistory/7961141/attach/3cabffb9a57c4374838b1a20050523d7</url>
      <link>https://lidarmansiwon.tistory.com</link>
    </image>
    <item>
      <title>[논문 리뷰] 강화학습 기반예인선 제어방법에 의한선박 자율 접안</title>
      <link>https://lidarmansiwon.tistory.com/16</link>
      <description>&lt;h1&gt;논문 요약: 강화학습 기반 예인선 제어방법에 의한 선박 자율 접안&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 글은 &lt;a href=&quot;http://journal.knst.kr/_common/do.php?a=full&amp;amp;bidx=2529&amp;amp;aidx=28828&quot;&gt;&quot;강화학습 기반 예인선 제어방법에 의한 선박 자율 접안&quot;&lt;/a&gt; (홍승조, 김진환 저자) 논문을 읽고 개인적으로 공부한 내용을 정리하였다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 서론 (Introduction)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 연구는 저속에서 기동성이 제한되는 대형 선박의 접안 작업을 지원하기 위해 예인선 자율 운항 기술을 제안한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;연구 배경:&lt;/b&gt; 기존의 예인선 접안 작업은 도선사와 예인선 운용 인력의 직관에 의존하는 수동적인 방식으로 이루어지며, 이는 많은 비용과 인력을 필요로 한다. 예인선의 자율 운항은 이러한 제한점을 보완할 수 있는 중요한 과제이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기존 연구의 한계:&lt;/b&gt; 기존 연구들은 예인선의 힘을 단순히 선박에 부착된 추진기(Thruster) 형태로 모델링하여, 예인선이 선박 주변을 이동하며 밀거나 당기는 실제 운용 방식을 제대로 반영하지 못했다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;본 연구의 제안:&lt;/b&gt; 실제 운용 방식을 고려하여 예인선의 위치 이동까지 포함한 선박 접안 문제를 &lt;b&gt;강화학습(PPO 알고리즘)&lt;/b&gt;을 통해 해결하고자 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 시스템 모델링 (System Modeling)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선박과 예인선의 동역학 및 상호작용을 하이브리드 시스템으로 정의하고, 항구 내에서 지배적인 외란인 바람을 모델링하였다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 선박 모델링&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;643&quot; data-origin-height=&quot;320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsigI2/dJMcagYdwjC/mBlicbtknsV3DQI5N1jNZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsigI2/dJMcagYdwjC/mBlicbtknsV3DQI5N1jNZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsigI2/dJMcagYdwjC/mBlicbtknsV3DQI5N1jNZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsigI2%2FdJMcagYdwjC%2FmBlicbtknsV3DQI5N1jNZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;532&quot; height=&quot;265&quot; data-origin-width=&quot;643&quot; data-origin-height=&quot;320&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;3자유도(Surge, Sway, Yaw) 비선형 모델을 사용하며 운동방정식은 식 (1)과 같다.&lt;br /&gt;$$M\dot{v}+Dv=\tau, \quad \dot{\eta}=R(\psi)v$$&lt;/li&gt;
&lt;li&gt;여기서 $M$은 관성행렬, $D$는 제동행렬, $\tau$는 제어 입력(예인선에 의한 힘)을 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 예인선 모델링 (Tugboat Modeling)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 연구에서는 2대의 예인선을 운용하며, 각 예인선은 선박의 지정된 위치(작용점)에 계류하여 힘을 가한다고 가정한다. 예인선의 개별적인 제어 입력이 선박 전체에 미치는 영향은 선박의 본체 고정 좌표계(Body-fixed frame)를 기준으로 한 기하학적 관계를 통해 계산된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;예인선 제어 변수:&lt;/b&gt;&lt;br /&gt;각 예인선 $i$ ($i=1, 2$)에 대하여 다음과 같은 변수를 정의한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$f_i$: 예인선이 작용하는 힘의 크기 (Magnitude)&lt;/li&gt;
&lt;li&gt;$\alpha_i$: 선박의 선수 방향($x$축)을 기준으로 한 힘의 작용 방향 (Direction angle)&lt;/li&gt;
&lt;li&gt;$(l_{x_i}, l_{y_i})$: 선박의 무게중심($O_b$)으로부터 $i$번째 예인선 작용점까지의 거리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;힘과 모멘트의 변환:&lt;/b&gt;&lt;br /&gt;두 예인선이 생성하는 힘은 선박의 무게중심에 작용하는 전체 제어력 벡터 $\tau = [\tau_u, \tau_v, \tau_r]^T$ (Surge force, Sway force, Yaw moment)로 변환된다. 이는 식 (3)과 같이 작용점의 위치와 힘의 방향에 따른 기하학적 관계 행렬을 통해 도출된다.이 식을 통해 예인선의 힘($f$)과 방향($\alpha$)이 선박의 종방향/횡방향 운동 및 회전 모멘트에 기여하는 성분으로 분해 및 합산되어 선박의 동적 모델에 입력된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$&lt;br /&gt;\tau = \begin{bmatrix}&lt;br /&gt;\cos(\alpha_1) &amp;amp; \sin(\alpha_1) &amp;amp; -l_{y_1}\cos(\alpha_1) + l_{x_1}\sin(\alpha_1) ; \&lt;br /&gt;\cos(\alpha_2) &amp;amp; \sin(\alpha_2) &amp;amp; -l_{y_2}\cos(\alpha_2) + l_{x_2}\sin(\alpha_2)&lt;br /&gt;\end{bmatrix}^T&lt;br /&gt;\begin{bmatrix} f_1 ; \ f_2 \end{bmatrix}&lt;br /&gt;$$&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.3 하이브리드 시스템 모델링&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1257&quot; data-origin-height=&quot;678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HCIsg/dJMcaaDHbN2/pglsLqe5A1i1kMSOxuokg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HCIsg/dJMcaaDHbN2/pglsLqe5A1i1kMSOxuokg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HCIsg/dJMcaaDHbN2/pglsLqe5A1i1kMSOxuokg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHCIsg%2FdJMcaaDHbN2%2FpglsLqe5A1i1kMSOxuokg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;625&quot; height=&quot;337&quot; data-origin-width=&quot;1257&quot; data-origin-height=&quot;678&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예인선의 제어(연속 시간)와 위치 이동(이산 시간)이 결합된 하이브리드 시스템으로 정의한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;운용 모드:&lt;/b&gt; 선박의 선수/선미에서 당기는 &lt;b&gt;'예인모드($q_1$)'&lt;/b&gt;와 한쪽 현측에서 밀고 당기는 &lt;b&gt;'접안모드($q_2$)'&lt;/b&gt;로 구분한다.&lt;/li&gt;
&lt;li&gt;모드 전환 시 예인선은 위치를 변경하며, 이 시간 동안 제어 입력 $\tau$는 0이 된다.&lt;/li&gt;
&lt;li&gt;예인선 제어(연속시간) + 예인선 위치 전환(이산 이벤트)이 결합된 &lt;b&gt;하이브리드 시스템&lt;/b&gt;으로 정의.&lt;/li&gt;
&lt;li&gt;두 가지 모드:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;q₁: 예인모드(towing)&lt;/b&gt; &amp;ndash; 예인선이 선수&amp;middot;선미 방향에서 당김.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;q₂: 접안모드(berthing)&lt;/b&gt; &amp;ndash; 한쪽 현측에 계류하여 밀고 당김.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;모드 전환 시:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예인선이 선박에서 떨어져 새 위치로 이동하는 &lt;b&gt;전이 구간&lt;/b&gt; 존재 (Fig. 2).&lt;/li&gt;
&lt;li&gt;이 시간 T 동안 제어입력 u(t)=0u(t) = 0u(t)=0 로 설정.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;G 함수: 이전 q와 현재 q를 비교해 &lt;b&gt;모드 유지/전환&lt;/b&gt; 결정.&lt;/li&gt;
&lt;li&gt;R 함수: 새 모드 시작 시 &lt;b&gt;제어입력 초기화&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.4 외란 모델링&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;항만 내에서는 &lt;b&gt;바람이 주요 외란&lt;/b&gt;이라 보고 바람만 고려.&lt;/li&gt;
&lt;li&gt;바람에 의한 힘&amp;middot;모멘트는 선체 전&amp;middot;측면 투영면적과 풍력계수, 상대풍 속도/방향으로 표현한다.&lt;/li&gt;
&lt;li&gt;바람의 힘은 풍속, 상대 풍향, 선박의 투영 면적 및 풍력 계수를 기반으로 계산된다. 이때, 바람 속도와 방향은 평균값을 중심으로 한 &lt;b&gt;가우시안 분포&lt;/b&gt;에서 샘플링된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 강화학습 알고리즘 (Reinforcement Learning Algorithm)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연속적인 행동 공간 처리에 적합하고 안정적인 성능을 보이는 &lt;b&gt;PPO(Proximal Policy Optimization)&lt;/b&gt; 알고리즘을 적용하였다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 PPO 알고리즘&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;행동공간이 연속인 문제에 적합한 &lt;b&gt;PPO(Proximal Policy Optimization)&lt;/b&gt; 채택.&lt;/li&gt;
&lt;li&gt;신뢰 영역 기반의 정책 경사 알고리즘으로, 정책 업데이트 시 범위를 제한하여 학습의 안정성을 보장한다.&lt;/li&gt;
&lt;li&gt;기존 정책과 새 정책의 확률비 rt(&amp;theta;)r_t(\theta)rt​(&amp;theta;) 를 이용해
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;clip된 &lt;b&gt;손실함수 L(&amp;theta;)&lt;/b&gt; 로 학습 안정성 확보.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이득값 A^t\hat{A}_tA^t​ 를 통해 좋은 행동은 강화, 나쁜 행동은 약화.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 학습 네트워크&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;467&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mrM3I/dJMcagDU7UL/3Gjysca3uFU6mdWd3irUZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mrM3I/dJMcagDU7UL/3Gjysca3uFU6mdWd3irUZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mrM3I/dJMcagDU7UL/3Gjysca3uFU6mdWd3irUZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmrM3I%2FdJMcagDU7UL%2F3Gjysca3uFU6mdWd3irUZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;355&quot; height=&quot;319&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;467&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정책 네트워크 + 가치 네트워크를 공유&lt;/b&gt;하는 구조(Fig. 3)이다.&lt;/li&gt;
&lt;li&gt;가치(Value)와 정책(Policy) 네트워크를 공유하며, 2개의 은닉층(64 노드, Tanh 활성함수)을 사용한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;입력(State):&lt;/b&gt; 선박의 상태($x, y, \psi, u, v, r$), 목표와의 오차($e_d, e_\psi, e_v$), 그리고 이전 단계의 행동값(Action)을 사용한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;출력(Action):&lt;/b&gt; 예인선 힘의 변화량($\Delta f$), 방향 변화량($\Delta \alpha$), 그리고 모드 변경을 결정하는 변수 $k$를 포함한 총 5개의 값을 출력한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$-1 \le k &amp;lt; 0$ 이면 예인모드($q_1$), $0 \le k \le 1$ 이면 접안모드($q_2$)를 선택한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.3 보상 함수&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목표: 목표 위치&amp;middot;방위&amp;middot;속력을 만족할수록 보상이 커지도록 설계되었다.&lt;/li&gt;
&lt;li&gt;보상 R:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;거리오차 ed, 방위오차 e&amp;psi;, 속도오차 ev를 기반으로 음의 가중합 형태.&lt;/li&gt;
&lt;li&gt;속도오차는 크기가 작으므로 계수 c를 곱해 비중 보정.&lt;/li&gt;
&lt;li&gt;정규화 인자 &amp;alpha;로 전체 보상을 0~1 사이로 조정.&lt;/li&gt;
&lt;li&gt;선박이 부두에 충돌 시 벌점 p = &amp;ndash;1 부여.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;매 타임스텝마다 보상을 계산해 PPO의 손실함수에 사용.&lt;/li&gt;
&lt;li&gt;선박이 부두에 충돌할 경우 벌점($p=-1$)을 부여한다.&lt;br /&gt;$$R=\frac{-||e_{d}||-||e_{\varphi}||-c||e_{\nu}||+\alpha}{\alpha}+p$$&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 시뮬레이션 결과 (Simulation Results)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python 기반의 OpenAI Gym 환경을 구축하여 검증을 수행하였다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 예비 시뮬레이션&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;6,000톤급 선박 모델을 사용하여 장애물이 없는 환경에서 학습을 진행한 결과, 예인선 2대에 의해 성공적으로 자율 접안함을 확인하였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6D2JK/dJMcabJnzgc/WXPPWLkmPkASNlEKdPPkg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6D2JK/dJMcabJnzgc/WXPPWLkmPkASNlEKdPPkg1/img.png&quot; data-origin-width=&quot;588&quot; data-origin-height=&quot;513&quot; data-is-animation=&quot;false&quot; width=&quot;447&quot; height=&quot;390&quot; style=&quot;width: 51.848%; margin-right: 10px;&quot; data-widthpercent=&quot;52.46&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6D2JK/dJMcabJnzgc/WXPPWLkmPkASNlEKdPPkg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6D2JK%2FdJMcabJnzgc%2FWXPPWLkmPkASNlEKdPPkg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;588&quot; height=&quot;513&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HtTkW/dJMcadAm0Ae/WtFkKTNg5xNx7E1URlFCPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HtTkW/dJMcadAm0Ae/WtFkKTNg5xNx7E1URlFCPK/img.png&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;593&quot; data-is-animation=&quot;false&quot; width=&quot;422&quot; height=&quot;406&quot; style=&quot;width: 46.9892%;&quot; data-widthpercent=&quot;47.54&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HtTkW/dJMcadAm0Ae/WtFkKTNg5xNx7E1URlFCPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHtTkW%2FdJMcadAm0Ae%2FWtFkKTNg5xNx7E1URlFCPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;616&quot; height=&quot;593&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2 본 시뮬레이션 (인천 내항 시나리오)&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;555&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6Dx8d/dJMcadAm0A1/DlMlS82dykseK0LtTKKUa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6Dx8d/dJMcadAm0A1/DlMlS82dykseK0LtTKKUa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6Dx8d/dJMcadAm0A1/DlMlS82dykseK0LtTKKUa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6Dx8d%2FdJMcadAm0A1%2FDlMlS82dykseK0LtTKKUa0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;343&quot; height=&quot;312&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;555&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 &lt;b&gt;인천 내항 부두 입항 시나리오&lt;/b&gt; 구성.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;좁은 갑문을 통과해 목표 부두에 접안하는 상황.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;각 모드(예인/접안)별 예인선 힘과 방향각, 변화량에 대해 &lt;b&gt;제약조건(Table 1)&lt;/b&gt; 설정.&lt;/li&gt;
&lt;li&gt;학습 과정:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초기에는 갑문/부두 충돌로 에피소드가 조기에 종료.&lt;/li&gt;
&lt;li&gt;학습이 진행되며 선박이 갑문을 통과하고, 예인모드&amp;ndash;접안모드 간 &lt;b&gt;모드 전환이 여러 번&lt;/b&gt; 발생(Fig. 8 상단).&lt;/li&gt;
&lt;li&gt;최종 단계에서는:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예인모드로 갑문 통과 후 &lt;b&gt;단 한 번의 모드 전환&lt;/b&gt;으로 접안모드에 진입,&lt;/li&gt;
&lt;li&gt;목표 위치에 도달하는 &lt;b&gt;안정된 정책&lt;/b&gt;으로 수렴(Fig. 8 하단, 논문 확인).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;외란 실험:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;풍속 11&amp;ndash;13 m/s, 풍향 205&amp;ndash;215&amp;deg;의 강한 바람을 인가.&lt;/li&gt;
&lt;li&gt;Fig. 9:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외란 없는 경우(노란 궤적)와 있는 경우(빨간 궤적) 모두 목표 부두에 접안 성공.&lt;/li&gt;
&lt;li&gt;녹색 구간에서 예인모드&amp;rarr;접안모드 전환이 일어남.&lt;br /&gt;&amp;rarr; 강한 바람에도 강화학습 기반 제어가 &lt;b&gt;실용적인 자율 접안 성능&lt;/b&gt;을 보임을 확인.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 결론 (Conclusion)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;본 연구는 PPO 알고리즘을 이용하여 예인선의 위치 이동(모드 전환)까지 고려한 선박 접안 문제를 해결하였다.&lt;/li&gt;
&lt;li&gt;예인선의 운용을 '예인모드'와 '접안모드'로 정의하고, 강화학습을 통해 예인선의 이동과 운용 판단을 자율화한 국내 최초의 연구 사례이다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>논문 리뷰</category>
      <category>강화학습</category>
      <category>예인선</category>
      <category>이접안</category>
      <author>lidarmansiwon</author>
      <guid isPermaLink="true">https://lidarmansiwon.tistory.com/16</guid>
      <comments>https://lidarmansiwon.tistory.com/16#entry16comment</comments>
      <pubDate>Fri, 21 Nov 2025 18:08:54 +0900</pubDate>
    </item>
    <item>
      <title>[제어] Sliding Mode Control</title>
      <link>https://lidarmansiwon.tistory.com/15</link>
      <description>&lt;h1&gt;슬라이딩 모드 제어(Sliding Mode Control, SMC) 요약&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문서는 Steve Brunton 교수의 &quot;Sliding Mode Control&quot; 강의 영상을 기반으로 작성되었다. SMC는 비선형 제어 시스템 분야에서 가장 강인(Robust)한 제어 기법 중 하나로 평가받는다. 영상은 &lt;a href=&quot;https://youtu.be/WZfz2Ezc-YA?si=UABF7eYHkL4iYT8t&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MATLAB Korea의 슬라이딩 모드 제어란?&lt;/a&gt; 이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위상 평면부터 SMC 이해를 위한 시각적, 수식적 설명 그리고 예제 설명이 매우 잘되어있는 영상이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. SMC의 개요 및 핵심 특성&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1. 정의&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슬라이딩 모드 제어(SMC)는 비선형(Nonlinear) 동적 시스템을 제어하기 위한 기법이다. 시스템의 상태(State)가 특정한 &lt;b&gt;'슬라이딩 표면(Sliding Surface)'&lt;/b&gt;에 도달한 후, 그 표면을 따라 미끄러지듯(Slide) 목표 지점으로 수렴하도록 제어 입력을 설계하는 방식이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.2. 장점과 단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;장점 (Pros):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;극단적인 강인성 (Robustness):&lt;/b&gt; 모델링의 불확실성(Uncertainty)이나 외부 외란(Disturbance)이 존재하더라도 시스템을 원하는 상태로 강제할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;차수 축소 (Order Reduction):&lt;/b&gt; $n$차 시스템을 슬라이딩 모드에서는 1차 시스템처럼 단순화하여 다룰 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단점 (Cons):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;채터링 (Chattering):&lt;/b&gt; 제어 입력이 고주파수로 매우 빠르게 스위칭(On/Off)하는 현상이 발생한다. 이는 모터나 밸브 같은 물리적 액츄에이터를 마모시키거나 손상시킬 수 있다. (후술할 '경계층' 기법으로 해결 가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 동작 원리: 위상 평면과 슬라이딩 표면&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1. 위상 평면 (Phase Plane) 해석&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차 시스템(예: 질량-스프링-댐퍼)의 거동을 이해하기 위해 위치($x$)와 속도($\dot{x}$)를 축으로 하는 위상 평면을 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;자유 반응:&lt;/b&gt; 제어가 없을 때, 댐핑(Damping)이 없으면 원형 궤적을 그리고, 댐핑이 있으면 나선형을 그리며 원점(0,0)으로 수렴한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SMC의 목표:&lt;/b&gt; 이 위상 평면상에 &lt;b&gt;슬라이딩 표면($s=0$)&lt;/b&gt;이라 불리는 직선(또는 초평면)을 긋고, 시스템의 상태가 이 선을 벗어나지 않도록 만든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2. 제어의 두 단계 (Two Phases of SMC)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SMC는 시스템의 거동을 명확하게 두 단계로 구분한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;도달 단계 (Reaching Mode):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시스템 상태가 초기 위치에서 &lt;b&gt;슬라이딩 표면($s=0$)에 도달할 때까지&lt;/b&gt;의 구간이다.&lt;/li&gt;
&lt;li&gt;이 단계에서는 시스템이 무조건 표면을 향해 가도록 강력한 제어 입력을 가한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;슬라이딩 단계 (Sliding Mode):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일단 표면에 도달하면, 상태 궤적은 &lt;b&gt;표면을 따라 원점(평형점)으로 미끄러져 내려간다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;이때 시스템의 동역학은 원래의 복잡한 $n$차 미분방정식이 아닌, 설계자가 정의한 슬라이딩 표면의 식(주로 1차 미분방정식)에 의해 지배된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 수학적 설계 및 유도 과정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SMC 제어기 설계는 시스템을 안정화시키는 제어 입력 $u$를 찾는 과정이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1. 시스템 모델 및 슬라이딩 변수 정의&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 2차 시스템을 가정한다 (예: $\ddot{x} = f(x, \dot{x}) + g(x, \dot{x})u$).&lt;br /&gt;먼저 &lt;b&gt;슬라이딩 변수(Sliding Variable) $s$&lt;/b&gt;를 정의한다.&lt;br /&gt;$$s = \lambda e + \dot{e} \quad (\text{또는 } s = ce + \dot{e})$$&lt;br /&gt;여기서 $e$는 오차($x_{des} - x$)이며, 목표가 원점 안정화라면 $x$ 자체를 사용한다. 영상에서는 $s = C^T \mathbf{x}$ 형태를 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 $s=0$이 유지된다면, $\dot{x} = -Cx$가 되어 시스템은 자연스럽게 $x=0$으로 수렴하는 &lt;b&gt;1차 시스템&lt;/b&gt;처럼 동작한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2. 도달 조건 (Reachability Condition)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제어기는 상태 궤적이 표면 $s=0$으로 수렴하도록 보장해야 한다. 이를 위해 &lt;b&gt;리야푸노프(Lyapunov) 안정성 판별법&lt;/b&gt;을 사용한다.&lt;br /&gt;리야푸노프 후보 함수를 $V = \frac{1}{2}s^2$라 할 때, 시스템이 안정하려면 $\dot{V} &amp;lt; 0$이어야 한다.&lt;br /&gt;$$\dot{V} = s \cdot \dot{s} &amp;lt; 0$$&lt;br /&gt;즉, $s$가 양수면 $\dot{s}$는 음수여야 하고, $s$가 음수면 $\dot{s}$는 양수여야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.3. 도달 법칙 (Reaching Law)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 도달 조건을 만족시키기 위해 $\dot{s}$의 거동을 강제로 정의한다. 가장 일반적인 방법은 &lt;b&gt;정속도 도달 법칙(Constant Rate Reaching Law)&lt;/b&gt;이다.&lt;br /&gt;$$\dot{s} = -\eta \cdot \text{sign}(s)$$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$\eta$ (eta): 설계자가 설정하는 양의 이득(Gain) 값. 이 값이 클수록 표면으로 더 빨리 도달하며 외란에 더 강인해진다.&lt;/li&gt;
&lt;li&gt;$\text{sign}(s)$: 부호 함수 ($s&amp;gt;0$이면 +1, $s&amp;lt;0$이면 -1).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.4. 제어 입력 $u$의 유도&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 정의된 도달 법칙과 실제 시스템의 동역학을 결합하여 $u$를 구한다.&lt;br /&gt;$$\dot{s} = \frac{\partial s}{\partial \mathbf{x}} \dot{\mathbf{x}} = \mathbf{C}^T (f(\mathbf{x}) + g(\mathbf{x})u)$$&lt;br /&gt;이 식을 위의 도달 법칙($-\eta \cdot \text{sgn}(s)$)과 같다고 놓고 $u$에 대해 푼다.&lt;br /&gt;$$u = g(\mathbf{x})^{-1} \left[ -\mathbf{C}^T f(\mathbf{x}) - \eta \cdot \text{sgn}(s) \right]$$&lt;br /&gt;이 식은 보통 두 부분으로 해석된다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;등가 제어 ($u_{eq}$):&lt;/b&gt; 시스템의 현재 동역학을 상쇄하여 상태를 표면 위에 머물게 하는 항 ($-\mathbf{C}^T f(\mathbf{x})$ 부분).&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스위칭 제어 ($u_{sw}$):&lt;/b&gt; 오차나 외란을 억제하고 표면으로 상태를 밀어 넣는 불연속 항 ($-\eta \cdot \text{sgn}(s)$ 부분).&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 채터링(Chattering) 문제와 해결책&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1. 채터링의 원인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상적인 SMC는 무한히 빠른 스위칭을 가정한다. 하지만 실제 디지털 제어 환경에서는 샘플링 시간의 지연 등으로 인해, 상태가 $s=0$을 정확히 따라가지 못하고 표면을 중심으로 위아래로 빠르게 진동한다. 이것이 채터링이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주된 원인은 불연속 함수인 &lt;b&gt;$\text{sgn}(s)$&lt;/b&gt;의 사용이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2. 해결책: 경계층(Boundary Layer) 도입&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불연속적인 스위칭을 완화하기 위해 $s=0$ 주변에 얇은 &lt;b&gt;경계층($\Phi$)&lt;/b&gt;을 설정한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$\text{sgn}(s)$ 함수 대신 연속 함수인 &lt;b&gt;포화 함수(Saturation Function, $\text{sat}$)&lt;/b&gt;를 사용한다.&lt;br /&gt;$$\text{sat}\left(\frac{s}{\Phi}\right) = \begin{cases} \text{sgn}(s) &amp;amp; \text{if } |s| &amp;gt; \Phi \ s/\Phi &amp;amp; \text{if } |s| \le \Phi \end{cases}$$&lt;/li&gt;
&lt;li&gt;&lt;b&gt;효과:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;경계층 밖($|s| &amp;gt; \Phi$): 기존처럼 강하게 표면으로 유도한다.&lt;/li&gt;
&lt;li&gt;경계층 안($|s| \le \Phi$): 이득이 부드럽게 변하여 고주파 진동(채터링)을 억제한다. 마치 높은 이득의 비례 제어(P-Control)처럼 동작한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;트레이드오프:&lt;/b&gt; 채터링은 줄어들지만, $s=0$에 완벽하게 수렴하지 않고 경계층 내부에 머무르는 약간의 정상 상태 오차가 발생할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 예제 시뮬레이션 결과 (MATLAB/Simulink)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영상에서는 질량-스프링-댐퍼 시스템에 SMC를 적용하여 비교 실험을 수행한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;제어 없음 (Uncontrolled):&lt;/b&gt; 시스템이 댐핑에 의해 천천히 멈춘다. (수렴 시간 &amp;gt; 10초)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기본 SMC ($\text{sgn}$ 함수 사용):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시스템이 약 2초 만에 매우 빠르게 목표 지점에 도달한다.&lt;/li&gt;
&lt;li&gt;하지만 제어 입력 그래프를 보면, 액츄에이터가 감당하기 힘들 정도로 &lt;b&gt;심각한 고주파 진동(채터링)&lt;/b&gt;이 관찰된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;경계층 SMC ($\text{sat}$ 함수 사용):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수렴 속도와 성능은 기본 SMC와 거의 동일하게 빠르다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;결정적인 차이:&lt;/b&gt; 제어 입력이 매끄럽다(Smooth). 채터링이 제거되어 실제 하드웨어에 적용 가능한 형태가 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 결론 요약&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SMC는 시스템을 원하는 동역학(슬라이딩 표면)으로 강제로 구속하는 기법이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;불확실성과 외란에 매우 강인&lt;/b&gt;하다는 것이 최대 장점이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;채터링&lt;/b&gt;이라는 물리적 한계가 있으나, &lt;b&gt;경계층(Saturation 함수)&lt;/b&gt; 기법을 통해 성능 저하를 최소화하며 해결할 수 있다.&lt;/li&gt;
&lt;li&gt;따라서 로봇, 항공, 전력 전자 등 정밀하고 강인한 제어가 필요한 분야에 매우 적합하다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>이론 정리</category>
      <category>Sliding mode control</category>
      <category>SMC</category>
      <category>슬라이딩 모드 컨트롤</category>
      <category>제어</category>
      <author>lidarmansiwon</author>
      <guid isPermaLink="true">https://lidarmansiwon.tistory.com/15</guid>
      <comments>https://lidarmansiwon.tistory.com/15#entry15comment</comments>
      <pubDate>Thu, 20 Nov 2025 22:08:23 +0900</pubDate>
    </item>
    <item>
      <title>Proximal Policy Optimization (PPO) - OpenAI</title>
      <link>https://lidarmansiwon.tistory.com/11</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script&gt; MathJax = { tex: {inlineMath: [['$', '$']]} }; &lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논문 링크: &lt;a href=&quot;https://arxiv.org/abs/1707.06347&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/abs/1707.06347&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1756829376368&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Proximal Policy Optimization Algorithms&quot; data-og-description=&quot;We propose a new family of policy gradient methods for reinforcement learning, which alternate between sampling data through interaction with the environment, and optimizing a &amp;quot;surrogate&amp;quot; objective function using stochastic gradient ascent. Whereas standar&quot; data-og-host=&quot;arxiv.org&quot; data-og-source-url=&quot;https://arxiv.org/abs/1707.06347&quot; data-og-url=&quot;https://arxiv.org/abs/1707.06347v2&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fjJ2J/hyZG7Emi8J/S88aL6AkKeaXVAhDPuWyH0/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/rlsul/hyZGbnau0T/cWXwy0PvVjRuTDmWHhjNp1/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000&quot;&gt;&lt;a href=&quot;https://arxiv.org/abs/1707.06347&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://arxiv.org/abs/1707.06347&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fjJ2J/hyZG7Emi8J/S88aL6AkKeaXVAhDPuWyH0/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/rlsul/hyZGbnau0T/cWXwy0PvVjRuTDmWHhjNp1/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Proximal Policy Optimization Algorithms&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;We propose a new family of policy gradient methods for reinforcement learning, which alternate between sampling data through interaction with the environment, and optimizing a &quot;surrogate&quot; objective function using stochastic gradient ascent. Whereas standar&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;arxiv.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 논문 리뷰는 전체적으로 논문을 바탕으로 공부 한 내용을 정리하였다. 필요한 부분에서 논문의 Figure와 수식을 보여주며 설명을 덧붙였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PPO는 강화학습(Reinforcement Learning)에서 &lt;b&gt;정책 경사(Policy Gradient)&lt;/b&gt; 계열의 알고리즘이다. 기존 정책 경사 방법들의 단점인 불안정한 학습과 복잡한 구현 문제를 해결하는데 중점을 둔 알고리즘이다. PPO의 가장 큰 목표는 &lt;b&gt;안정적이고 신뢰할 수 있는 정책 업데이트&lt;/b&gt;를 통해 학습 성능을 높이는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;▷ &lt;b&gt;정책 경사(Policy Gradient)&lt;/b&gt;란?&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정책 경사(Policy Gradient): &lt;/b&gt;&quot;잘한 행동은 더 많이 하도록, 못한 행동은 덜 하도록 직접적으로 가르치는 방법&quot;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;강화 학습의 목표는 agent가 최상의 보상을 받도록 행동을 가르치는 것이다. 이때 agent의 행동 지침을 정책(Policy)라고 부른다. 정책 경사는 이 '정책'을 직접적으로 좋게 만드는 방법이다.&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;골프 선수 비유&amp;nbsp;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기 골프를 배우는 로봇이 있다고 상상해 보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정책(Policy)&lt;/b&gt;: 로봇의 스윙 자세, 클럽 선택 등 샷을 하기 위한 모든 행동 방식.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;행동(Action)&lt;/b&gt;: 실제로 골프채를 휘두르는 것.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;보상(Reward)&lt;/b&gt;: 공이 홀컵에 가까이 갈수록 높은 점수(보상)를 받음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 로봇을 어떻게 훈련시킬까?&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;일단 쳐본다&lt;/b&gt;: 현재의 정책(스윙 자세)으로 공을 쳐본다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;결과를 확인한다&lt;/b&gt;: 공이 홀컵에 아주 가깝게 붙음. &quot;이번 샷은 아주 좋음&quot; (높은 보상 획득)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;정책을 수정한다&lt;/b&gt;: 방금 그 &quot;아주 좋았던&quot; 샷을 만들었던 스윙 자세를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;더 자주 하도록&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;정책을 직접 수정. 즉, 그 스윙 자세와 관련된 파라미터들을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;보상이 증가하는 방향&lt;/b&gt;으로 업데이트.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;반복&lt;/b&gt;: 반대로 공이 엉뚱한 곳으로 날아갔다면(낮은 보상), 그 스윙 자세는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;덜 하도록&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;정책을 수정.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이처럼&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;'결과(보상)가 좋았던 행동의 확률을 높이고, 결과가 나빴던 행동의 확률은 낮추는'&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;방식으로 정책을 직접 업데이트하는 것이 바로 정책 경사의 핵심 아이디어이다. 여기서 '경사(Gradient)'라는 단어는 '어느 방향으로 정책을 수정해야 보상이 가장 많이 늘어날까?'를 수학적으로 계산하는 것을 의미한다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;정책 경사의 핵심 수식&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정책 경사의 목표는 보상의 기댓값 $J(\theta)$를 최대로 만드는 정책의 파라미터 $\theta$를 찾는 것이다. 이를 위해 $J(\theta)$를 $\theta$로 미분한 경사(gradient)를 계산하고, 이 경사를 따라 파라미터를 업데이트한다. 그 경사는 다음과 같이 표현된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;$$\nabla_\theta J(\theta) = \hat{\mathbb{E}}_t \left[ \nabla_\theta \log \pi_\theta(a_t|s_t) \hat{A}_t \right]$$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$\nabla_\theta J(\theta)$ : 우리가 계산하고 싶은 '경사'. 즉, '정책을 어느 방향으로 업데이트해야 보상이 커지는가'에 대한 정보.&lt;/li&gt;
&lt;li&gt;$\pi_\theta(a_t|s_t)$ : 현재 정책. 상태 $s_t$에서 행동 $a_t$를 할 확률을 의미한다.&lt;/li&gt;
&lt;li&gt;$\nabla_\theta \log \pi_\theta(a_t|s_t)$ :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;'행동의 방향'&lt;/b&gt;이다. 이 부분이 특정 행동($a_t$)을 할 확률을 높이거나 낮추는 역할을 수행한다.&lt;/li&gt;
&lt;li&gt;$\hat{A}_t$ :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;'행동의 좋고 나쁨'&lt;/b&gt;을 알려주는 척도(어드밴티지 함수). 이 값이 크면(보상이 좋으면) 해당 행동의 확률을 더 많이 높이고, 작거나 음수이면(보상이 나쁘면) 확률을 낮추게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;결국 이 수식은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&quot;좋았던 행동(&lt;span&gt;&lt;span&gt;&lt;span aria-hidden=&quot;true&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;A&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;^&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;t&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;)은 더 자주 하도록, 나빴던 행동(&lt;span&gt;&lt;span&gt;&lt;span aria-hidden=&quot;true&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;A&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;^&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;t&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;)은 덜 하도록 정책(&lt;span&gt;&lt;span&gt;&lt;span aria-hidden=&quot;true&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;pi;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;theta;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;)을 업데이트하라&quot;&lt;/b&gt;는 직관적인 아이디어를 수학적으로 표현한 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;PPO&lt;/b&gt;는 이 기본적인 정책 경사 아이디어에서 한 걸음 더 나아가, 정책이 너무 급격하게 변해서 학습이 불안정해지는 것을 막기 위해 '클리핑'이라는 안전장치를 추가한, 더 발전된 알고리즘이라고 이해할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 아이디어는 간단하다.&lt;b&gt; 이전 정책(old policy)과 새로운 정책(new policy)의 차이가 너무 커지지 않도록 제한&lt;/b&gt;하는 것이다. 정책이 급격하게 변하면 학습이 불안정해지고 좋은 방향으로 수렴하기 어렵기 때문이다. 이를 위해&amp;nbsp;&lt;b&gt;PPO는 '클리핑(Clipping)'이라는 독특한 방법을 사용&lt;/b&gt;한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PPO의 성공 비결은 클리핑(Clipping)에 있다고 해도 무관하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클리핑을 아주 간단하게 비유하자면 &quot;자동차의 속도 제한 장치&quot;와 같다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;클리핑(Clipping)이란?&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정책(Policy)이 한 번에 너무 많이 변하지 않도록 강제로 &quot;상한선&quot;과 &quot;하한선&quot;을 설정하는 기술이다. &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동차의 속도 제한 장치가 시속 110km를 넘지 못하게 막는 것처럼, 클리핑은 정책 업데이트의 변화량을 특정 범위 $ [1 - \epsilon, 1 + \epsilon] $ 안에 가둬버린다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정책 업데이트: AI가 경험을 통해 자신의 행동 지침(정책)을 수정하는 과정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;변화량:&lt;/b&gt; 이전 정책 대비 새로운 정책이 얼마나 달라졌는지의 정도 ($r_t(\theta)$로 측정)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클리핑 범위 $ [1 - \epsilon, 1 + \epsilon] $:&lt;/b&gt; &quot;정책을 바꾸더라도 이 범위를 벗어날 수는 없어!&quot;라고 정해놓은 &lt;b&gt;안전 구간&lt;/b&gt;. 보통 $\epsilon$은 0.1이나 0.2 같은 작은 값을 쓴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 필자는 이게 왜 핵심인가 궁금했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클리핑이 없는 이전의 정책 경사 알고리즘들은 다음과 같은 큰 문제가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제점: &quot;대박&quot; 한 번에 흥분해서 망하는 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 어떤 행동을 우연히 했는데, 운 좋게 엄청나게 큰 보상(대박)을 받았다고 상상해 보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;클리핑이 없다면?&lt;/b&gt; AI는 &quot;이 행동이 최고다!&quot;라고 과도하게 믿어버린다. 그래서 그 행동을 할 확률을 &lt;b&gt;아주 아주 크게&lt;/b&gt; 높여버리게 된다. (정책이 급격하게 변함) 하지만 그건 그냥 운일 수 도 있다. 그 결과, 다른 좋은 행동들을 탐색할 기회를 잃고 오히려 전체 성능이 나락으로 떨어지는 &lt;b&gt;학습 붕괴(catastrophic collapse)&lt;/b&gt;가 발생할 수 있다. 마치 주식 초보가 우연히 한 종목으로 대박을 치고 전 재산을 몰빵하는 것과 같다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클리핑이 있다면?&lt;/b&gt; AI가 엄청난 보상을 받아도, 클리핑이 &quot;워워, 진정해. 좋은 건 알겠는데 일단 조금만 바꿔보자.&quot;라며 제동을 건다. 정책의 변화량을 최대 $1+\epsilon$까지만 허용하기 때문에, AI는 &lt;b&gt;흥분하지 않고 침착하게&lt;/b&gt; 학습을 이어나갈 수 있다. 반대로 아주 나쁜 결과를 얻었을 때도, &quot;&lt;span&gt;&lt;span&gt;&lt;span aria-hidden=&quot;true&quot;&gt;&lt;span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&amp;minus;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;ϵ&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&quot;까지만 변화를 제한하여 &quot;이번 한 번의 실수로 이 행동을 완전히 포기하지는 말자&quot;고 막아준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://arxiv.org/pdf/1707.06347&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Schulman et al., (2017)&lt;/a&gt;에서는 다음과 같이 설명한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. 클리핑의 목적: 보수적인 정책 업데이트 (Conservative Policy Iteration)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 수식(논문 eq(6))은 TRPO와 같은 이전 알고리즘에서 사용하던 목적 함수 $L^{CPI}(\theta)$를 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$L^{CPI}(\theta) = \hat{\mathbb{E}}_t \left[ \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{old}}(a_t|s_t)} \hat{A}_t \right] = \hat{\mathbb{E}}_t [r_t(\theta) \hat{A}_t]$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 문제는, 이 수식을 제한 없이 최대화하면 $r_t(\theta)$ (정책 변화량)가 너무 커져서 정책이 과도하게 업데이트되고 학습이 불안정해진다는 것이다. 논문에서는 이를 &quot;excessively large policy update&quot;라고 표현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하기 위해 PPO는 수식 (7), 즉 &lt;b&gt;클리핑된 목적 함수 $L^{CLIP}$&lt;/b&gt; 을 제안한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$L^{CLIP}(\theta) = \hat{\mathbb{E}}_t \left[ \min(r_t(\theta) \hat{A}_t, \text{clip}(r_t(\theta), 1 - \epsilon, 1 + \epsilon) \hat{A}_t) \right]$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 $\min$ 함수와 $\text{clip}$ 함수의 조합이다. 이 조합이 바로 정책 업데이트에 '브레이크'를 거는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2. 클리핑의 작동 방식 (Figure 1)&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Figure 1&lt;/b&gt;은 클리핑이 어떻게 작동하는지를 가장 직관적으로 보여준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;301&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l8inJ/btsQhfNrRCh/ZJCapjpUsgpryaquebidfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l8inJ/btsQhfNrRCh/ZJCapjpUsgpryaquebidfK/img.png&quot; data-alt=&quot;Schulman et al., (2017)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l8inJ/btsQhfNrRCh/ZJCapjpUsgpryaquebidfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl8inJ%2FbtsQhfNrRCh%2FZJCapjpUsgpryaquebidfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;718&quot; height=&quot;301&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;301&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Schulman et al., (2017)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;왼쪽 그래프 (어드밴티지가 긍정적일 때, $A &amp;gt; 0$):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;상황:&lt;/b&gt; 현재 한 행동이 평균보다 좋았으므로, 이 행동을 할 확률을 높여야 한다 ($r_t(\theta)$을 키워야 함).&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클리핑:&lt;/b&gt; 하지만 $r_t(\theta)$가 $1+\epsilon$을 초과하는 순간, 목적 함수 $L^{CLIP}$의 값은 더 이상 증가하지 않고 수평선처럼 평평해진다. 즉, 보상이 아무리 좋아도 정책 업데이트의 이득(incentive)에 상한선(cap)을 둬서 과도한 업데이트를 막는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;오른쪽 그래프 (어드밴티지가 부정적일 때, $A &amp;lt; 0$):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;상황:&lt;/b&gt; 현재 한 행동이 평균보다 나빴으므로, 이 행동을 할 확률을 낮춰야 한다 ($r_t(\theta)$을 줄여야 함).&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클리핑:&lt;/b&gt; $r_t(\theta)$가 $1-\epsilon$보다 작아져도 목적 함수는 더 이상 나빠지지 않는다. 이는 한 번의 큰 실수로 인해 특정 행동의 확률을 지나치게 많이 줄여버리는 비관적인 업데이트를 막는 &lt;b&gt;하한선(floor)&lt;/b&gt; 역할을 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 $L^{CLIP}$은 기존의 목적 함수 $L^{CPI}$에서 이득을 취하되, 정책이 너무 멀리 벗어나려고 하면 그 이득을 잘라내어(clip) 버리는 **비관적인 경계(pessimistic bound) 또는 하한선(lower bound)**의 역할을 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3. 클리핑의 효과 (Figure 2)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Figure 2&lt;/b&gt;는 실제 학습 과정에서 클리핑의 효과를 보여준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;301&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/URCec/btsQhwOTVVK/HNFQ86I6lJN0Q4K2QAIA51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/URCec/btsQhwOTVVK/HNFQ86I6lJN0Q4K2QAIA51/img.png&quot; data-alt=&quot;Schulman et al., (2017)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/URCec/btsQhwOTVVK/HNFQ86I6lJN0Q4K2QAIA51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FURCec%2FbtsQhwOTVVK%2FHNFQ86I6lJN0Q4K2QAIA51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;715&quot; height=&quot;301&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;301&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Schulman et al., (2017)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;가로축:&lt;/b&gt; 정책 업데이트 강도 (0은 업데이트 안 함, 1은 PPO가 한 번 업데이트한 상태)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;세로축:&lt;/b&gt; 목적 함수의 값&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프를 보면, 주황색 선($L^{CPI}$)은 업데이트 강도가 커짐에 따라 제한 없이 계속 올라간다. 하지만 초록색 선($L^{CLIP}$)은 어느 지점(KL 발산이 약 0.02가 되는 지점)에서 최대치를 찍고 더 이상 크게 증가하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 $L^{CLIP}$이 &lt;b&gt;정책이 너무 많이 변하는 것을 막는 페널티&lt;/b&gt;로 작용하고 있음을 보여준다. PPO는 이 $L^{CLIP}$을 최적화하기 때문에, $L^{CPI}$를 직접 최적화할 때보다 훨씬 안정적으로 정책을 업데이트할 수 있는 것이다. 논문의 설명처럼 $L^{CLIP}$은 $L^{CPI}$의 **하한선(lower bound)**으로 작용하며, 너무 큰 정책 업데이트에 대한 페널티를 부과한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;PPO의 핵심: 클리핑된 목적 함수 (Clipped Surrogate Objective Function)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PPO의 핵심은 바로 이 '클리핑된 목적 함수'에 있다. 수식은 복잡해 보일 수 있지만, 아이디어는 직관적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$L^{CLIP}(\theta) = \hat{\mathbb{E}}_t \left[ \min \left( r_t(\theta) \hat{A}_t, \text{clip}(r_t(\theta), 1 - \epsilon, 1 + \epsilon) \hat{A}_t \right) \right]$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 각 항목의 의미는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$r_t(\theta)$: &lt;b&gt;확률 비율(Probability Ratio)&lt;/b&gt;로, 새로운 정책과 이전 정책이 특정 행동을 선택할 확률의 비율&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$r_t(\theta) = \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{old}}(a_t|s_t)}$$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$r_t(\theta) &amp;gt; 1$ 이면, 새로운 정책이 해당 행동을 할 확률이 더 높다는 의미.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;$r_t(\theta) &amp;lt; 1$ 이면, 새로운 정책이 해당 행동을 할 확률이 더 낮다는 의미.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\hat{A}_t$:&amp;nbsp;&lt;b&gt;어드밴티지 함수(Advantage Function)&lt;/b&gt;로, 특정 상태에서 특정 행동을 하는 것이 평균적으로 얼마나 더 좋았는지를 나타내는 값임.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$\hat{A}_t &amp;gt; 0$ 이면, 평균보다 좋은 행동.&lt;/li&gt;
&lt;li&gt;$\hat{A}_t &amp;lt; 0$ 이면, 평균보다 나쁜 행동.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\epsilon$: &lt;b&gt;클리핑 하이퍼 파라미터.&amp;nbsp;&lt;/b&gt;0.1 또는 0.2 같은 작은 값을 사용한다고 함. 정책이 얼마나 변할 수 있는지를 제한하는 역할을 함.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이 목적 함수가 어떻게 작동하는지에 대해서는 아래 두가지 경우로 나눌 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. 어드밴티지가 양수일 때 ($\hat{A}_t &amp;gt; 0$)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, 과거에 했던 행동이 평균보다 &lt;b&gt;좋은 결과&lt;/b&gt;를 냈을 때.&lt;/li&gt;
&lt;li&gt;따라서 우리는 이 행동을 할 확률($r_t(\theta)$)을 높이고 싶음.&lt;/li&gt;
&lt;li&gt;하지만 PPO는 $r_t(\theta)$가 $1 + \epsilon$ 이상으로 너무 커지는 것을 막는다. 정책이 한 번에 너무 낙관적으로 변해서 학습이 불안정해지는 것을 방지한다.&lt;/li&gt;
&lt;li&gt;목적 함수는 $r_t(\theta) \hat{A}_t$ 와 $(1 + \epsilon) \hat{A}_t$ 중 &lt;b&gt;더 작은 값&lt;/b&gt;을 선택하게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2. 어드밴티지가 음수일 때 ($\hat{A}_t &amp;lt; 0$)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, 과거에 했던 행동이 평균보다 &lt;b&gt;나쁜 결과&lt;/b&gt;를 냈을 때.&lt;/li&gt;
&lt;li&gt;따라서 우리는 이 행동을 할 확률($r_t(\theta)$)을 낮추고 싶음.&lt;/li&gt;
&lt;li&gt;PPO는 $r_t(\theta)$가 $1 - \epsilon$ 이하로 너무 작아지는 것을 막는다. 좋은 행동을 찾을 기회를 완전히 잃어버리는 비관적인 업데이트를 막기 위함.&lt;/li&gt;
&lt;li&gt;목적 함수는 $r_t(\theta) \hat{A}_t$ 와 $(1 - \epsilon) \hat{A}_t$ 중 &lt;b&gt;더 큰 값&lt;/b&gt;을 선택하게 된다. (어드밴티지가 음수이므로, 전체 값은 더 큰 쪽이 덜 감소하게 됨.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로, PPO의 클리핑 메커니즘은 정책 업데이트에 &lt;b&gt;안전장치&lt;/b&gt;를 거는 것과 같다. 어드밴티지가 긍정적이든 부정적이든, 정책이 한 번에 너무 크게 변하는 것을 막아 &lt;b&gt;안정적인 학습&lt;/b&gt;을 가능하게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;PPO 알고리즘의 전체 구조&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PPO는 보통 &lt;b&gt;액터-크리틱(Actor-Critic)&lt;/b&gt; 구조를 기반으로 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;액터(Actor):&lt;/b&gt; 정책($\pi_\theta$)을 나타내며, 현재 상태를 보고 어떤 행동을 할지 결정함. PPO의 클리핑된 목적 함수를 통해 업데이트된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;크리틱(Critic):&lt;/b&gt; 가치 함수($V_\phi$)를 나타내며, 현재 상태의 가치(얼마나 좋은 상태인지)를 평가한다. 이는 어드밴티지($\hat{A}_t$)를 계산하는 데 사용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습 과정은 다음과 같이 진행.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;현재 정책($\pi_{\theta_{old}}$)을 사용하여 여러 에피소드를 실행하며 데이터(상태, 행동, 보상 등)를 수집.&lt;/li&gt;
&lt;li&gt;수집된 데이터를 사용하여 각 타임스텝의 **어드밴티지($\hat{A}_t$)**를 계산.&lt;/li&gt;
&lt;li&gt;클리핑된 목적 함수를 최대화하는 방향으로 정책(액터)의 파라미터 $\theta$를 여러 번 업데이트.&lt;/li&gt;
&lt;li&gt;동시에 가치 함수(크리틱)의 예측 오차를 최소화하도록 파라미터 $\phi$를 업데이트.&lt;/li&gt;
&lt;li&gt;1~4 과정을 반복.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;PPO의 장점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;구현의 단순함&lt;/b&gt;: TRPO와 달리 복잡한 2차 미분 계산이 필요 없어 코드로 구현하기가 훨씬 쉽다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;안정적인 성능&lt;/b&gt;: 다양한 환경에서 견고하고 안정적인 성능을 보여준다. 하이퍼파라미터 튜닝에 덜 민감한 편.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 효율성&lt;/b&gt;: 수집한 데이터를 여러 번의 업데이트(multiple epochs)에 재사용하여 학습 효율을 높인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 장점들 덕분에 PPO는 현재 강화학습 분야에서 가장 널리 사용되는 알고리즘 중 하나가 되었다. 복잡한 문제를 해결하기 위한 베이스라인(baseline) 알고리즘으로 자주 채택된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Schulman,&amp;nbsp;J.,&amp;nbsp;Wolski,&amp;nbsp;F.,&amp;nbsp;Dhariwal,&amp;nbsp;P.,&amp;nbsp;Radford,&amp;nbsp;A.,&amp;nbsp;&amp;amp;&amp;nbsp;Klimov,&amp;nbsp;O.&amp;nbsp;(2017).&amp;nbsp;*Proximal&amp;nbsp;Policy&amp;nbsp;Optimization&amp;nbsp;Algorithms*.&amp;nbsp;Retrieved&amp;nbsp;from&amp;nbsp;&lt;a href=&quot;https://arxiv.org/abs/1707.06347&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/abs/1707.06347&lt;/a&gt;&lt;/p&gt;</description>
      <category>논문 리뷰</category>
      <author>lidarmansiwon</author>
      <guid isPermaLink="true">https://lidarmansiwon.tistory.com/11</guid>
      <comments>https://lidarmansiwon.tistory.com/11#entry11comment</comments>
      <pubDate>Wed, 3 Sep 2025 01:12:32 +0900</pubDate>
    </item>
    <item>
      <title>[논문 리뷰] USV Formation Path Planning Based on Behavior Trees and Fast Marching Method</title>
      <link>https://lidarmansiwon.tistory.com/10</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script&gt; MathJax = { tex: {inlineMath: [['$', '$']]} }; &lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 논문 자료&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논문 링크: &lt;a title=&quot;[논문 링크]&quot; href=&quot;https://ieeexplore.ieee.org/document/10170942&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ieeexplore.ieee.org/document/10170942&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1749110682588&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;USV Formation Path Planning Based on Behavior Trees and Fast Marching Method&quot; data-og-description=&quot;In the deployment of multiple Unmanned Surface Vehicles (USVs) for collaborative operation, path planning is a crucial component. This paper addresses the path planning problem for USV formations operating in complex marine environments and proposes a mult&quot; data-og-host=&quot;ieeexplore.ieee.org&quot; data-og-source-url=&quot;https://ieeexplore.ieee.org/document/10170942&quot; data-og-url=&quot;https://ieeexplore.ieee.org/document/10170942&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bP1YzU/hyY0w0NEXA/cSfUZi0GZswPl0kSvZGUJ0/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200,https://scrap.kakaocdn.net/dn/c5V0zI/hyY33bCt9M/pKxZAOudjeDZlJqGgUUKQ0/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200&quot;&gt;&lt;a href=&quot;https://ieeexplore.ieee.org/document/10170942&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ieeexplore.ieee.org/document/10170942&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bP1YzU/hyY0w0NEXA/cSfUZi0GZswPl0kSvZGUJ0/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200,https://scrap.kakaocdn.net/dn/c5V0zI/hyY33bCt9M/pKxZAOudjeDZlJqGgUUKQ0/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;USV Formation Path Planning Based on Behavior Trees and Fast Marching Method&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;In the deployment of multiple Unmanned Surface Vehicles (USVs) for collaborative operation, path planning is a crucial component. This paper addresses the path planning problem for USV formations operating in complex marine environments and proposes a mult&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ieeexplore.ieee.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ 위 논문 리뷰는 온전히 개인이 공부하면서 정리한 글입니다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 논문 알아보기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 논문 주제 및 목적&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 논문은 다중 무인수상선박(USV) 편대 운항 시 협력적 작전을 위한 경로 계획(Path Planning)을 다룬다. 특히 좁고 복잡한 해상 환경에서 USV 편대의 운항 경로를 효율적으로 계획하기 위해 행동트리(Behavior Tree, BT)와 &lt;b&gt;Fast Marching Method (FMM)&lt;/b&gt; 를 결합한 편대 경로 계획 방법론을 제안한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 제안한 방법의 특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-start=&quot;289&quot; data-end=&quot;393&quot;&gt;&lt;b&gt;적응형 편대 라이브러리 (Adaptive Formation Library)&lt;/b&gt;: FMM을 기반으로 속도 퍼텐셜 필드(velocity potential field)를 활용하여 형성.&lt;/li&gt;
&lt;li data-start=&quot;289&quot; data-end=&quot;393&quot;&gt;&lt;b&gt;행동트리 기반 장애물 회피 제어 전략&lt;/b&gt;: 행동트리를 사용하여 상황에 따른 효율적인 제어 전략을 도출함.&lt;/li&gt;
&lt;li data-start=&quot;289&quot; data-end=&quot;393&quot;&gt;&lt;b&gt;복잡한 해상 환경&lt;/b&gt;에서도 높은 성능을 발휘하며, 특히 좁은 해협에서 장애물 회피 성능이 우수함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.3 연구 배경 및 중요성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;USV&amp;nbsp;편대는&amp;nbsp;군사적,&amp;nbsp;민간적&amp;nbsp;용도에서&amp;nbsp;항구&amp;nbsp;순찰,&amp;nbsp;해안&amp;nbsp;감시,&amp;nbsp;환경&amp;nbsp;모니터링,&amp;nbsp;무인&amp;nbsp;운송&amp;nbsp;등&amp;nbsp;다양한&amp;nbsp;분야에서&amp;nbsp;활용된다.&amp;nbsp;편대&amp;nbsp;경로&amp;nbsp;계획은&amp;nbsp;미션&amp;nbsp;수행&amp;nbsp;시작점에서&amp;nbsp;목표&amp;nbsp;지점까지&amp;nbsp;안전하고&amp;nbsp;최적의&amp;nbsp;경로를&amp;nbsp;결정하여&amp;nbsp;효율적이며&amp;nbsp;충돌&amp;nbsp;위험을&amp;nbsp;최소화하는&amp;nbsp;데&amp;nbsp;중요한&amp;nbsp;역할을&amp;nbsp;한다.&amp;nbsp;기존&amp;nbsp;경로&amp;nbsp;계획&amp;nbsp;알고리즘으로는&amp;nbsp;Dijkstra,&amp;nbsp;A*,&amp;nbsp;PSO,&amp;nbsp;ACO,&amp;nbsp;유전자&amp;nbsp;알고리즘(GA),&amp;nbsp;APF,&amp;nbsp;FMS&amp;nbsp;등이&amp;nbsp;연구되었으며,&amp;nbsp;특히&amp;nbsp;FMS는&amp;nbsp;국부&amp;nbsp;최소&amp;nbsp;문제를&amp;nbsp;해결하고&amp;nbsp;연속성&amp;nbsp;및&amp;nbsp;부드러운&amp;nbsp;경로&amp;nbsp;생성&amp;nbsp;능력에서&amp;nbsp;강점을&amp;nbsp;가지고&amp;nbsp;있다.&amp;nbsp;FMM은&amp;nbsp;계산&amp;nbsp;속도가&amp;nbsp;빠르고&amp;nbsp;실시간&amp;nbsp;경로&amp;nbsp;생성이&amp;nbsp;가능해,&amp;nbsp;복잡한&amp;nbsp;해상&amp;nbsp;환경에서&amp;nbsp;USV의&amp;nbsp;실시간&amp;nbsp;운항에&amp;nbsp;적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.4 행동트리의 장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모듈화(Modularity), 확장성(Scalability), 결함 내성(Fault tolerance) 측면에서 탁월한 성능을 제공한다.&lt;/li&gt;
&lt;li&gt;협력 제어, 충돌 회피, 유연성과 적응성을 보장하는 전략으로 유용하게 활용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 논문 기술 요소&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 이론적 모델 (Theoretical Model)&lt;/h3&gt;
&lt;h4 data-end=&quot;124&quot; data-start=&quot;91&quot; data-ke-size=&quot;size20&quot;&gt;A. Fast Marching Method (FMM)&lt;/h4&gt;
&lt;p data-end=&quot;205&quot; data-start=&quot;126&quot; data-ke-size=&quot;size16&quot;&gt;Fast Marching Method(FMM)는 Eikonal 방정식을 해결하기 위해 제안된 방법으로, 다음과 같은 형태의 방정식을 해결한다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-end=&quot;205&quot; data-start=&quot;126&quot; data-ke-size=&quot;size16&quot;&gt;$|\nabla T(x,y)| \cdot V(x,y) = 1$&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;205&quot; data-start=&quot;126&quot; data-ke-size=&quot;size16&quot;&gt;여기서 $T(x,y)$ 는 좌표 $(x,y)$ 에서의 파면 도달 시간(wave arrival time)을 의미하며, $V(x,y)$ 는 파면 전파 속도를 나타낸다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;205&quot; data-start=&quot;126&quot; data-ke-size=&quot;size16&quot;&gt;특정 지점 $(x_0,y_0)$에서의 경계 조건은 $T(x_0,y_0) = 0$으로 주어진다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;205&quot; data-start=&quot;126&quot; data-ke-size=&quot;size16&quot;&gt;위 식을 upwind 차분법을 사용하여 풀면 다음과 같은 형태가 된다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;205&quot; data-start=&quot;126&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;$\max\left(\frac{T - T_1}{\Delta x}, 0\right)^2 + \max\left(\frac{T - T_2}{\Delta y}, 0\right)^2 = \frac{1}{V^2(x,y)}$&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 $T_1$과 $T_2$는 현재 격자점의 주변 격자점 중 최소 도달 시간을 나타내며, 각각 다음과 같이 표현된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;$T_1 = \min\left(T_{(x-\Delta x,y)},\, T_{(x+\Delta x,y)}\right)$&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;$T_2 = \min\left(T_{(x,y-\Delta y)},\, T_{(x,y+\Delta y)}\right)$&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;829&quot; data-start=&quot;790&quot; data-ke-size=&quot;size16&quot;&gt;이 방정식을 $T$ 에 관해 명시적으로 풀게 된다.&amp;nbsp;이렇게 구한 도달시간 행렬 $T$를 기반으로, 경사 하강법(Gradient Descent Method)을 이용하여 시작점에서 목표점까지의 최적 경로를 결정한다.&lt;/p&gt;
&lt;p data-end=&quot;1200&quot; data-start=&quot;1111&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;1200&quot; data-start=&quot;1111&quot; data-ke-size=&quot;size20&quot;&gt;B. Fast Marching Square Method (FMS)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 FMM은 생성된 경로가 장애물과 지나치게 가까워 안전성 문제를 야기할 수 있다. 따라서 안전성을 높이기 위해 &lt;b&gt;Fast Marching Square Method(FMS)&lt;/b&gt;가 제안되었으며, FMS는 FMM을 두 번 연속 적용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FMS 알고리즘 과정:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 초기 이진 맵(binary map)을 생성한다. (흰색: 이동 가능 공간, 검정색: 장애물)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 첫 번째 FMM을 적용하여 장애물로부터 퍼지는 파면을 통해 속도 퍼텐셜 맵(velocity potential) $W(x)$를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 두 번째 FMM을 목표점에서부터 적용하여, 도달시간 퍼텐셜 필드(arrival time potential) $D(x)$를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 생성된 $D(x)$를 바탕으로 경사 하강법(아래 4.1 참고)을 적용하여 시작점에서 목표점까지 최적 경로를 얻는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 과정은 장애물에 너무 가까운 경로가 생성되는 문제를 방지하며, 결과적으로 무인수상선(USV)의 운항 안전성을 높인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;514&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lpeh5/btsOrqbHCo9/W9YafnV1COgMpQezJksPI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lpeh5/btsOrqbHCo9/W9YafnV1COgMpQezJksPI0/img.png&quot; data-alt=&quot;출처 [1]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lpeh5/btsOrqbHCo9/W9YafnV1COgMpQezJksPI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flpeh5%2FbtsOrqbHCo9%2FW9YafnV1COgMpQezJksPI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;514&quot; height=&quot;390&quot; data-origin-width=&quot;514&quot; data-origin-height=&quot;390&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 [1]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 그림 (Fig. 1) 설명:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(a) &lt;b&gt;초기 이진 맵&lt;/b&gt;: 장애물과 이동 가능 공간을 구분한 상태를 나타냄.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(b) &lt;b&gt;속도 퍼텐셜 필드 &amp;amp;W(x)&amp;amp;&lt;/b&gt;: 장애물로부터 거리에 따라 파면이 전파된 상태를 나타냄.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(c) &lt;b&gt;도달시간 퍼텐셜 필드 &amp;amp;D(x)&amp;amp;&lt;/b&gt;: 목표점으로부터 파면이 전파되어 생성된 도달시간을 표현한 상태.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(d) &lt;b&gt;최적 경로 생성 결과&lt;/b&gt;: (c)의 도달시간 퍼텐셜 필드를 이용하여 경사 하강법을 통해 결정된 최종 경로.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;C. 행동 트리 (Behavior Trees)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행동 트리(Behavior Trees)의 핵심 개념은 복잡한 작업을 보다 작은 하위 작업(subtask)들로 분해하여 이를 조합하고 조율하여 전체 작업을 수행하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행동 트리는 &lt;b&gt;실행 노드(execution nodes)&lt;/b&gt;와 &lt;b&gt;제어 노드(control nodes)&lt;/b&gt;로 구성된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행 노드(Execution nodes): 리프(leaf) 노드*이며, 조건 노드(condition node)와 행동 노드(action node)로 구분된다.&lt;/li&gt;
&lt;li&gt;제어 노드(Control nodes): 내부 노드로 논리와 작업 전환을 담당하며, 주로 다음과 같은 유형을 포함한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;순차(sequence) 노드&lt;/b&gt;: 자식 노드를 순서대로 실행하며, 모든 자식 노드가 성공해야 성공을 반환하고, 하나라도 실패하면 실패를 반환하고 실행을 중단한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;선택(selector) 노드&lt;/b&gt;: 자식 노드를 차례대로 실행하며, 하나의 자식 노드라도 성공하면 성공을 반환하고 실행을 중단하며, 모든 자식 노드가 실패해야 실패를 반환한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;병렬(parallel) 노드&lt;/b&gt;: 여러 자식 노드를 동시에 실행할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* &lt;b&gt;리프 노드(Leaf Node)&lt;/b&gt; 란 트리 구조에서 더 이상 하위 노드를 가지지 않는 &lt;b&gt;마지막(끝) 노드&lt;/b&gt;를 의미&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행동 트리의 기본 노드 유형은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순차 노드 (Sequence node): 자식 노드를 순차적으로 실행함.&lt;/li&gt;
&lt;li&gt;선택 노드 (Selector node): 자식 노드 중 하나라도 성공하면 성공을 반환함.&lt;/li&gt;
&lt;li&gt;조건 노드 (Condition node): 특정 조건의 만족 여부를 판단하여 성공 혹은 실패를 반환함.&lt;/li&gt;
&lt;li&gt;행동 노드 (Action node): 행동 트리의 리프 노드로 특정 작업을 수행하고 성공 또는 실패를 반환함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 USV 편대 장애물 회피 경로 계획&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;A. Fast Marching Method 기반 적응형 편대 라이브러리 (Adaptive Formation Library)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경 제약 조건 아래에서 무인수상선박(USV) 편대가 장애물을 회피하기 위해 형상을 유연하게 조정할 수 있도록, 편대 라이브러리를 구축하고, FMM(Fast Marching Method)의 속도 퍼텐셜 필드를 활용하여 적응적인 형상 변화를 달성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;편대의 관계를 나타내는 매트릭스(편대의&amp;nbsp;각도,&amp;nbsp;거리&amp;nbsp;등&amp;nbsp;상대적&amp;nbsp;형상&amp;nbsp;정보를&amp;nbsp;나타낸&amp;nbsp;행렬)는 다음과 같이 정의된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;$ E=[R L\varphi]^T $&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여기서 $R = [R_1, R_2, R_3, \dots, R_n]$은 USV의 식별 지시자(identity indicators)를 나타낸다.&lt;/li&gt;
&lt;li&gt;$L = [l_1, l_2, l_3, \dots, l_n]$은 원하는 편대 간격(desired formation distance)을 나타낸다.&lt;/li&gt;
&lt;li&gt;$\varphi= [\varphi_1, \varphi_2, \varphi_3, \dots, \varphi_n]$는 원하는 편대 형상 각도(desired formation angle)를 나타낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;즉, 각 행은 리더로부터 특정 USV까지의 편대 번호, 거리($l_i$), 각도($\varphi_i$)로 구성됨.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;USV 편대의 주요 형상은 삼각형(triangular), 열(columnar), 선형(linear) 등으로 구분할 수 있으며, 다중 USV 편대 제어를 위한 편대 라이브러리는 다음과 같은 행렬 집합으로 표현된다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;$E=({E i | E j,E c,E t})$&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;각각의 형상 파라미터 행렬은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt; 선형 편대(linear formation)&lt;/b&gt; $E_j$:&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;$E_j&amp;nbsp;=&amp;nbsp;\begin{bmatrix} &lt;br /&gt;1&amp;nbsp;&amp;amp;&amp;nbsp;0&amp;nbsp;&amp;amp;&amp;nbsp;0&amp;nbsp;\\ &lt;br /&gt;2&amp;nbsp;&amp;amp;&amp;nbsp;l_i&amp;nbsp;&amp;amp;&amp;nbsp;\frac{\pi}{2}&amp;nbsp;\\ &lt;br /&gt;\vdots&amp;nbsp;&amp;amp;&amp;nbsp;\vdots&amp;nbsp;&amp;amp;&amp;nbsp;\vdots&amp;nbsp;\\ &lt;br /&gt;i&amp;nbsp;&amp;amp;&amp;nbsp;l_i&amp;nbsp;&amp;amp;&amp;nbsp;\pi(\sqrt{i}+1) &lt;br /&gt;\end{bmatrix}$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt; 열 편대(column formation)&lt;/b&gt; $E_c$:&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;$E_c&amp;nbsp;=&amp;nbsp;\begin{bmatrix} &lt;br /&gt;1&amp;nbsp;&amp;amp;&amp;nbsp;0&amp;nbsp;&amp;amp;&amp;nbsp;0&amp;nbsp;\\ &lt;br /&gt;2&amp;nbsp;&amp;amp;&amp;nbsp;l_i&amp;nbsp;&amp;amp;&amp;nbsp;\pi&amp;nbsp;\\ &lt;br /&gt;\vdots&amp;nbsp;&amp;amp;&amp;nbsp;\vdots&amp;nbsp;&amp;amp;&amp;nbsp;\vdots&amp;nbsp;\\ &lt;br /&gt;i&amp;nbsp;&amp;amp;&amp;nbsp;l_i&amp;nbsp;&amp;amp;&amp;nbsp;\pi &lt;br /&gt;\end{bmatrix}$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;삼각형 편대(triangular formation)&lt;/b&gt; $E_t$:&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;$E_t&amp;nbsp;=&amp;nbsp;\begin{bmatrix} &lt;br /&gt;1&amp;nbsp;&amp;amp;&amp;nbsp;0&amp;nbsp;&amp;amp;&amp;nbsp;0&amp;nbsp;\\ &lt;br /&gt;2&amp;nbsp;&amp;amp;&amp;nbsp;l_i&amp;nbsp;&amp;amp;&amp;nbsp;\frac{3\pi}{4}&amp;nbsp;\\ &lt;br /&gt;\vdots&amp;nbsp;&amp;amp;&amp;nbsp;\vdots&amp;nbsp;&amp;amp;&amp;nbsp;\vdots&amp;nbsp;\\ &lt;br /&gt;i&amp;nbsp;&amp;amp;&amp;nbsp;l_i&amp;nbsp;&amp;amp;&amp;nbsp;\frac{\pi}{2}\left(\frac{3}{2}+\sqrt{i}\right) &lt;br /&gt;\end{bmatrix}$&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;행렬 $E_j, E_c, E_t$에 있는 값 중 편대 간격 $l_i$만 지정해주면, 나머지 각도 값들은 이미 행렬 내에서 고정적으로 정의되어 있기 때문에, 자동으로 그림 2와 같은 &lt;b&gt;기본 편대 형태&lt;/b&gt;가 형성되는 구조이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행렬 편대 형태 간격($l$) 이외의 각도($\varphi$) 값의 특징&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;$E_j$&lt;/td&gt;
&lt;td&gt;선형 편대(linear formation)&lt;/td&gt;
&lt;td&gt;각도가 $\pi/2$ (90&amp;deg;)로 일정하게 설정됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$E_c$&lt;/td&gt;
&lt;td&gt;열 편대(column formation)&lt;/td&gt;
&lt;td&gt;각도가 $\pi$ (180&amp;deg;)로 일정하게 설정됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$E_t$&lt;/td&gt;
&lt;td&gt;삼각형 편대(triangular formation)&lt;/td&gt;
&lt;td&gt;각도가 약 135&amp;deg;(3&amp;pi;/4) 등으로 설정되어 삼각 형태 유지&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;439&quot; data-origin-height=&quot;202&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9xdlZ/btsOq92Dwaz/POLZ0yjA0LReMwCKmhH3kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9xdlZ/btsOq92Dwaz/POLZ0yjA0LReMwCKmhH3kk/img.png&quot; data-alt=&quot;출처 [1]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9xdlZ/btsOq92Dwaz/POLZ0yjA0LReMwCKmhH3kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9xdlZ%2FbtsOq92Dwaz%2FPOLZ0yjA0LReMwCKmhH3kk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;439&quot; height=&quot;202&quot; data-origin-width=&quot;439&quot; data-origin-height=&quot;202&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 [1]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논문에서 소개된 편대 형상 라이브러리의 기본 형상은 Figure 2와 같이 나타낼 수 있으며, 이를 기반으로 환경 변화에 따라 적응형 편대 변경과 장애물 회피가 가능하다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;형상 행렬 라이브러리 $E$를 만드려면 환경 제약에 따라 다중 USV 형성 변화 및 장애물 회피의 토대를 마련해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FMM velocity potential field와 결합하여 환경의 적응 형성 변화를 달성 할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행렬 변환에 의해, 리더에 대한 각도 및 거리 행렬은 글로벌 좌표계에서 위치 행렬로 변환되어 다음 USV 궤적 추적의 문제를 해결한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;추가 설명:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. 기본적인 개념 정리&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논문에서는 편대의 상대적 형상 적보(각도, 거리 등)를 나타낸 행렬을 가지고 있다. 이 상대적인 형상 정보는 일반적으로 편대 리더(leader)를 기준으로 한 상대 좌표계에서 정의된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 운항 중인 USV(무인수상선박)들은 현실 세계(global coordinate system)에 위치하고 있다.&lt;br /&gt;따라서 &lt;b&gt;상대 좌표계에서 정의된 편대 형상 정보&lt;/b&gt;를 실제 &lt;b&gt;글로벌 좌표계에서의 각 USV 위치로 변환&lt;/b&gt;할 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 글로벌 좌표계 변환이 필요한 이유:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에는 리더를 기준으로 하는 &lt;b&gt;상대적 편대 형상 정보&lt;/b&gt;(거리와 각도)를 가지고 있다. 예를 들어, 두 번째 USV가 리더로부터 거리 $l_i$만큼 떨어져 있고, 특정 각도 $\varphi_i$로 위치한다는 정보가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 경로 계획 및 제어를 수행하기 위해서는 이 상대적 정보가 아닌, 지도(map)나 GPS와 같은 글로벌 좌표계를 기준으로 한 위치로 변환되어야 한다. 이를 통해 각 USV가 실제로 목표 경로를 따라 안정적으로 움직일 수 있도록 제어 명령을 내릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;글로벌 좌표계에서 위치행렬(F) 생성 과정:&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;$F&amp;nbsp;=&amp;nbsp;\begin{bmatrix}&lt;br /&gt;R_1&amp;nbsp;&amp;amp;&amp;nbsp;x_1&amp;nbsp;&amp;amp;&amp;nbsp;y_1&amp;nbsp;\\&lt;br /&gt;R_2&amp;nbsp;&amp;amp;&amp;nbsp;x_2&amp;nbsp;&amp;amp;&amp;nbsp;y_2&amp;nbsp;\\&lt;br /&gt;\vdots&amp;nbsp;&amp;amp;&amp;nbsp;\vdots&amp;nbsp;&amp;amp;&amp;nbsp;\vdots&amp;nbsp;\\&lt;br /&gt;R_i&amp;nbsp;&amp;amp;&amp;nbsp;x_i&amp;nbsp;&amp;amp;&amp;nbsp;y_i&lt;br /&gt;\end{bmatrix}$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 행렬 $F$는 편대에 속한 각 USV의 실제 글로벌 좌표계에서 위치 정보를 담고 있다.&lt;/li&gt;
&lt;li&gt;각 행의 첫 번째 요소인 $R_i$는 USV의 ID (번호)를 의미하고, 두 번째와 세 번째 요소 $(x_i, y_i)$는 그 USV의 글로벌 좌표를 나타낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;형상 라이브러리 행렬&lt;/b&gt; $F$ 는 다음 절차로 계산된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(1) 리더의 글로벌 좌표계 위치 ($x_1, y_1$)를 초기화하여 행렬 $F$의 첫 번째 행에 저장한다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;$F&amp;nbsp;=&amp;nbsp;[R_1,&amp;nbsp;x_1,&amp;nbsp;y_1]$&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;155&quot; data-start=&quot;74&quot; data-ke-size=&quot;size16&quot;&gt;(2) 이후 팔로워(follower)들의 상대적 위치를 글로벌 좌표로 변환하여 저장한다. &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;$i$번째 추종자(follower)의 전역 좌표계(global coordinate)에서 위치 $[x_i,y_i]$를 다음 단계로 계산한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;155&quot; data-start=&quot;74&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;155&quot; data-start=&quot;74&quot; data-ke-size=&quot;size16&quot;&gt;팔로워의 위치를 구하는 과정:&lt;/p&gt;
&lt;p data-end=&quot;155&quot; data-start=&quot;74&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;a. 속도 퍼텐셜 필드(velocity potential field) $W_i$ 를 사용하여 편대 간격을 조정한다. (편대 간격을 속도 퍼텐셜 필드 값 $W_i$에 따라 조정):&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;$l_i^e&amp;nbsp;=&amp;nbsp;l_i^*&amp;nbsp;W_i$&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;b. 속도 퍼텐셜 필드 값 $W_i$를 기반으로 편대의 각도를 조정한다:&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;$\varphi_i^c = \varphi_i * (1 - W_i) + \varphi_i^t * W_i$&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;여기서, $\varphi_i^c$는 조정된 편대 형상 각도이다. 이때 각도는 리더를 기준으로 한 상대 좌표계(local coordinate system) 기준이다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;192&quot; data-start=&quot;157&quot;&gt;$\varphi_i$ : 기존 편대 형상의 각도 (원래 목표로 설정된 각도)&lt;/li&gt;
&lt;li data-end=&quot;192&quot; data-start=&quot;157&quot;&gt;$\varphi_i^t$ : 장애물을 만났을 때 적응적으로 조정된 목표 편대의 각도 (장애물 회피 등을 위해 수정된 각도)&lt;/li&gt;
&lt;li data-end=&quot;192&quot; data-start=&quot;157&quot;&gt;$W_i$ : 속도 퍼텐셜 필드(Velocity potential field)의 값으로, 환경 정보를 반영하여 0~1 사이의 값을 가진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;첨자 $c$ (&lt;b&gt;current&lt;/b&gt;의 약자):&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;192&quot; data-start=&quot;157&quot;&gt;$\varphi_i^c$는 현재(Current)의 편대 형상 각도를 나타낸다.&lt;/li&gt;
&lt;li data-end=&quot;192&quot; data-start=&quot;157&quot;&gt;즉, 현재 실제 USV 편대가 유지하고 있는, 혹은 가지고 있는 형상의 각도를 의미&lt;/li&gt;
&lt;li data-end=&quot;192&quot; data-start=&quot;157&quot;&gt;환경 변화(장애물 등)가 없었다면 원래 설정한 각도를 유지하고 있을 테니, 기존의 각도를 기반으로 환경 변화에 따라 현재 편대가 가진 실제 각도를 표현할 때 사용하는 첨자&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첨자 $t$ (&lt;b&gt;target&lt;/b&gt;의 약자):&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$\varphi_i^t$는 목표(Target) 편대 형상 각도를 나타낸다.&lt;/li&gt;
&lt;li&gt;장애물을 회피하거나 형상을 변경할 필요가 있을 때, 앞으로 변경하고자 하는 목표 형상 각도&lt;/li&gt;
&lt;li&gt;장애물 회피 등 환경 변화가 발생할 때 새로운 목표 형상을 결정하여, 편대가 점차 이 목표 형상 각도로 바뀌어 가는 과정에서 사용되는 첨자&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 각 팔로워가 리더를 기준으로 한 로컬 좌표계(local coordinate system)에서 어디에 위치할지 결정하는 단계:&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;c. 리더를 기준으로 한 로컬 좌표계에서 팔로워의 상대 위치 벡터 $P_i$를 계산한다:&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;$P_i = [l_i^e \cdot \cos(\varphi_i^e),\quad l_i^e \cdot \sin(\varphi_i^e)]^T$&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;192&quot; data-start=&quot;157&quot;&gt;$l_i^e$: 리더와 팔로워 사이의 거리 (편대 간격을 환경에 따라 조정한 값)&lt;/li&gt;
&lt;li data-end=&quot;192&quot; data-start=&quot;157&quot;&gt;$\varphi_i^e$: 리더의 진행 방향(로컬 좌표계의 x축)을 기준으로 팔로워가 위치할 상대적 방향 각도&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 결과로 얻어지는 $P_i$는 팔로워가 리더로부터 &lt;b&gt;x축(리더 진행 방향)으로 얼마나 떨어져 있는지&lt;/b&gt;와 &lt;b&gt;y축(리더 진행 방향의 수직 방향)으로 얼마나 떨어져 있는지&lt;/b&gt;를 알려주는 벡터이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;d. 로컬 좌표계에서 얻은 위치 벡터를 전역 좌표계로 변환하여 전역 좌표상의 위치를 얻는다(각 에이전트의 실제 위치를 얻는 과정):&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;$[x_i, y_i]^T = [x_1, y_1]^T + R(\alpha) \cdot P_i$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;192&quot; data-start=&quot;157&quot;&gt;$[x_1, y_1]^T$: 리더의 실제 전역 좌표 (GPS 또는 지도 상의 절대 좌표)&lt;/li&gt;
&lt;li data-end=&quot;192&quot; data-start=&quot;157&quot;&gt;$R(\alpha)$: 로컬 좌표계를 글로벌 좌표계로 바꿔주는 회전 변환 행렬&lt;/li&gt;
&lt;li data-end=&quot;192&quot; data-start=&quot;157&quot;&gt;$\alpha$: 리더가 실제 전역 좌표계에서 향하고 있는 방향 각도 (리더가 바라보는 글로벌 좌표계에서의 절대 방향)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;여기서, 좌표 회전 변환 행렬(rotation matrix) $R(\alpha)$는 다음과 같다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;$R(\alpha)&amp;nbsp;=&amp;nbsp;\begin{bmatrix} &lt;br /&gt;\cos(\alpha)&amp;nbsp;&amp;amp;&amp;nbsp;-\sin(\alpha)\\[6pt] &lt;br /&gt;\sin(\alpha)&amp;nbsp;&amp;amp;&amp;nbsp;\cos(\alpha) &lt;br /&gt;\end{bmatrix}$&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size18&quot;&gt;물리적 의미와 예시:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;192&quot; data-start=&quot;157&quot;&gt;로컬 좌표계에서 팔로워가 $P_i=[10,0]^T$라면 리더의 방향으로부터 정면으로 10m 떨어진 위치에 있다는 뜻.&lt;/li&gt;
&lt;li data-end=&quot;192&quot; data-start=&quot;157&quot;&gt;하지만 실제 전역 좌표계에서 리더가 $(100,200)$ 위치에 있고, 리더의 방향이 90&amp;deg;(전역 좌표계에서 북쪽 방향)이라면, 팔로워의 글로벌 위치는 회전 행렬로 인해 다음과 같이 변환된다:&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;$[x_i,y_i]^T&amp;nbsp;=&amp;nbsp;[100,200]^T&amp;nbsp;+&amp;nbsp;R(90^\circ)\cdot[10,0]^T&amp;nbsp;=&amp;nbsp;[100,200]^T&amp;nbsp;+&amp;nbsp; &lt;br /&gt;\begin{bmatrix} &lt;br /&gt;0&amp;nbsp;&amp;amp;&amp;nbsp;-1&amp;nbsp;\\ &lt;br /&gt;1&amp;nbsp;&amp;amp;&amp;nbsp;0 &lt;br /&gt;\end{bmatrix}\cdot[10,0]^T&amp;nbsp;=&amp;nbsp;[100,200]^T&amp;nbsp;+&amp;nbsp;[0,10]^T&amp;nbsp;=&amp;nbsp;[100,210]^T$&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;즉, 로컬 좌표에서 리더의 정면으로 10m 떨어져 있던 팔로워는, 리더가 실제로 북쪽을 바라보며 $(100,200)$ 지점에 위치할 때 글로벌 좌표계상으로 $(100,210)$ 위치에 놓이게 된다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;(3) 계산된 추종자의 전역 위치 $[x_i,y_i]$를 전역 좌표계 행렬 $F$에 추가한다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;전체 편대 라이브러리 행렬의 일반적인 형태는 다음과 같다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;$F&amp;nbsp;=&amp;nbsp;\begin{bmatrix} &lt;br /&gt;R_1&amp;nbsp;&amp;amp;&amp;nbsp;x_1&amp;nbsp;&amp;amp;&amp;nbsp;y_1&amp;nbsp;\\ &lt;br /&gt;R_2&amp;nbsp;&amp;amp;&amp;nbsp;x_2&amp;nbsp;&amp;amp;&amp;nbsp;y_2&amp;nbsp;\\ &lt;br /&gt;\vdots&amp;nbsp;&amp;amp;&amp;nbsp;\vdots&amp;nbsp;&amp;amp;&amp;nbsp;\vdots&amp;nbsp;\\ &lt;br /&gt;R_i&amp;nbsp;&amp;amp;&amp;nbsp;x_i&amp;nbsp;&amp;amp;&amp;nbsp;y_i &lt;br /&gt;\end{bmatrix}$&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;삼각형 형상 라이브러리 행렬의 예시는 다음과 같이 표현된다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;$F&amp;nbsp;=&amp;nbsp;\begin{bmatrix} &lt;br /&gt;R_1&amp;nbsp;&amp;amp;&amp;nbsp;x_1&amp;nbsp;&amp;amp;&amp;nbsp;y_1&amp;nbsp;\\[6pt] &lt;br /&gt;2&amp;nbsp;&amp;amp;&amp;nbsp;x_1&amp;nbsp;-&amp;nbsp;lW_2(\cos\alpha&amp;nbsp;-&amp;nbsp;\sin\alpha)&amp;nbsp;&amp;amp;&amp;nbsp;y_1&amp;nbsp;-&amp;nbsp;lW_2(\cos\alpha&amp;nbsp;+&amp;nbsp;\sin\alpha)&amp;nbsp;\\[6pt] &lt;br /&gt;3&amp;nbsp;&amp;amp;&amp;nbsp;x_1&amp;nbsp;+&amp;nbsp;lW_3(\cos\alpha&amp;nbsp;-&amp;nbsp;\sin\alpha)&amp;nbsp;&amp;amp;&amp;nbsp;y_1&amp;nbsp;-&amp;nbsp;lW_3(\cos\alpha&amp;nbsp;+&amp;nbsp;\sin\alpha) &lt;br /&gt;\end{bmatrix}$&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;192&quot; data-start=&quot;157&quot;&gt;$R_i$ : 각 USV를 식별하는 인덱스 (ID)&lt;/li&gt;
&lt;li data-end=&quot;192&quot; data-start=&quot;157&quot;&gt;$(x_i,y_i)$ : 글로벌 좌표계에서의 i번째 USV의 실제 위치 좌표&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-end=&quot;192&quot; data-start=&quot;157&quot; data-ke-size=&quot;size20&quot;&gt;B. 행동 트리 기반 편대 형상 변환 제어 전략&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;771&quot; data-origin-height=&quot;771&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crRAMm/btsOqr3S3Ng/OPosvAL0StAGkE3xzUKUSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crRAMm/btsOqr3S3Ng/OPosvAL0StAGkE3xzUKUSk/img.png&quot; data-alt=&quot;출처 [1]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crRAMm/btsOqr3S3Ng/OPosvAL0StAGkE3xzUKUSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrRAMm%2FbtsOqr3S3Ng%2FOPosvAL0StAGkE3xzUKUSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;488&quot; height=&quot;488&quot; data-origin-width=&quot;771&quot; data-origin-height=&quot;771&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 [1]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행동트리 기반 형상 변환 전략은 모듈성 및 계층적 구조를 특징으로 한다. 복잡한 장애물 환경에서 편대를 최적화하여 장애물을 회피한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;192&quot; data-start=&quot;157&quot;&gt;Figure 3과 같이 상황에 따라(한쪽 또는 양쪽에 장애물 존재) 적절한 하위 작업(subtask)을 선택하여 편대 형상을 조정하고, 장애물을 회피하여 좁은 환경을 통과할 수 있도록 한다.&lt;/li&gt;
&lt;li data-end=&quot;192&quot; data-start=&quot;157&quot;&gt;적시에 형상 조정을 수행함으로써 장애물을 효과적으로 회피하고 작업의 안전한 수행을 보장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;형상 장애물 회피 행동트리 전략의 구체적인 단계:&lt;/p&gt;
&lt;p data-end=&quot;2085&quot; data-start=&quot;1889&quot; data-ke-size=&quot;size16&quot;&gt;행동트리의 루트 노드는 장애물 회피 순차 노드(sequence node)이며, 자식 노드를 순차적으로 실행한다. 먼저 환경 인식 노드는 이동 가능한 영역과 원하는 경로(desired route)를 확인하고, 경로가 장애물 사이에 있을 경우 확장 노드(action node)를 실행하여 스케일링 팩터(scaling factor) $\rho$를 계산한다.&lt;/p&gt;
&lt;p data-end=&quot;2168&quot; data-start=&quot;2087&quot; data-ke-size=&quot;size16&quot;&gt;그림 3의 경우, 장애물 사이에서 편대 너비가 $D_{unit}$보다 작을 때 형상 조정이 필요하며, 이때 스케일링 팩터는 다음과 같이 정의된다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-end=&quot;2168&quot; data-start=&quot;2087&quot; data-ke-size=&quot;size16&quot;&gt;$\rho=D_{max}/D$&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-end=&quot;2168&quot; data-start=&quot;2087&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2258&quot; data-start=&quot;2215&quot; data-ke-size=&quot;size16&quot;&gt;여기서 $D_{max}$는 통과 가능한 경로의 최대 너비를 나타내고 $D$는 형성의 최대 너비를 나타낸다.&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2258&quot; data-start=&quot;2215&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2258&quot; data-start=&quot;2215&quot; data-ke-size=&quot;size16&quot;&gt;formation transformation selection node는 scaling factor $\rho$를 분석하고 obstacle avoidance formation transformation mode (장애물 회피 형성 변환 모드)를 결정하여 다음 세가지 동작 노드 중 하나를 입력한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2258&quot; data-start=&quot;2215&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2320&quot; data-start=&quot;2260&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(a)&lt;/b&gt; $\rho \geq 1$인 경우:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2320&quot; data-start=&quot;2260&quot;&gt;편대 변환이 필요하지 않은 형상 유지(zero transformation) 액션 노드가 활성화된다.&lt;/li&gt;
&lt;li data-end=&quot;2320&quot; data-start=&quot;2260&quot;&gt;편대는 형상을 변경하지 않고 장애물을 통과할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(b)&lt;/b&gt; $\rho_m &amp;lt; \rho &amp;lt; 1$인 경우 (여기서 $\rho_m = l_{min}/D$):&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동일 형상 축소(isomorphic transformation) 액션 노드가 활성화된다.&lt;/li&gt;
&lt;li data-end=&quot;2320&quot; data-start=&quot;2260&quot;&gt;이 경우, 원래의 편대 형상을 유지한 채 편대의 크기를 축소하여 장애물을 통과한다.&lt;/li&gt;
&lt;li data-end=&quot;2320&quot; data-start=&quot;2260&quot;&gt;새로운 형상 매트릭스의 간격(distance parameter)은 다음 식으로 결정된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;$l_i^d&amp;nbsp;=&amp;nbsp;l_i&amp;nbsp;\times&amp;nbsp;\rho$&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(c)&lt;/b&gt; $0 \leq \rho \leq \rho_m$인 경우:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2320&quot; data-start=&quot;2260&quot;&gt;다른 형상으로 전환(heteromorphic transformation) 액션 노드가 활성화된다.&lt;/li&gt;
&lt;li data-end=&quot;2320&quot; data-start=&quot;2260&quot;&gt;이때, 형상 적응 비용 함수(formation adaptation cost function)는 다음 식과 같이 계산된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;$S_{fd}(F_c,F_t)&amp;nbsp;=&amp;nbsp;\frac{D_{max}}{D}$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;편대 라이브러리로부터 가장 적합한 목표 편대(target formation)를 선택한다.&lt;/li&gt;
&lt;li&gt;이후 다른 형상으로 전환하는 액션 노드와 경로 재계획(route replanning) 액션 노드를 수행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이러한 방식으로 USV 편대는 상황에 따라 장애물을 효과적으로 회피하며 안전하고 효율적인 경로를 유지할 수 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;624&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dMdoY8/btsOr1bxZ76/gNt34uv6fZIAq5tesN7Brk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dMdoY8/btsOr1bxZ76/gNt34uv6fZIAq5tesN7Brk/img.png&quot; data-alt=&quot;출처 [1]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dMdoY8/btsOr1bxZ76/gNt34uv6fZIAq5tesN7Brk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdMdoY8%2FbtsOr1bxZ76%2FgNt34uv6fZIAq5tesN7Brk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;505&quot; height=&quot;533&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;624&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 [1]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ 위 논문 리뷰는 온전히 개인이 공부하면서 정리한 글입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[1] H. Cao, R. Song, J. Xu, X. Hu, Z. Bao, and L. Bao, &quot;USV formation path planning based on behavior trees and fast marching method,&quot; in 2023 5th International Conference on Intelligent Control, Measurement and Signal Processing (ICMSP), Chengdu, China, May 2023, doi: 10.1109/ICMSP58539.2023.10170942.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 추가 학습 내용&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 경사 하강법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경사 하강법(Gradient Descent)은 최적화 문제에서 자주 사용되는 반복적(iterative) 수치 해법이다. 주로 어떤 함수의 값을 최소화하거나 최대화할 때 쓰인다. 일반적으로 머신러닝에서 비용 함수(Cost function)의 최소값을 찾는 데 널리 활용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기본 원리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경사 하강법은 함수의 그래디언트(Gradient)를 활용한다. 그래디언트는 함수가 가장 급격히 증가하는 방향을 가리키는 벡터로서, 경사 하강법은 이 벡터의 반대 방향으로 조금씩 이동하면서 함수의 최솟값을 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 $f(x)$를 최소화하는 경우, 경사 하강법의 갱신 규칙은 다음과 같다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;$x_{new}&amp;nbsp;=&amp;nbsp;x_{old}&amp;nbsp;-&amp;nbsp;\eta&amp;nbsp;\nabla&amp;nbsp;f(x_{old})$&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$x_{new}$ : 새롭게 갱신된 변수 값&lt;/li&gt;
&lt;li&gt;$x_{old}$ : 이전 단계의 변수 값&lt;/li&gt;
&lt;li&gt;$\eta$ : 학습률(Learning rate)이라고 하며, 한 번에 이동하는 보폭을 의미&lt;/li&gt;
&lt;li&gt;$\nabla f(x_{old})$ : $f(x)$의 미분값(그래디언트)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;학습률(Learning rate, $\eta$)의 역할&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습률은 매우 중요한 하이퍼파라미터로, 적절한 값을 설정하는 것이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;학습률이 너무 클 때&lt;/b&gt;:&lt;br /&gt;최솟값을 건너뛰어 오히려 발산할 위험이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;학습률이 너무 작을 때&lt;/b&gt;:&lt;br /&gt;최적점에 도달하는 데 시간이 너무 오래 걸리거나, 국부적 최소점(local minima)에 빠지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>논문 리뷰</category>
      <category>behavior tree</category>
      <category>FMM</category>
      <category>FMS</category>
      <category>path planning</category>
      <category>USV</category>
      <category>usv formation</category>
      <category>usv formation path planning based on behavior trees and fast marching method</category>
      <category>군집</category>
      <category>논문 리뷰</category>
      <category>의사 결정</category>
      <author>lidarmansiwon</author>
      <guid isPermaLink="true">https://lidarmansiwon.tistory.com/10</guid>
      <comments>https://lidarmansiwon.tistory.com/10#entry10comment</comments>
      <pubDate>Thu, 5 Jun 2025 20:15:29 +0900</pubDate>
    </item>
    <item>
      <title>[ROS2 GPS, IMU 항법 코드] ROS2 기반 GPS + IMU 항법 시스템 구현</title>
      <link>https://lidarmansiwon.tistory.com/9</link>
      <description>&lt;h2 data-end=&quot;161&quot; data-start=&quot;154&quot; data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-end=&quot;310&quot; data-start=&quot;163&quot; data-ke-size=&quot;size16&quot;&gt;이 글에서는 ROS 2 기반 시스템에서 GPS(Global Positioning System)와 IMU(Inertial Measurement Unit) 센서 데이터를 활용하여 선박 또는 로봇의 위치 및 자세 정보를 계산하고 항법 메시지로 변환하는 과정을 다룹니다.&lt;/p&gt;
&lt;p data-end=&quot;310&quot; data-start=&quot;163&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;321&quot; data-start=&quot;312&quot; data-ke-size=&quot;size26&quot;&gt;사용 목적&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;427&quot; data-start=&quot;323&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;375&quot; data-start=&quot;323&quot;&gt;GPS와 IMU 센서를 기반으로 자율운항 선박이나 로봇의 실시간 위치 및 자세 정보를 추정&lt;/li&gt;
&lt;li data-end=&quot;427&quot; data-start=&quot;376&quot;&gt;추정된 정보를 바탕으로 Guidance &amp;amp; Control 알고리즘에 필요한 데이터를 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-end=&quot;445&quot; data-start=&quot;434&quot; data-ke-size=&quot;size26&quot;&gt;이론 설명&lt;/h2&gt;
&lt;h3 data-end=&quot;479&quot; data-start=&quot;447&quot; data-ke-size=&quot;size23&quot;&gt;1. GPS 좌표계를 UTM 좌표계로 변환하는 이유&lt;/h3&gt;
&lt;h4 data-end=&quot;497&quot; data-start=&quot;481&quot; data-ke-size=&quot;size20&quot;&gt;GPS 좌표계의 한계&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;619&quot; data-start=&quot;498&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;561&quot; data-start=&quot;498&quot;&gt;GPS는 &lt;b&gt;WGS84&lt;/b&gt; 기반의 &lt;b&gt;위도(latitude), 경도(longitude)&lt;/b&gt; 정보를 제공합니다.&lt;/li&gt;
&lt;li data-end=&quot;619&quot; data-start=&quot;562&quot;&gt;이는 &lt;b&gt;구면 좌표계&lt;/b&gt; 기반이므로 &lt;b&gt;거리 계산&lt;/b&gt;, &lt;b&gt;상대 위치 추정&lt;/b&gt;에 어려움이 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;637&quot; data-start=&quot;621&quot; data-ke-size=&quot;size20&quot;&gt;UTM 좌표계의 장점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;853&quot; data-start=&quot;638&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;745&quot; data-start=&quot;638&quot;&gt;&lt;b&gt;UTM (Universal Transverse Mercator)&lt;/b&gt; 좌표계는 지구를 60개의 존(Zone)으로 나누고, 각 존에서 &lt;b&gt;2차원 직교 좌표계 (X, Y)&lt;/b&gt; 로 변환합니다.&lt;/li&gt;
&lt;li data-end=&quot;790&quot; data-start=&quot;746&quot;&gt;이를 통해 &lt;b&gt;미터 단위의 거리&lt;/b&gt;, &lt;b&gt;상대 위치 계산&lt;/b&gt;이 용이해집니다.&lt;/li&gt;
&lt;li data-end=&quot;853&quot; data-start=&quot;791&quot;&gt;예: &quot;현재 위치가 기준 위치에서 동쪽으로 24.5m, 북쪽으로 17.2m 떨어져 있음&quot;과 같은 계산이 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-end=&quot;927&quot; data-start=&quot;855&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;927&quot; data-start=&quot;857&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;따라서 GPS로 받은 위도/경도 값을 UTM으로 변환하면, 로컬 좌표계를 기준으로 상대 위치를 쉽게 계산할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;951&quot; data-start=&quot;934&quot; data-ke-size=&quot;size23&quot;&gt;2. IMU 센서의 역할&lt;/h3&gt;
&lt;p data-end=&quot;996&quot; data-start=&quot;953&quot; data-ke-size=&quot;size16&quot;&gt;IMU는 &lt;b&gt;3축 가속도계&lt;/b&gt;와 &lt;b&gt;3축 자이로스코프&lt;/b&gt;를 포함한 센서입니다.&lt;/p&gt;
&lt;h4 data-end=&quot;1009&quot; data-start=&quot;998&quot; data-ke-size=&quot;size20&quot;&gt;주요 데이터&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1121&quot; data-start=&quot;1010&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1041&quot; data-start=&quot;1010&quot;&gt;&lt;b&gt;orientation&lt;/b&gt;: 자세 (쿼터니언 형태)&lt;/li&gt;
&lt;li data-end=&quot;1079&quot; data-start=&quot;1042&quot;&gt;&lt;b&gt;angular velocity&lt;/b&gt;: 회전 속도 (rad/s)&lt;/li&gt;
&lt;li data-end=&quot;1121&quot; data-start=&quot;1080&quot;&gt;&lt;b&gt;linear acceleration&lt;/b&gt;: 가속도 (optional)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;1133&quot; data-start=&quot;1123&quot; data-ke-size=&quot;size20&quot;&gt;사용 이유&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1231&quot; data-start=&quot;1134&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1172&quot; data-start=&quot;1134&quot;&gt;GPS는 위치는 정확하지만 자세나 회전 정보를 제공하지 않습니다.&lt;/li&gt;
&lt;li data-end=&quot;1231&quot; data-start=&quot;1173&quot;&gt;IMU는 실시간 회전 각도(yaw)와 회전 속도(r)를 추정할 수 있어, GPS를 보완합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-end=&quot;1292&quot; data-start=&quot;1233&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;1292&quot; data-start=&quot;1235&quot; data-ke-size=&quot;size16&quot;&gt;항법 시스템에서 IMU는 선박의 방향(&amp;psi;), 회전 속도(r), 가속도 등 자세 정보를 제공합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-end=&quot;1313&quot; data-start=&quot;1299&quot; data-ke-size=&quot;size26&quot;&gt;코드 기반 설명&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1355&quot; data-start=&quot;1315&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이제 위의 이론을 바탕으로 실제 ROS 2 C++ 코드 구현 내용을 살펴보겠습니다. 아래에 소개할 코드는 GitHub 저장소 &lt;a href=&quot;https://github.com/lidarmansiwon/macro_gnc&quot;&gt;macro_gnc&lt;/a&gt;에서 확인할 수 있으며, 항법 데이터를 생성하는 핵심 모듈 중 하나인 gps_navigation.cpp 파일에 해당합니다. 이 코드는 ROS 2 노드로 동작하며, GPS 및 IMU 센서로부터 받은 데이터를 바탕으로 선박 혹은 무인이동체의 위치 및 자세 정보를 실시간으로 계산하여 퍼블리시합니다.&lt;/p&gt;
&lt;figure id=&quot;og_1749048447074&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - lidarmansiwon/macro_gnc&quot; data-og-description=&quot;Contribute to lidarmansiwon/macro_gnc development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/lidarmansiwon/macro_gnc&quot; data-og-url=&quot;https://github.com/lidarmansiwon/macro_gnc&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/biaNR0/hyY5hgjaem/lq73hsxtPKPA5pxKLsCu40/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/lidarmansiwon/macro_gnc&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/lidarmansiwon/macro_gnc&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/biaNR0/hyY5hgjaem/lq73hsxtPKPA5pxKLsCu40/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - lidarmansiwon/macro_gnc&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to lidarmansiwon/macro_gnc development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-end=&quot;1355&quot; data-start=&quot;1315&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;693&quot; data-start=&quot;446&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;특히 속도 계산 부분에는 navigation/tool/velocity_calculator.hpp 파일이 사용됩니다. 이 모듈은 이동체의 위치 및 자세 변화율로부터 선속(u), 횡속(v), 그리고 요레이크(r)를 계산하며, 저역통과 필터(Low-Pass Filter, LPF)를 적용하여 노이즈가 많은 센서 데이터로부터 안정적인 속도 추정치를 제공합니다. 이 필터 계수는 파라미터로 조정할 수 있어 상황에 맞는 튜닝이 가능합니다.&lt;/p&gt;
&lt;p data-end=&quot;754&quot; data-start=&quot;695&quot; data-ke-size=&quot;size16&quot;&gt;이제 gps_navigation.cpp 코드의 주요 구성 요소와 각 기능에 대해 자세히 살펴보겠습니다.&lt;/p&gt;
&lt;h3 data-end=&quot;1377&quot; data-start=&quot;1357&quot; data-ke-size=&quot;size23&quot;&gt;초기화 및 파라미터 설정&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1749047082576&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;this-&amp;gt;declare_parameter&amp;lt;std::string&amp;gt;(&quot;imu_topic&quot;, &quot;/imu_topic&quot;);
this-&amp;gt;declare_parameter&amp;lt;std::string&amp;gt;(&quot;gps_topic&quot;, &quot;/gps_topic&quot;);
this-&amp;gt;declare_parameter&amp;lt;std::string&amp;gt;(&quot;navigation_topic&quot;, &quot;/navigation_data&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1627&quot; data-start=&quot;1599&quot;&gt;사용자 정의 토픽 이름을 파라미터로 설정합니다.&lt;/li&gt;
&lt;li data-end=&quot;1697&quot; data-start=&quot;1628&quot;&gt;reference_gps_latitude, reference_gps_longitude는 상대 좌표의 기준점입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Parameter 값으로 전달 받은 filter 강도 전달&lt;/h3&gt;
&lt;pre id=&quot;code_1749047524094&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  double velocity_alpha = this-&amp;gt;get_parameter(&quot;velocity_alpha&quot;).as_double();
  vel_calc_ = VelocityCalculator(velocity_alpha);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;IMU 데이터를 활용한 자세 계산&lt;/h3&gt;
&lt;pre id=&quot;code_1749047199494&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;geometry_msgs::msg::Quaternion quat = imu_data_-&amp;gt;orientation;
std::array&amp;lt;double, 3&amp;gt; rpy = euler_from_quaternion({quat.x, quat.y, quat.z, quat.w});
double yaw = rpy[2];&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2160&quot; data-start=&quot;2124&quot;&gt;쿼터니언 &amp;rarr; 롤/피치/요(roll, pitch, yaw) 변환&lt;/li&gt;
&lt;li data-end=&quot;2202&quot; data-start=&quot;2161&quot;&gt;항법에서 가장 중요한 건 &lt;b&gt;yaw (&amp;psi;)&lt;/b&gt;, 즉 선박의 방향입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Pub, Sub 객체 정의 및 timer 정의&lt;/h3&gt;
&lt;pre id=&quot;code_1749047567438&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  // 생성자에서 시작 표시.
  RCLCPP_INFO(this-&amp;gt;get_logger(), &quot;Run GPS Navigation&quot;);

  imu_subscription_ = this-&amp;gt;create_subscription&amp;lt;sensor_msgs::msg::Imu&amp;gt;(
    imu_topic, rclcpp::SensorDataQoS(), std::bind(&amp;amp;GPSNavigation::imu_callback, this, _1));

  gps_subscription_ = this-&amp;gt;create_subscription&amp;lt;sensor_msgs::msg::NavSatFix&amp;gt;(
    gps_topic, rclcpp::SensorDataQoS(), std::bind(&amp;amp;GPSNavigation::gps_callback, this, _1));

  navigation_publisher_ = this-&amp;gt;create_publisher&amp;lt;mk3_msgs::msg::NavigationType&amp;gt;(
    navigation_topic, 10);

  timer_ = this-&amp;gt;create_wall_timer(50ms, std::bind(&amp;amp;GPSNavigation::process, this));&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;생성자에서 노드를 처음 시작할 때, RCLCPP_INFO 로그 기능을 통하여 시작을 알립니다.&lt;/li&gt;
&lt;li&gt;&amp;nbsp;전달 받을 센서 GPS, IMU에 대한 Subscribe 코드와 최종 계산된 항법 데이터를 Publish 할 코드를 작성합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;timer를 통하여 50ms(20Hz) 간격으로 함수를 실행합니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;GPSNavigation::process() 함수 상세 설명&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 센서 데이터 유효성 검사&lt;/h4&gt;
&lt;pre id=&quot;code_1749047851551&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (!gps_data_ || !imu_data_)
{
  RCLCPP_WARN_THROTTLE(this-&amp;gt;get_logger(), *this-&amp;gt;get_clock(), 3000, &quot;GPS or IMU data not received yet.&quot;);
  return;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;423&quot; data-start=&quot;360&quot;&gt;&lt;b&gt;목적&lt;/b&gt;: GPS 또는 IMU 데이터가 아직 수신되지 않은 경우 경고 메시지를 출력하고 함수를 종료합니다.&lt;/li&gt;
&lt;li data-end=&quot;496&quot; data-start=&quot;424&quot;&gt;&lt;b&gt;기능&lt;/b&gt;: ROS 2에서는 센서 콜백이 비동기로 도착하므로, 해당 데이터가 nullptr인지 확인하여 안정성을 확보합니다.&lt;/li&gt;
&lt;li data-end=&quot;567&quot; data-start=&quot;497&quot;&gt;&lt;b&gt;기능 강조&lt;/b&gt;: RCLCPP_WARN_THROTTLE은 3초에 한 번씩만 경고를 출력하여 로그 과부하를 방지합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. GPS 좌표 &amp;rarr; UTM 좌표 변환&lt;/h4&gt;
&lt;pre id=&quot;code_1749047892458&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double latitude = gps_data_-&amp;gt;latitude;
double longitude = gps_data_-&amp;gt;longitude;
...
GeographicLib::UTMUPS::Forward(latitude, longitude, zone, northp, utm_x, utm_y);&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;836&quot; data-start=&quot;776&quot;&gt;&lt;b&gt;목적&lt;/b&gt;: 위도(latitude)와 경도(longitude)를 &lt;b&gt;UTM 평면좌표계&lt;/b&gt;로 변환합니다.&lt;/li&gt;
&lt;li data-end=&quot;929&quot; data-start=&quot;837&quot;&gt;&lt;b&gt;사용 라이브러리&lt;/b&gt;: &lt;a href=&quot;https://geographiclib.sourceforge.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-end=&quot;907&quot; data-start=&quot;853&quot;&gt;GeographicLib&lt;/a&gt;의 UTMUPS::Forward 함수&lt;/li&gt;
&lt;li data-end=&quot;929&quot; data-start=&quot;837&quot;&gt;&lt;b&gt;입력값 &lt;/b&gt;: latitude, longitude&lt;/li&gt;
&lt;li data-end=&quot;1046&quot; data-start=&quot;930&quot;&gt;&lt;b&gt;출력값 &lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1046&quot; data-start=&quot;946&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;975&quot; data-start=&quot;946&quot;&gt;utm_x, utm_y: 변환된 평면 좌표&lt;/li&gt;
&lt;li data-end=&quot;1001&quot; data-start=&quot;978&quot;&gt;zone: UTM 존 (6도 간격)&lt;/li&gt;
&lt;li data-end=&quot;1046&quot; data-start=&quot;1004&quot;&gt;northp: 북반구 여부 (남반구일 경우 UTM 보정 방식이 다름)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1749047944320&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;RCLCPP_INFO(this-&amp;gt;get_logger(), &quot;Converted GPS (%.8f, %.8f) &amp;rarr; UTM (%.2f, %.2f), Zone: %d, Northp: %d\n&quot;, latitude, longitude, utm_x, utm_y, zone, northp);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;디버깅 정보 출력&lt;/b&gt;: 실제 GPS 좌표가 어떻게 변환되었는지 로그에 출력하여 추적 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 기준점 설정 및 상대 좌표 계산&lt;/h4&gt;
&lt;pre id=&quot;code_1749047963857&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (!origin_set_) {
    ...
    GeographicLib::UTMUPS::Forward(reference_gps_latitude_, reference_gps_longitude_, reference_zone, reference_northp, reference_utm_x, reference_utm_y);
    ...
    origin_set_ = true;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1597&quot; data-start=&quot;1531&quot;&gt;&lt;b&gt;목적&lt;/b&gt;: 기준 GPS 좌표(reference)를 최초 한 번만 UTM으로 변환하여 고정된 원점으로 사용합니다.&lt;/li&gt;
&lt;li data-end=&quot;1697&quot; data-start=&quot;1598&quot;&gt;&lt;b&gt;기능 설명&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1697&quot; data-start=&quot;1613&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1654&quot; data-start=&quot;1613&quot;&gt;origin_set_ 플래그는 기준점이 한 번만 설정되도록 합니다.&lt;/li&gt;
&lt;li data-end=&quot;1697&quot; data-start=&quot;1657&quot;&gt;일반적으로 &lt;b&gt;로컬 좌표계&lt;/b&gt;를 만들기 위한 원점 설정에 해당합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1749047984431&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;double relative_x = utm_x - reference_utm_x;
double relative_y = utm_y - reference_utm_y;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1876&quot; data-start=&quot;1800&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1846&quot; data-start=&quot;1800&quot;&gt;&lt;b&gt;목적&lt;/b&gt;: UTM 좌표계 상에서 기준점으로부터의 상대적인 위치를 계산합니다.&lt;/li&gt;
&lt;li data-end=&quot;1876&quot; data-start=&quot;1847&quot;&gt;&lt;b&gt;결과&lt;/b&gt;: 로컬 UTM 좌표 (x, y) 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. 자세 정보 추출 (IMU &amp;rarr; Roll, Pitch, Yaw)&lt;/h4&gt;
&lt;pre id=&quot;code_1749048009632&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;geometry_msgs::msg::Quaternion quat = imu_data_-&amp;gt;orientation;
std::array&amp;lt;double, 3&amp;gt; rpy = euler_from_quaternion({quat.x, quat.y, quat.z, quat.w});
double roll = rpy[0], pitch = rpy[1], yaw = rpy[2];&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2214&quot; data-start=&quot;2134&quot;&gt;&lt;b&gt;목적&lt;/b&gt;: IMU로부터 쿼터니언(quaternion) 형태의 자세 정보를 받아서 오일러각(Roll, Pitch, Yaw)으로 변환합니다.&lt;/li&gt;
&lt;li data-end=&quot;2269&quot; data-start=&quot;2215&quot;&gt;&lt;b&gt;사용 함수&lt;/b&gt;: euler_from_quaternion() (사용자 정의 함수로 추정)&lt;/li&gt;
&lt;li data-end=&quot;2333&quot; data-start=&quot;2270&quot;&gt;&lt;b&gt;중요한 값&lt;/b&gt;: yaw는 선박의 방향(heading)을 의미하는 각도로, 이후 속도 계산에 활용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5. 속도 계산 (velocity_calculator 사용)&lt;/h4&gt;
&lt;pre id=&quot;code_1749048031209&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;auto result = vel_calc_.update(x, y, psi, time_sec);
double LPFVel_x = result.LPFVel_x;
double LPFVel_y = result.LPFVel_y;
double angular_velocity = result.angular_velocity;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2629&quot; data-start=&quot;2563&quot;&gt;&lt;b&gt;목적&lt;/b&gt;: 위치 변화와 자세 변화량을 기반으로 선속(u), 횡속(v), 회전 속도(r)를 계산합니다.&lt;/li&gt;
&lt;li data-end=&quot;2699&quot; data-start=&quot;2630&quot;&gt;&lt;b&gt;사용 클래스&lt;/b&gt;: velocity_calculator.hpp에 정의된 VelocityCalculator 클래스&lt;/li&gt;
&lt;li data-end=&quot;2792&quot; data-start=&quot;2700&quot;&gt;&lt;b&gt;핵심 알고리즘&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2792&quot; data-start=&quot;2717&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2756&quot; data-start=&quot;2717&quot;&gt;이전 상태와 현재 상태 간 시간차(dt)를 사용하여 속도를 계산&lt;/li&gt;
&lt;li data-end=&quot;2792&quot; data-start=&quot;2759&quot;&gt;저역통과필터(LPF)를 통해 노이즈 제거 및 평활화 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2891&quot; data-start=&quot;2793&quot;&gt;&lt;b&gt;출력 항목&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2891&quot; data-start=&quot;2808&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2830&quot; data-start=&quot;2808&quot;&gt;LPFVel_x: 선속 (u)&lt;/li&gt;
&lt;li data-end=&quot;2855&quot; data-start=&quot;2833&quot;&gt;LPFVel_y: 횡속 (v)&lt;/li&gt;
&lt;li data-end=&quot;2891&quot; data-start=&quot;2858&quot;&gt;angular_velocity: 회전 속도 (r)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6. 최종 메시지 생성 및 퍼블리시&lt;/h4&gt;
&lt;pre id=&quot;code_1749048048608&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;NavigationType nav_msg;
nav_msg.x = x;
nav_msg.y = y;
nav_msg.psi = psi * 180.0 / M_PI;
nav_msg.u = LPFVel_x;
nav_msg.v = LPFVel_y;
nav_msg.r = angular_velocity;
nav_msg.w = imu_data_-&amp;gt;angular_velocity.z;

navigation_publisher_-&amp;gt;publish(nav_msg);&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3235&quot; data-start=&quot;3180&quot;&gt;&lt;b&gt;목적&lt;/b&gt;: 계산된 위치, 자세, 속도를 NavigationType 메시지 형태로 퍼블리시&lt;/li&gt;
&lt;li data-end=&quot;3394&quot; data-start=&quot;3236&quot;&gt;&lt;b&gt;메시지 필드 설명&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3394&quot; data-start=&quot;3255&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3286&quot; data-start=&quot;3255&quot;&gt;x, y: 로컬 UTM 좌표계 기준 상대 위치&lt;/li&gt;
&lt;li data-end=&quot;3321&quot; data-start=&quot;3289&quot;&gt;psi: 선박의 방향각 (라디안 &amp;rarr; 도 단위 변환)&lt;/li&gt;
&lt;li data-end=&quot;3359&quot; data-start=&quot;3324&quot;&gt;u, v, r: 각각 선속, 횡속, 요 회전 속도&lt;/li&gt;
&lt;li data-end=&quot;3394&quot; data-start=&quot;3362&quot;&gt;w: IMU에서 측정된 z축 각속도 (원시 데이터)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;3409&quot; data-start=&quot;3401&quot; data-ke-size=&quot;size26&quot;&gt;종합 요약&lt;/h2&gt;
&lt;p data-end=&quot;3595&quot; data-start=&quot;3411&quot; data-ke-size=&quot;size16&quot;&gt;이 함수는 GPS와 IMU 데이터를 융합하여 &lt;b&gt;이동체의 현재 위치, 자세, 속도 정보를 계산&lt;/b&gt;한 후, 이를 다른 노드에서 사용할 수 있도록 퍼블리시하는 핵심적인 항법 처리 로직입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;velocity_calculator.hpp 설명&lt;/h3&gt;
&lt;h4 data-end=&quot;165&quot; data-start=&quot;153&quot; data-ke-size=&quot;size20&quot;&gt;1. 목적&lt;/h4&gt;
&lt;p data-end=&quot;341&quot; data-start=&quot;166&quot; data-ke-size=&quot;size16&quot;&gt;velocity_calculator.hpp는 &lt;b&gt;위치 및 자세 정보의 시간 변화율을 통해 속도&lt;/b&gt;를 계산하기 위한 유틸리티 클래스입니다.&lt;br /&gt;ROS 2 시스템에서 GPS와 IMU를 기반으로 수집된 위치(x, y) 및 방향(psi: 요각)을 기반으로 선속(u), 횡속(v), 각속도(r)를 추정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 핵심 원리: 수치 미분(Numerical Differentiation)&lt;/h4&gt;
&lt;pre id=&quot;code_1749048188583&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dx = x - prev_x_;
dy = y - prev_y_;
dpsi = normalize_angle(psi - prev_psi_);
dt = current_time - prev_time_;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;803&quot; data-start=&quot;520&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;644&quot; data-start=&quot;520&quot;&gt;고전적인 &lt;b&gt;1차 수치 미분법&lt;/b&gt;(forward difference)을 이용해 속도를 추정합니다.&lt;span&gt;&lt;span&gt;&lt;span aria-hidden=&quot;true&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;803&quot; data-start=&quot;646&quot;&gt;이를 바탕으로 선속(u), 횡속(v), 회전속도(r)를 계산:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;803&quot; data-start=&quot;691&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;735&quot; data-start=&quot;691&quot;&gt;u = (dx * cos(psi) + dy * sin(psi)) / dt&lt;/li&gt;
&lt;li data-end=&quot;783&quot; data-start=&quot;738&quot;&gt;v = (-dx * sin(psi) + dy * cos(psi)) / dt&lt;/li&gt;
&lt;li data-end=&quot;803&quot; data-start=&quot;786&quot;&gt;r = dpsi / dt&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 구현이 간단하고 실시간으로 사용하기 적합하나, &lt;b&gt;센서 노이즈에 민감&lt;/b&gt;하다는 단점이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 노이즈 보완: 저역통과 필터(Low-Pass Filter)&lt;/h4&gt;
&lt;pre id=&quot;code_1749048229241&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LPFVel_x = LPF_alpha * vel_x + (1 - LPF_alpha) * LPFVel_x;
LPFVel_y = LPF_alpha * vel_y + (1 - LPF_alpha) * LPFVel_y;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고주파 노이즈(잡음)를 줄이기 위해 저역통과 필터(LPF)를 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LPF_alpha는 필터 계수로, 0.0~1.0 사이의 값입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1269&quot; data-start=&quot;1196&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1221&quot; data-start=&quot;1196&quot;&gt;1에 가까울수록 새 데이터 반영률이 높고,&lt;/li&gt;
&lt;li data-end=&quot;1269&quot; data-start=&quot;1224&quot;&gt;0에 가까울수록 이전 데이터 반영률이 높아져 더 부드럽지만 반응이 느려집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;1307&quot; data-start=&quot;1276&quot; data-ke-size=&quot;size20&quot;&gt;4. 한계점: 미분 기반 속도 추정의 문제점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1494&quot; data-start=&quot;1309&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1347&quot; data-start=&quot;1309&quot;&gt;&lt;b&gt;GPS나 위치 센서는 노이즈가 포함된 데이터를 제공합니다.&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1452&quot; data-start=&quot;1348&quot;&gt;&lt;b&gt;수치 미분&lt;/b&gt;은 노이즈를 증폭시킵니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1452&quot; data-start=&quot;1376&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1417&quot; data-start=&quot;1376&quot;&gt;데이터가 순간적으로 튈 경우, 계산된 속도 값도 크게 튈 수 있습니다.&lt;/li&gt;
&lt;li data-end=&quot;1452&quot; data-start=&quot;1420&quot;&gt;dt가 작을수록 더 민감해지므로 주의가 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1494&quot; data-start=&quot;1453&quot;&gt;LPF만으로는 &lt;b&gt;동적 환경 변화에 적응하거나 예측이 불가능&lt;/b&gt;합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;1546&quot; data-start=&quot;1501&quot; data-ke-size=&quot;size20&quot;&gt;5. 개선 방향: 칼만 필터(Kalman Filter)를 활용한 보정&lt;/h4&gt;
&lt;p data-end=&quot;1608&quot; data-start=&quot;1548&quot; data-ke-size=&quot;size16&quot;&gt;많은 연구자들은 속도 추정을 더 정밀하게 하기 위해 &lt;b&gt;Kalman Filter (KF)&lt;/b&gt; 를 적용합니다.&lt;/p&gt;
&lt;h4 data-end=&quot;1635&quot; data-start=&quot;1610&quot; data-ke-size=&quot;size20&quot;&gt;Kalman Filter 기본 개념:&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1765&quot; data-start=&quot;1636&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1683&quot; data-start=&quot;1636&quot;&gt;센서 노이즈를 모델링하고, 현재 상태(위치, 속도 등)를 추정하는 확률 기반 필터&lt;/li&gt;
&lt;li data-end=&quot;1733&quot; data-start=&quot;1684&quot;&gt;&lt;b&gt;예측(Predict)&lt;/b&gt; 단계 + &lt;b&gt;관측 업데이트(Update)&lt;/b&gt; 단계를 반복&lt;/li&gt;
&lt;li data-end=&quot;1765&quot; data-start=&quot;1734&quot;&gt;시간에 따라 누적되는 오차를 &lt;b&gt;수학적으로 최소화&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항법 요소 데이터 출처 설명&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;위치 (x, y)&lt;/td&gt;
&lt;td&gt;GPS (UTM 변환 후)&lt;/td&gt;
&lt;td&gt;기준 GPS 대비 상대 위치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;자세 (&amp;psi;)&lt;/td&gt;
&lt;td&gt;IMU&lt;/td&gt;
&lt;td&gt;방향 추정 (yaw)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;선형 속도 (u, v)&lt;/td&gt;
&lt;td&gt;위치 변화량 기반&lt;/td&gt;
&lt;td&gt;LPF 적용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;각속도 (r, w)&lt;/td&gt;
&lt;td&gt;위치 변화량 + IMU&lt;/td&gt;
&lt;td&gt;yaw 변화량 기반 또는 자이로 직접 측정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-end=&quot;3147&quot; data-start=&quot;3136&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;3147&quot; data-start=&quot;3136&quot; data-ke-size=&quot;size20&quot;&gt;참고 사항&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3267&quot; data-start=&quot;3149&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3187&quot; data-start=&quot;3149&quot;&gt;UTM 변환에는 &lt;b&gt;GeographicLib&lt;/b&gt; 라이브러리를 사용&lt;/li&gt;
&lt;li data-end=&quot;3237&quot; data-start=&quot;3188&quot;&gt;IMU 오차와 GPS 신호 손실 등을 고려해 추가 필터 (예: 칼만 필터) 적용 가능&lt;/li&gt;
&lt;li data-end=&quot;3267&quot; data-start=&quot;3238&quot;&gt;본 코드는 ROS 2 Humble 기준으로 작성됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 코드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 저장소: &lt;a href=&quot;https://github.com/lidarmansiwon/macro_gnc&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MACRO_GNC&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1749048434807&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - lidarmansiwon/macro_gnc&quot; data-og-description=&quot;Contribute to lidarmansiwon/macro_gnc development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/lidarmansiwon/macro_gnc&quot; data-og-url=&quot;https://github.com/lidarmansiwon/macro_gnc&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/biaNR0/hyY5hgjaem/lq73hsxtPKPA5pxKLsCu40/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/lidarmansiwon/macro_gnc&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/lidarmansiwon/macro_gnc&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/biaNR0/hyY5hgjaem/lq73hsxtPKPA5pxKLsCu40/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - lidarmansiwon/macro_gnc&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to lidarmansiwon/macro_gnc development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;[gps_navigation.cpp 전체 코드]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1749048458752&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;memory&amp;gt;
// C++ Standard lib 
#include &amp;lt;chrono&amp;gt;
#include &amp;lt;functional&amp;gt;
#include &amp;lt;string&amp;gt;
#include &quot;rclcpp/rclcpp.hpp&quot;

#include &amp;lt;GeographicLib/UTMUPS.hpp&amp;gt;

#include &quot;std_msgs/msg/string.hpp&quot;
#include &quot;mk3_msgs/msg/navigation_type.hpp&quot;
#include &quot;sensor_msgs/msg/imu.hpp&quot;
#include &quot;sensor_msgs/msg/nav_sat_fix.hpp&quot;

#include &quot;navigation/gps_navigation.hpp&quot;
#include &quot;navigation/tool/quaternion_utils.hpp&quot;
#include &quot;navigation/tool/velocity_calculator.hpp&quot;


using std::placeholders::_1;
using namespace std::chrono_literals;

GPSNavigation::GPSNavigation(const rclcpp::NodeOptions &amp;amp; node_options)
: Node(&quot;gps_navigation&quot;, node_options), 
  reference_gps_latitude_(0.0),
  reference_gps_longitude_(0.0),
  reference_utm_x(0.0),
  reference_utm_y(0.0),
  reference_zone(0.0),
  reference_northp(false),
  vel_calc_(0.9)
{ 
  this-&amp;gt;declare_parameter&amp;lt;std::string&amp;gt;(&quot;imu_topic&quot;, &quot;/imu_topic&quot;);
  this-&amp;gt;declare_parameter&amp;lt;std::string&amp;gt;(&quot;gps_topic&quot;, &quot;/gps_topic&quot;);
  this-&amp;gt;declare_parameter&amp;lt;std::string&amp;gt;(&quot;navigation_topic&quot;, &quot;/navigation_data&quot;);

  this-&amp;gt;declare_parameter&amp;lt;double&amp;gt;(&quot;reference_gps_latitude&quot;, 0.0);
  this-&amp;gt;declare_parameter&amp;lt;double&amp;gt;(&quot;reference_gps_longitude&quot;, 0.0);
  this-&amp;gt;declare_parameter&amp;lt;double&amp;gt;(&quot;velocity_alpha&quot;, 0.9);

  std::string imu_topic = this-&amp;gt;get_parameter(&quot;imu_topic&quot;).as_string();
  std::string gps_topic = this-&amp;gt;get_parameter(&quot;gps_topic&quot;).as_string();
  std::string navigation_topic = this-&amp;gt;get_parameter(&quot;navigation_topic&quot;).as_string();

  reference_gps_latitude_  = this-&amp;gt;get_parameter(&quot;reference_gps_latitude&quot;).as_double();
  reference_gps_longitude_ = this-&amp;gt;get_parameter(&quot;reference_gps_longitude&quot;).as_double();
  double velocity_alpha = this-&amp;gt;get_parameter(&quot;velocity_alpha&quot;).as_double();
  vel_calc_ = VelocityCalculator(velocity_alpha);

  // 생성자에서 시작 표시.
  RCLCPP_INFO(this-&amp;gt;get_logger(), &quot;Run GPS Navigation&quot;);

  imu_subscription_ = this-&amp;gt;create_subscription&amp;lt;sensor_msgs::msg::Imu&amp;gt;(
    imu_topic, rclcpp::SensorDataQoS(), std::bind(&amp;amp;GPSNavigation::imu_callback, this, _1));

  gps_subscription_ = this-&amp;gt;create_subscription&amp;lt;sensor_msgs::msg::NavSatFix&amp;gt;(
    gps_topic, rclcpp::SensorDataQoS(), std::bind(&amp;amp;GPSNavigation::gps_callback, this, _1));

  navigation_publisher_ = this-&amp;gt;create_publisher&amp;lt;mk3_msgs::msg::NavigationType&amp;gt;(
    navigation_topic, 10);

  timer_ = this-&amp;gt;create_wall_timer(50ms, std::bind(&amp;amp;GPSNavigation::process, this));
}

GPSNavigation::~GPSNavigation()
{

}

void GPSNavigation::imu_callback(const sensor_msgs::msg::Imu::SharedPtr msg){imu_data_ = msg;}
void GPSNavigation::gps_callback(const sensor_msgs::msg::NavSatFix::SharedPtr msg){gps_data_ = msg;}

void GPSNavigation::process()
{
  if (!gps_data_ || !imu_data_)
  {
    RCLCPP_WARN_THROTTLE(this-&amp;gt;get_logger(), *this-&amp;gt;get_clock(), 3000, &quot;GPS or IMU data not received yet.&quot;);
    return;
  }

  double latitude = gps_data_-&amp;gt;latitude;
  double longitude = gps_data_-&amp;gt;longitude;

  double utm_x, utm_y;
  int zone;
  bool northp;

  try {
    // 입력: latitude, longitude, 출력: zone(UTM 존), northp(북반구, 남반구 여부), utm_x, utm_y
    GeographicLib::UTMUPS::Forward(latitude, longitude, zone, northp, utm_x, utm_y);
    RCLCPP_INFO(this-&amp;gt;get_logger(), &quot;Converted GPS (%.8f, %.8f) &amp;rarr; UTM (%.2f, %.2f), Zone: %d, Northp: %d\n&quot;, latitude, longitude, utm_x, utm_y, zone, northp);
  } catch (const std::exception &amp;amp;e) {
    RCLCPP_ERROR(this-&amp;gt;get_logger(), &quot;UTM conversion failed: %s&quot;, e.what());
    return;
  }

  if (!origin_set_) {

    try {
        GeographicLib::UTMUPS::Forward(reference_gps_latitude_, reference_gps_longitude_, reference_zone, reference_northp, reference_utm_x, reference_utm_y);
        RCLCPP_INFO(this-&amp;gt;get_logger(), &quot;Converted Reference GPS (%.8f, %.8f) &amp;rarr; Reference UTM (%.2f, %.2f), Zone: %d, Northp: %d\n&quot;, 
        reference_gps_latitude_, reference_gps_longitude_, reference_utm_x, reference_utm_y, reference_zone, reference_northp);
    } catch (const std::exception &amp;amp;e) {
        RCLCPP_ERROR(this-&amp;gt;get_logger(), &quot;Reference UTM conversion failed: %s&quot;, e.what());
        return;
    }
    origin_set_ = true;
  }

  double relative_x = utm_x - reference_utm_x;
  double relative_y = utm_y - reference_utm_y;


  RCLCPP_INFO(this-&amp;gt;get_logger(), &quot;Relative position: x=%.2f, y=%.2f\n\n&quot;, relative_x, relative_y);

  geometry_msgs::msg::Quaternion quat;

  quat = imu_data_-&amp;gt;orientation;

  std::array&amp;lt;double, 3&amp;gt; rpy = euler_from_quaternion(
    {quat.x, quat.y, quat.z, quat.w}
  );

  double roll = rpy[0];
  double pitch = rpy[1];
  double yaw = rpy[2];

  double x = relative_x;
  double y = relative_y;
  double psi = yaw;

  rclcpp::Time current_time = this-&amp;gt;now();
  double time_sec = current_time.seconds();

  auto result = vel_calc_.update(x, y, psi, time_sec);
  double LPFVel_x = result.LPFVel_x;
  double LPFVel_y = result.LPFVel_y;
  double angular_velocity = result.angular_velocity;

  NavigationType nav_msg;
  nav_msg.x = x;
  nav_msg.y = y;
  nav_msg.psi = psi * 180.0 / M_PI;
  nav_msg.u = LPFVel_x;
  nav_msg.v = LPFVel_y;
  nav_msg.r = angular_velocity;
  nav_msg.w = imu_data_-&amp;gt;angular_velocity.z;

  navigation_publisher_-&amp;gt;publish(nav_msg);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[velocity_calculator.hpp 전체 코드]&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1749048511739&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#ifndef VELOCITY_CALCULATOR_HPP
#define VELOCITY_CALCULATOR_HPP

#include &amp;lt;cmath&amp;gt;
#include &amp;lt;chrono&amp;gt;

class VelocityCalculator
{
public:
  VelocityCalculator(double alpha = 0.9)
    : alpha_(alpha), prev_initialized_(false), LPFVel_x_(0.0), LPFVel_y_(0.0) {}

  struct VelocityResult {
    double vx;
    double vy;
    double angular_velocity;
    double LPFVel_x;
    double LPFVel_y;
  };

  VelocityResult update(double x, double y, double psi, double current_time)
  {
    if (!prev_initialized_) {
      prev_x_ = x;
      prev_y_ = y;
      prev_psi_ = psi;
      prev_time_ = current_time;
      prev_initialized_ = true;
      return {0.0, 0.0, 0.0, 0.0, 0.0};
    }

    // double dt = current_time - prev_time_;
    // if (dt &amp;lt;= 0.0) {
    //   return {0.0, 0.0, 0.0, LPFVel_x_, LPFVel_y_};
    // }
    double dt = 0.05;

    double dx = x - prev_x_;
    double dy = y - prev_y_;
    double dpsi = psi - prev_psi_;

    double vx_fixed = dx / dt;
    double vy_fixed = dy / dt;

    double cos_psi = std::cos(psi);
    double sin_psi = std::sin(psi);

    double vx = vx_fixed * cos_psi + vy_fixed * sin_psi;
    double vy = -vx_fixed * sin_psi + vy_fixed * cos_psi;

    double angular_velocity = dpsi / dt;

    // Low-pass filter
    LPFVel_x_ = alpha_ * LPFVel_x_ + (1 - alpha_) * vx;
    LPFVel_y_ = alpha_ * LPFVel_y_ + (1 - alpha_) * vy;

    // Save current state
    prev_x_ = x;
    prev_y_ = y;
    prev_psi_ = psi;
    prev_time_ = current_time;

    return {vx, vy, angular_velocity, LPFVel_x_, LPFVel_y_};
  }

private:
  double alpha_;
  bool prev_initialized_;
  double prev_x_, prev_y_, prev_psi_, prev_time_;
  double LPFVel_x_, LPFVel_y_;
};

#endif  // VELOCITY_CALCULATOR_HPP&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>ROS2</category>
      <category>GPS</category>
      <category>imu</category>
      <category>Navigation</category>
      <category>ROS2</category>
      <category>topic</category>
      <category>항법</category>
      <author>lidarmansiwon</author>
      <guid isPermaLink="true">https://lidarmansiwon.tistory.com/9</guid>
      <comments>https://lidarmansiwon.tistory.com/9#entry9comment</comments>
      <pubDate>Wed, 4 Jun 2025 23:50:17 +0900</pubDate>
    </item>
    <item>
      <title>[ROS2 Multi Thread] Multi Thread의 필요성</title>
      <link>https://lidarmansiwon.tistory.com/8</link>
      <description>&lt;h2 data-end=&quot;76&quot; data-start=&quot;68&quot; data-ke-size=&quot;size26&quot;&gt;1. 개요&lt;/h2&gt;
&lt;p data-end=&quot;195&quot; data-start=&quot;78&quot; data-ke-size=&quot;size16&quot;&gt;ROS2에서는 하나의 노드에서 여러 개의 토픽을 구독하거나, 다양한 센서 데이터를 동시에 처리해야 하는 경우가 많다. 이때, 시스템이 어떻게 콜백을 처리하는지에 따라 전체 성능과 반응 속도에 큰 영향을 미친다.&lt;/p&gt;
&lt;p data-end=&quot;195&quot; data-start=&quot;78&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;195&quot; data-start=&quot;78&quot; data-ke-size=&quot;size16&quot;&gt;ROS2의 기본 실행 모델은 SingleThreadedExecutor이기 때문에, 아무리 많은 토픽을 구독하더라도 &lt;b&gt;콜백은 하나씩 순차적으로 처리&lt;/b&gt;된다. 이로 인해 &lt;b&gt;여러 센서 데이터가 동시에 들어와도 처리 지연(latency)&lt;/b&gt;이 발생할 수 있다. 이러한 문제를 해결하기 위한 방법이 바로 &lt;b&gt;멀티 스레드(Multi-Threading)&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;195&quot; data-start=&quot;78&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;308&quot; data-start=&quot;294&quot; data-ke-size=&quot;size26&quot;&gt;2. 상황 예제 코드&lt;/h2&gt;
&lt;p data-end=&quot;387&quot; data-start=&quot;310&quot; data-ke-size=&quot;size16&quot;&gt;다음은 하나의 노드에서 IMU, Odometry 또는 PoseStamped 데이터를 구독하고, 일정 주기로 통합 처리를 수행하는 예제이다.&lt;/p&gt;
&lt;p data-end=&quot;387&quot; data-start=&quot;310&quot; data-ke-size=&quot;size16&quot;&gt;여러개의 Topic을 callback 함수를 통하여 subscribe 하는 극단적인 상황 예이다.&lt;/p&gt;
&lt;p data-end=&quot;387&quot; data-start=&quot;310&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1748620769658&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;memory&amp;gt;
#include &amp;lt;chrono&amp;gt;
#include &amp;lt;functional&amp;gt;
#include &amp;lt;string&amp;gt;
#include &quot;rclcpp/rclcpp.hpp&quot;
#include &quot;std_msgs/msg/string.hpp&quot;
#include &quot;geometry_msgs/msg/pose_stamped.hpp&quot;
#include &quot;mk3_msgs/msg/navigation_type.hpp&quot;
#include &quot;sensor_msgs/msg/imu.hpp&quot;
#include &quot;nav_msgs/msg/odometry.hpp&quot;

using std::placeholders::_1;
using namespace std::chrono_literals;

class odom_navigation : public rclcpp::Node
{
public:
  odom_navigation()
  : Node(&quot;odom_navigation&quot;)
  {
    navigation_publisher_ = this-&amp;gt;create_publisher&amp;lt;mk3_msgs::msg::NavigationType&amp;gt;(&quot;navigation&quot;, 10); 
    timer_ = this-&amp;gt;create_wall_timer(100ms, std::bind(&amp;amp;odom_navigation::process, this));  // 주기적으로 처리

    imu_subscription_ = this-&amp;gt;create_subscription&amp;lt;sensor_msgs::msg::Imu&amp;gt;(
      &quot;/ouster/imu&quot;, 10, std::bind(&amp;amp;odom_navigation::imu_callback, this, _1));

    use_odometry = false;
    if (use_odometry)
    {
      odom_subscription_ = this-&amp;gt;create_subscription&amp;lt;nav_msgs::msg::Odometry&amp;gt;(
        &quot;odometry&quot;, 10, std::bind(&amp;amp;odom_navigation::odometry_callback, this, _1));
    }
    else
    {
      pose_subscription_ = this-&amp;gt;create_subscription&amp;lt;geometry_msgs::msg::PoseStamped&amp;gt;(
        &quot;current_pose&quot;, 10, std::bind(&amp;amp;odom_navigation::pose_callback, this, _1));
    }
  }

private:
  void process()
  {
    RCLCPP_INFO(this-&amp;gt;get_logger(), &quot;Processing data...&quot;);

    // 수신한 센서 데이터를 활용하여 통합 처리
    if (last_imu_ &amp;amp;&amp;amp; (use_odometry ? last_odom_ : last_pose_))
    {
      mk3_msgs::msg::NavigationType msg;
      msg.header.stamp = this-&amp;gt;now();
      msg.yaw_rate = last_imu_-&amp;gt;angular_velocity.z;
      if (use_odometry)
        msg.vel_x = last_odom_-&amp;gt;twist.twist.linear.x;
      else
        msg.pos_x = last_pose_-&amp;gt;pose.position.x;

      navigation_publisher_-&amp;gt;publish(msg);
    }
  }

  void odometry_callback(const nav_msgs::msg::Odometry::SharedPtr msg)
  {
    RCLCPP_INFO(this-&amp;gt;get_logger(), &quot;Received Odom&quot;);
    last_odom_ = msg;
  }

  void pose_callback(const geometry_msgs::msg::PoseStamped::SharedPtr msg)
  {
    RCLCPP_INFO(this-&amp;gt;get_logger(), &quot;Received Pose&quot;);
    last_pose_ = msg;
  }

  void imu_callback(const sensor_msgs::msg::Imu::SharedPtr msg)
  {
    RCLCPP_INFO(this-&amp;gt;get_logger(), &quot;Received IMU&quot;);
    last_imu_ = msg;
  }

  bool use_odometry;
  rclcpp::TimerBase::SharedPtr timer_;
  rclcpp::Subscription&amp;lt;sensor_msgs::msg::Imu&amp;gt;::SharedPtr imu_subscription_;
  rclcpp::Subscription&amp;lt;nav_msgs::msg::Odometry&amp;gt;::SharedPtr odom_subscription_;
  rclcpp::Subscription&amp;lt;geometry_msgs::msg::PoseStamped&amp;gt;::SharedPtr pose_subscription_;
  rclcpp::Publisher&amp;lt;mk3_msgs::msg::NavigationType&amp;gt;::SharedPtr navigation_publisher_;

  // 수신한 데이터를 저장할 멤버 변수
  sensor_msgs::msg::Imu::SharedPtr last_imu_;
  nav_msgs::msg::Odometry::SharedPtr last_odom_;
  geometry_msgs::msg::PoseStamped::SharedPtr last_pose_;
};

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared&amp;lt;odom_navigation&amp;gt;());
  rclcpp::shutdown();
  return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;387&quot; data-start=&quot;310&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;422&quot; data-start=&quot;403&quot; data-ke-size=&quot;size26&quot;&gt;3. 싱글 스레드 모델의 특징&lt;/h2&gt;
&lt;p data-end=&quot;466&quot; data-start=&quot;424&quot; data-ke-size=&quot;size16&quot;&gt;기본적으로 아래와 같은 형태로 작성된 노드는 싱글 스레드 기반으로 동작한다.&lt;/p&gt;
&lt;pre id=&quot;code_1748620260628&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rclcpp::spin(std::make_shared&amp;lt;odom_navigation&amp;gt;());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;195&quot; data-start=&quot;78&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;195&quot; data-start=&quot;78&quot; data-ke-size=&quot;size16&quot;&gt;이때 사용되는 SingleThreadedExecutor의 동작 방식은 다음과 같다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;775&quot; data-start=&quot;581&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;606&quot; data-start=&quot;581&quot;&gt;&lt;b&gt;콜백을 하나씩 순차적으로 실행&lt;/b&gt;한다.&lt;/li&gt;
&lt;li data-end=&quot;647&quot; data-start=&quot;607&quot;&gt;하나의 콜백 함수가 실행 중이면, 다른 콜백은 &lt;b&gt;큐에 대기&lt;/b&gt;한다.&lt;/li&gt;
&lt;li data-end=&quot;690&quot; data-start=&quot;648&quot;&gt;CPU 코어가 여럿 있더라도, &lt;b&gt;실제 처리는 한 스레드만 수행&lt;/b&gt;한다.&lt;/li&gt;
&lt;li data-end=&quot;733&quot; data-start=&quot;691&quot;&gt;구조가 단순하고 디버깅이 쉬우며, &lt;b&gt;동기화 문제&lt;/b&gt;가 발생하지 않는다.&lt;/li&gt;
&lt;li data-end=&quot;775&quot; data-start=&quot;734&quot;&gt;하지만, &lt;b&gt;센서 데이터가 많은 경우 병목 현상&lt;/b&gt;이 발생할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;914&quot; data-start=&quot;777&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어, IMU와 Odometry, PoseStamped 데이터를 동시에 구독할 경우, IMU 콜백이 실행되는 동안 다른 콜백은 큐에 쌓여 순서대로 처리된다. 콜백 처리 시간이 길어지면 다른 센서 데이터도 늦게 처리되므로 실시간성이 떨어진다.&lt;/p&gt;
&lt;p data-end=&quot;195&quot; data-start=&quot;78&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;3406&quot; data-start=&quot;3377&quot; data-ke-size=&quot;size26&quot;&gt;4. 싱글 스레드 사용 시 발생할 수 있는 문제&lt;/h2&gt;
&lt;p data-end=&quot;3466&quot; data-start=&quot;3408&quot; data-ke-size=&quot;size16&quot;&gt;이 예제에서 SingleThreadedExecutor를 사용하면 다음과 같은 문제가 발생할 수 있다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3776&quot; data-start=&quot;3468&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3552&quot; data-start=&quot;3468&quot;&gt;&lt;b&gt;콜백 함수들이 순차적으로 실행됨&lt;/b&gt;: imu_callback, pose_callback, process()는 동시에 실행되지 않는다.&lt;/li&gt;
&lt;li data-end=&quot;3636&quot; data-start=&quot;3553&quot;&gt;imu_callback()이 오래 걸리거나 블로킹 연산을 포함하면, process()나 pose_callback()이 &lt;b&gt;지연&lt;/b&gt;된다.&lt;/li&gt;
&lt;li data-end=&quot;3710&quot; data-start=&quot;3637&quot;&gt;센서 데이터가 빠르게 들어오는 경우, &lt;b&gt;처리 지연(latency)&lt;/b&gt; 으로 인해 데이터 유실이나 동기화 오류가 생길 수 있다.&lt;/li&gt;
&lt;li data-end=&quot;3776&quot; data-start=&quot;3711&quot;&gt;process() 주기보다 빠른 센서 입력이 쌓이면서 &lt;b&gt;큐 오버플로우 또는 지연 전파&lt;/b&gt;가 발생할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;941&quot; data-start=&quot;921&quot; data-ke-size=&quot;size26&quot;&gt;5. 멀티 스레드 모델의 필요성&lt;/h2&gt;
&lt;p data-end=&quot;992&quot; data-start=&quot;943&quot; data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해 ROS2에서는 MultiThreadedExecutor를 제공한다.&lt;/p&gt;
&lt;p data-end=&quot;992&quot; data-start=&quot;943&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1011&quot; data-start=&quot;994&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;멀티 스레드 실행 방식:&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748620305828&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rclcpp::executors::MultiThreadedExecutor executor;
executor.add_node(node);
executor.spin();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1125&quot; data-start=&quot;1118&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;장점:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1232&quot; data-start=&quot;1127&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1159&quot; data-start=&quot;1127&quot;&gt;여러 개의 콜백을 &lt;b&gt;동시에 병렬 실행&lt;/b&gt;할 수 있다.&lt;/li&gt;
&lt;li data-end=&quot;1204&quot; data-start=&quot;1160&quot;&gt;각 콜백이 별도의 스레드에서 실행되므로, &lt;b&gt;콜백 처리 지연이 줄어든다.&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1232&quot; data-start=&quot;1205&quot;&gt;멀티 코어 CPU를 적극적으로 활용 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1241&quot; data-start=&quot;1234&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;단점:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1371&quot; data-start=&quot;1243&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1281&quot; data-start=&quot;1243&quot;&gt;동시에 실행되는 콜백 간에 &lt;b&gt;데이터 충돌 가능성&lt;/b&gt;이 존재한다.&lt;/li&gt;
&lt;li data-end=&quot;1329&quot; data-start=&quot;1282&quot;&gt;&lt;b&gt;공유 자원 보호를 위한 mutex 또는 atomic 변수 사용&lt;/b&gt;이 필요하다.&lt;/li&gt;
&lt;li data-end=&quot;1371&quot; data-start=&quot;1330&quot;&gt;디버깅이 복잡해지고, &lt;b&gt;콜백 실행 순서가 예측 불가능&lt;/b&gt;할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1434&quot; data-start=&quot;1378&quot; data-ke-size=&quot;size26&quot;&gt;6. 예제 상황에서의 멀티 스레드 사용 시 장점&lt;/h2&gt;
&lt;p data-end=&quot;3860&quot; data-start=&quot;3805&quot; data-ke-size=&quot;size16&quot;&gt;예제 상황에서 MultiThreadedExecutor를 사용하면 다음과 같은 이점이 있다:&lt;/p&gt;
&lt;pre id=&quot;code_1748620859556&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  auto node = std::make_shared&amp;lt;odom_navigation&amp;gt;();
  rclcpp::executors::MultiThreadedExecutor executor;
  executor.add_node(node);
  executor.spin();
  rclcpp::shutdown();
  return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;4131&quot; data-start=&quot;4124&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;4131&quot; data-start=&quot;4124&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;장점:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4315&quot; data-start=&quot;4133&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4209&quot; data-start=&quot;4133&quot;&gt;&lt;b&gt;콜백 함수가 병렬로 실행&lt;/b&gt;되므로, IMU 수신 도중에도 process()나 pose_callback()이 실행 가능하다.&lt;/li&gt;
&lt;li data-end=&quot;4256&quot; data-start=&quot;4210&quot;&gt;각 콜백이 &lt;b&gt;별도 스레드에서 실행&lt;/b&gt;되어, 센서 간 독립적인 처리가 가능하다.&lt;/li&gt;
&lt;li data-end=&quot;4296&quot; data-start=&quot;4257&quot;&gt;&lt;b&gt;멀티 코어 CPU를 효과적으로 활용&lt;/b&gt;하여 처리 속도를 높인다.&lt;/li&gt;
&lt;li data-end=&quot;4315&quot; data-start=&quot;4297&quot;&gt;실시간성 개선 및 지연 감소.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;4345&quot; data-start=&quot;4322&quot; data-ke-size=&quot;size26&quot;&gt;7. 멀티 스레드 사용 시 주의할 점&lt;/h2&gt;
&lt;p data-end=&quot;4385&quot; data-start=&quot;4347&quot; data-ke-size=&quot;size16&quot;&gt;멀티 스레드는 성능을 높이지만, 다음과 같은 문제를 유발할 수 있다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4610&quot; data-start=&quot;4387&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4476&quot; data-start=&quot;4387&quot;&gt;콜백 간에 &lt;b&gt;공유 자원(last_imu_, last_pose_ 등)&lt;/b&gt; 을 동시에 접근할 수 있으므로 &lt;b&gt;race condition&lt;/b&gt; 발생 가능성 있음.&lt;/li&gt;
&lt;li data-end=&quot;4554&quot; data-start=&quot;4477&quot;&gt;따라서 공유 자원 접근 시 std::mutex 또는 rclcpp::GuardCondition 등을 이용한 &lt;b&gt;동기화가 필수&lt;/b&gt;.&lt;/li&gt;
&lt;li data-end=&quot;4610&quot; data-start=&quot;4555&quot;&gt;콜백 순서가 보장되지 않기 때문에, 시간 스탬프를 기반으로 적절한 데이터 동기화 전략이 필요함.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1434&quot; data-start=&quot;1378&quot; data-ke-size=&quot;size26&quot;&gt;8. SingleThreadedExecutor vs MultiThreadedExecutor 비교&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;콜백 실행&lt;/td&gt;
&lt;td&gt;하나씩 순차 처리&lt;/td&gt;
&lt;td&gt;병렬 실행 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;콜백 처리 속도&lt;/td&gt;
&lt;td&gt;느림 (병목 가능성 있음)&lt;/td&gt;
&lt;td&gt;빠름 (실시간성 향상)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;동기화 문제&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;td&gt;있음 (mutex 등 필요)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;구현 난이도&lt;/td&gt;
&lt;td&gt;쉬움&lt;/td&gt;
&lt;td&gt;어려움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;적합한 상황&lt;/td&gt;
&lt;td&gt;단일 센서, 낮은 빈도&lt;/td&gt;
&lt;td&gt;다중 센서, 고빈도, 실시간성 요구&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1748&quot; data-start=&quot;1740&quot; data-ke-size=&quot;size26&quot;&gt;9. 결론&lt;/h2&gt;
&lt;p data-end=&quot;1927&quot; data-start=&quot;1750&quot; data-ke-size=&quot;size16&quot;&gt;SingleThreadedExecutor는 구조가 간단하고 안전하지만, 콜백 처리가 병목을 일으킬 수 있다. 실시간성이 중요한 시스템이나 다중 센서 환경에서는 MultiThreadedExecutor를 사용하여 병렬 처리를 적용하는 것이 필요하다. 단, 이 경우 공유 자원에 대한 철저한 동기화 설계가 요구된다.&lt;/p&gt;
&lt;p data-end=&quot;2010&quot; data-start=&quot;1929&quot; data-ke-size=&quot;size16&quot;&gt;멀티 스레드를 사용하는 것이 항상 정답은 아니며, 시스템 요구사항과 데이터 흐름, 리소스 특성에 따라 적절한 실행 모델을 선택하는 것이 중요하다.&lt;/p&gt;
&lt;p data-end=&quot;2010&quot; data-start=&quot;1929&quot; data-ke-size=&quot;size16&quot;&gt;멀티 스레드는 강력한 도구이지만, 동기화와 구조적 설계를 신중히 고려해야만 안정적이고 빠른 시스템을 만들 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;2010&quot; data-start=&quot;1929&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2010&quot; data-start=&quot;1929&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2010&quot; data-start=&quot;1929&quot; data-ke-size=&quot;size16&quot;&gt;※ 본 글은 필자가 공부를 하며 정리한 내용입니다. 차후 더 자세한 예제와 활용 방식에 대하여 포스팅 예정입니다. 감사합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2010&quot; data-start=&quot;1929&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>ROS2</category>
      <category>multithreadedexecutor</category>
      <category>ROS2</category>
      <category>singlethreadedexecutor</category>
      <category>로봇제어</category>
      <category>멀티스레드</category>
      <category>싱글스레드</category>
      <author>lidarmansiwon</author>
      <guid isPermaLink="true">https://lidarmansiwon.tistory.com/8</guid>
      <comments>https://lidarmansiwon.tistory.com/8#entry8comment</comments>
      <pubDate>Sat, 31 May 2025 01:07:26 +0900</pubDate>
    </item>
    <item>
      <title>[C++ 기초부터 심화까지 Chapter 06. 객체지향과 클래스]</title>
      <link>https://lidarmansiwon.tistory.com/7</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;6장 소개&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그래밍 패러다임: 프로그램을 어떤 절차와 구조로 만들 것인지에 대한 스타일이나 접근 방법&lt;/li&gt;
&lt;li&gt;이절에서는 여러 패러다임 중 &lt;b&gt;비구조적 프로그래밍&lt;/b&gt;과 &lt;b&gt;절차적 프로그래밍&lt;/b&gt;에 대하여 알아봄&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;06-1 객체지향 이전의 프로그래밍 패러다임&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;비구조적 프로그래밍&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비구조적 프로그래밍: &lt;b&gt;코드를 구조화하지 않고 작성하는 방법&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;첫번째 줄부터 마지막 줄까지 차례대로 실행되며, 코드의 흐름을 이동하는 goto문을 사용하는 특징이 있음.&lt;/li&gt;
&lt;li&gt;대표적 비구조적 프로그래밍 언어: 어셈블리어, 초창기 fortran&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;절차적 프로그래밍&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소스 코드를 여러 부분으로 나눠서 활용하는 패러다임&lt;/li&gt;
&lt;li&gt;프로시저를 이용해 구조화하는 방식&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프로시저&lt;/b&gt;: 일련의 코드 묶음, 함수&lt;/li&gt;
&lt;li&gt;대표적 절차적 프로그래밍 언어: C, cobol, fortran&lt;/li&gt;
&lt;li&gt;절차적 프로그래밍에서는 코드의 논리 구조를 --&amp;gt; &lt;b&gt;모듈화&lt;/b&gt;할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모듈화&lt;/b&gt;를 하면 같은 기능을 수행하는 코드를 다시 작성하지 않아서 재사용할 수 있음.&lt;/li&gt;
&lt;li&gt;라이브러리처럼 누군가가 만들어 놓은 기능을 사용하면 프로그램을 더 쉽게 개발할 수 있음.&lt;/li&gt;
&lt;li&gt;코드 가독성이 좋아짐.&lt;/li&gt;
&lt;li&gt;절차적 프로그래밍으로 작성한 코드는 &lt;b&gt;프로그램의 중심 흐름&lt;/b&gt;을 당담하는 코드와 &lt;b&gt;프로시저&lt;/b&gt;를 구현하는 코드로 나뉨.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;절차적 프로그래밍의 한계&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로시저가 가진 논리적 다층 구조를 프로그래밍 내부에서 표현하는데 한계가 있음.&lt;/li&gt;
&lt;li&gt;불필요한 프로시저를 호출하거나 전역 변수를 수정할 수 있다. --&amp;gt; 프로그램 동작에 치명적인 영향을 줄 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;06-2 객체지향 프로그래밍&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체지향 프로그래밍의 4가지 특징 이해하기.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체지향 프로그래밍 개념&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;객체지향 프로그래밍(object oriented programming, OOP)&lt;/b&gt;: 객체라는 논리적인 개념으로 코드를 구성&lt;/li&gt;
&lt;li&gt;&quot;사물을 객체로 인식하고 표현하는 것은 사람에게 매우 자연스럽고 이해하기 쉬운 방식&quot;&lt;/li&gt;
&lt;li&gt;이전의 패러다임은 모두 하향식(top-down) 방법이지만, 객체지향 프로그래밍은 &lt;b&gt;논리적인 단위&lt;/b&gt;를 먼저 정의한 후에 이를 조합해 프로그램이 수행할 기능을 만드는 &lt;b&gt;상향식(bottom-up)&lt;/b&gt; 방법이다.&lt;/li&gt;
&lt;li&gt;객체지향 프로그래밍에서는 데이터(속성)와 함수(기능)을 가진 객체를 먼저 정의하고, 객체를 기초로 원하는 실행 흐름을 소스 코드로 작성.&lt;/li&gt;
&lt;li&gt;상향식 == 레고&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;has-a 관계&lt;/b&gt;: 동력장치 (내부: 가솔린 엔진, 가속페달, 동력 전달 장치(미션, 바퀴), 제동 장치(브레이크)) --&amp;gt; 이러한 포함 관계. 상호 교환 X&lt;/li&gt;
&lt;li&gt;&lt;b&gt;is-a 관계&lt;/b&gt;: 엔진(가솔린 엔진, 디젤 엔진, 가스 엔진, 전기 엔진) --&amp;gt; 상호 교환 O, 하위 객체가 상위 객체를 대신할 수 있음. --&amp;gt; Generalization(일반화)라고 표현.&lt;/li&gt;
&lt;li&gt;is-a 관계에서는 상위 객체의 속성을 그대로 물려받아 새로운 객체의 특성을 추가하는 &lt;b&gt;상속(inheritance)&lt;/b&gt;이 가능함. 상속은 07장에서 다룸.&lt;/li&gt;
&lt;li&gt;상속: 엔진{ 내연기관 엔진(가솔린 엔진, 디젤 엔진, 가스 엔진), 전기 엔진}&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체 지향 프로그래밍의 특징&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;추상화 (abstraction)&lt;/li&gt;
&lt;li&gt;캡슐화 (encapsulation)&lt;/li&gt;
&lt;li&gt;상속성 (inheritance)&lt;/li&gt;
&lt;li&gt;다형성 (polymorphism)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;추상화 (abstraction)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현실 세계의 사물을 모델링하여 객체로 만들 때, 어떤 부류에서 불필요한 요소는 배제하고 공통된 특징만 추출하는 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;캡슐화 (encapsulation)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복잡한 내부 기능을 묶어 외부에서 불필요한 정보를 감추는 것&lt;/li&gt;
&lt;li&gt;Ex) 객체 외부 시선 --&amp;gt; 가속 페달 --&amp;gt; 출력 조절(내연기관 엔진)&lt;/li&gt;
&lt;li&gt;객체 내부 시선 --&amp;gt; 가속 페달 --&amp;gt; 출력 조절(출력 조절기 --&amp;gt; 피스톤)&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;상속성 (inheritance)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가솔린 엔진은 내연기관 엔진의 속성을 그대로 상속받아 가솔린 연료를 사용하는 특성&lt;/li&gt;
&lt;li&gt;부모 객체의 특성을 이어받는 것을 상속&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;4&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;다형성 (polymorphism)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상속받은 객체는 부모 객체의 역할을 대신할 수 있음.&lt;/li&gt;
&lt;li&gt;Ex) 내연 기관 엔진이 사용되는 자리에 가솔린, 디젤, 가스 엔진을 사용할 수 있음.&lt;/li&gt;
&lt;li&gt;완전히 똑같으면 대체할 이유가 없음. 상속받은 객체는 자신만의 특성을 반영하여 다르게 동작해야함.&lt;/li&gt;
&lt;li&gt;--&amp;gt; 이처럼 상속 관계의 객체에서 같은 기능(함수)이 다르게 동작하는 특성을 &lt;b&gt;다형성&lt;/b&gt;이라고 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;06-3 클래스와 인스턴스&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스 개념 이해&lt;/li&gt;
&lt;li&gt;C++ 클래스와 인스턴스를 선언하는 문법 이해&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클래스란?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체와 클래스는 비슷함: 데이터와 함수를 포함하는 논리 단위&lt;/li&gt;
&lt;li&gt;변수: 값을 저장할 수 있는 공간, 어떤 형식의 값이 저장되는지 데이터 형식으로 컴파일러에게 알려줌.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클래스&lt;/b&gt;는 데이터 형식, &lt;b&gt;객체&lt;/b&gt;는 변수&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클래스&lt;/b&gt;는 객체가 포함하는 데이터와 함수를 정의하는 문법적인 요소.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;객체&lt;/b&gt;는 특정 클래스를 사용할 수 있도록 만든 변수&lt;/li&gt;
&lt;li&gt;클래스를 정의할 때는 &lt;b&gt;객체가 가질 데이터와 함수를 작성&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;이때, 클래스가 포함하는 데이터를 &lt;b&gt;'멤버 변수'&lt;/b&gt;, 함수를 &lt;b&gt;'멤버 함수'&lt;/b&gt;라고 함.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;멤버 변수&lt;/b&gt;는 클래스의 속성을, &lt;b&gt;멤버 함수&lt;/b&gt;는 클래스의 행동을 나타냄.&lt;/li&gt;
&lt;li&gt;생성자와 소멸자라는 특별함 함수도 포함할 수 있음&lt;/li&gt;
&lt;li&gt;멤버 변수와 멤버 함수를 정의할 때는 &lt;b&gt;public&lt;/b&gt;, &lt;b&gt;private&lt;/b&gt;와 같은 접근 지정자가 필요함.&lt;/li&gt;
&lt;li&gt;접근 지정자: 접근 범위를 통제 --&amp;gt; 캡슐화와 관련&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클래스 정의 문법&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스 선언부: 멤버 변수, 멤버 함수, 생성자, 소멸자&lt;/li&gt;
&lt;li&gt;멤버 함수 정의부: 멤버 함수 정의&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;// 클래스 선언 키워드 + 클래스 이름   + 접근 지정자 + 부모 클래스(선택)
//    (class)     + (gs_engine) + (public)  + (ic_engine)
// class 선언부
class gs_engine : public ic_engine {
public:
    gs_engine();  // 생성자와 
    ~gs_engine(); // 소멸자(선택)
    int get_current_fuel() {return current_fuel;};          // 멤버 함수 선언과 구현

private:
    void acceleration_output() override;                    // &quot;&quot;
    void increasing_fuel() { increasing_piston_speed(); };  // &quot;&quot;
    void increasing_psiton_speed() {};                      // &quot;&quot;

    int current_fuel; //멤버 변수 선언
    int piston_speed; // &quot;&quot;
};
// ---

// 멤버 함수 정의부
// 멤버 함수 네임스페이스 -&amp;gt; gs_engine
void gs_engine::acceleration_output() {
    increasing_fuel();
    current_fuel++;
}
// --&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스 선언부: 클래스 선언하는 곳, 멤버 함수 정의를 제외한 클래스와 관련된 모든 것들을 이곳에서 선언.&lt;/li&gt;
&lt;li&gt;멤버 함수 정의부: 클래스에 선언한 멤버 함수를 정의.&lt;/li&gt;
&lt;li&gt;클래스 선언 키워드: 클래스를 선언하는 class 키워드.&lt;/li&gt;
&lt;li&gt;클래스 이름: 클래스를 나타내는 이름. 객체를 만들 때 사용하는 데이터 형식으로 볼 수 있음.&lt;/li&gt;
&lt;li&gt;부모 클래스(선택): 클래스가 다른 클래스를 상속받을 때, 쌍점(:) 다음에 접근 지정자와 부모 클래스를 지정. 상속 받지 않으면 생략.&lt;/li&gt;
&lt;li&gt;접근 지정자: 멤버 변수와 함수가 외부에서 접근할 수 있는지를 표시.&lt;/li&gt;
&lt;li&gt;생성자와 소멸자(선택): 객체가 생성되거나 소멸할 때 호출되는 함수. 선택적으로 사용 가능&lt;/li&gt;
&lt;li&gt;멤버 함수 선언과 정의: 객체에 포함할 멤버 함수를 선언. 간단한 함수는 클래스 선언부에서 중괄호{}를 이용해 정의하기도 함.&lt;/li&gt;
&lt;li&gt;멤버 함수 네임스페이스: 멤버 함수 정의부에서는 해당 함수가 어떤 클래스에 속하는지 알 수 있도록 클래스 이름을 네임스페이스로 사용.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체 선언&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앞에서는 클래스 정의하는 방법을 알아봄.&lt;/li&gt;
&lt;li&gt;정의한 클래스를 사용하려면 객체를 선언해야함.&lt;/li&gt;
&lt;li&gt;클래스의 객체를 선언하는 방법은 데이터 형식으로 변수를 선언하는 것과 같음.&lt;/li&gt;
&lt;li&gt;클래스의 객체를 선언하는 두가지 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클래스를 데이터 형식으로 삼고, 객체를 선언.&lt;/li&gt;
&lt;li&gt;new 키워드를 사용하여 동적으로 메모리를 할당. --&amp;gt; 이 경우 반드시 delete 키워드로 메모리를 해제 해야함.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1746631732347&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 클래스 정의
class engine {...(생략)...};

// 클래스를 데이터 형식처럼 사용하는 방법
engine my_engine;

// 클래스 형식으로 동적 메모리 할당과 해제 방법
engine *my_engine_pointer = new engine();
delete my_engine_pointer;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1746631783137&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;- 객체로 클래스의 멤버에 접근
my_engine.current_fuel; // 멤버 변수에 접근
my_engine.inceasing_piston_speed(); // 멤버 함수 호출&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;adative_cruise_modeling.cpp&amp;nbsp;참고&lt;br /&gt;&lt;br /&gt;※&amp;nbsp;이&amp;nbsp;글은&amp;nbsp;직접&amp;nbsp;구매한『Do&amp;nbsp;it!&amp;nbsp;C++&amp;nbsp;완전&amp;nbsp;정복』(문종채,&amp;nbsp;조규남&amp;nbsp;저,&amp;nbsp;성안당)을&amp;nbsp;참고하여&amp;nbsp;개인적으로&amp;nbsp;학습한&amp;nbsp;내용을&amp;nbsp;정리한&amp;nbsp;것입니다.&amp;nbsp;본문에&amp;nbsp;사용된&amp;nbsp;내용&amp;nbsp;및&amp;nbsp;예제&amp;nbsp;코드는&amp;nbsp;책의&amp;nbsp;내용을&amp;nbsp;기반으로&amp;nbsp;하되,&amp;nbsp;이해를&amp;nbsp;돕기&amp;nbsp;위한&amp;nbsp;개인적인&amp;nbsp;해석과&amp;nbsp;실습&amp;nbsp;결과를&amp;nbsp;포함하고&amp;nbsp;있습니다.&lt;br /&gt;&lt;br /&gt;참고한 예제 코드: [GitHub - mystous/DoItCPP]([&lt;a href=&quot;https://github.com/mystous/DoItCPP)](https://github.com/mystous/DoItCPP&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/mystous/DoItCPP)](https://github.com/mystous/DoItCPP&lt;/a&gt;))&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;예제&amp;nbsp;코드의&amp;nbsp;일부는&amp;nbsp;위&amp;nbsp;오픈소스&amp;nbsp;저장소를&amp;nbsp;참고하거나&amp;nbsp;수정하여&amp;nbsp;활용했으며,&amp;nbsp;해당&amp;nbsp;저장소는&amp;nbsp;학습용으로&amp;nbsp;공개되어&amp;nbsp;있습니다.&lt;/p&gt;</description>
      <category>개발 언어/C++</category>
      <author>lidarmansiwon</author>
      <guid isPermaLink="true">https://lidarmansiwon.tistory.com/7</guid>
      <comments>https://lidarmansiwon.tistory.com/7#entry7comment</comments>
      <pubDate>Thu, 8 May 2025 00:30:50 +0900</pubDate>
    </item>
    <item>
      <title>[C++ 기초부터 심화까지 Chapter 05. 예외 처리 구문]</title>
      <link>https://lidarmansiwon.tistory.com/5</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;5장 소개&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예외 처리하기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그래밍에서 exception이란 문법적인 오류가 아닌 &lt;b&gt;의도하지 않은 오류&lt;/b&gt;를 의미&lt;/li&gt;
&lt;li&gt;즉, 예외 처리란 프로그램 실행 흐름상 발생하는 오류에 대응하는 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;05-1 예외 처리 구문&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;try, catch, throw 문으로 예외 처리하기&lt;/li&gt;
&lt;li&gt;assert로 예외 처리하기&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소스 코드에 예외 처리를 해두면 예기치 못한 상황에도 유연하게 대처할 수 있음&lt;/li&gt;
&lt;li&gt;안정성을 높이고 비정상적으로 종요되지 않도록 함.&lt;/li&gt;
&lt;li&gt;유지 보수와 디버깅을 쉽게 만듦 --&amp;gt; 큰 규모의 프로젝트를 진행할 때, 함께 일하는 워커들이 코드의 안정성과 이해도를 높이는데 도움을 줌.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;try, catch, throw 문으로 예외 처리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;try: 예외가 발생할 수 있는 코드 블록을 중괄호 {}로 감싸 준다.&lt;/li&gt;
&lt;li&gt;throw: 예외를 catch 블록으로 던진다.&lt;/li&gt;
&lt;li&gt;catch: throw로 던진 예외를 받아서 처리한다.&lt;/li&gt;
&lt;li&gt;throw가 던지는 예외 형식과 일치할 때만 예외를 처리하도록 함.&lt;/li&gt;
&lt;li&gt;try_throw_catch_1.cpp 참고&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1746629496344&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try {
    // 예외를 던질 수 있는 코드      --&amp;gt; 1. 예외 발생 가능한 코드 블록
    throw 예외_값                 --&amp;gt; 2. 예외 발생
} catch (예외_형식 예외_이름) {     --&amp;gt; 3. 예외 형식 확인  
    // 예외를 처리하는 코드          --&amp;gt; 4. 예외 처리 블록
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;catch(...) 문으로 기타 예외 처리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 어떤 데이터 형식의 예외를 throw로 던졌는데, 받아주는 catch 문이 없다면?&lt;/li&gt;
&lt;li&gt;catch(...) 문으로 &lt;b&gt;처리 되지 않은 나머지 예외 모두 받기&lt;/b&gt;가 가능함.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1746629585202&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;catch (...) // 처리되지 않은 나머지 예외 모두 받기
{
  cout &amp;lt;&amp;lt; &quot;catch all&quot; &amp;lt;&amp;lt; endl;
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예외가 전달되는 순서&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 함수 내에서 예외를 던졌는데, 받아주는 catch가 없으면? --&amp;gt; 호출한 main 영역의 catch 문에서 정상적으로 처리된다.&lt;/li&gt;
&lt;li&gt;예외 처리의 책임은 throw가 발생한 함수를 &lt;b&gt;호출한 쪽&lt;/b&gt;으로 넘어간다. --&amp;gt; 이 현상을 &lt;b&gt;Stack unwinding, 스택 풀기&lt;/b&gt;라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;assertion, 어설션을 이용한 예외 처리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;assertion은 코드를 검증하여 예상치 못한 상황에서 프로그램 동작을 중단시키는 도구, 안전성과 신뢰성을 높여줌.&lt;/li&gt;
&lt;li&gt;C++에서는 헤더에 정의된 assert 매크로를 통하여 예외를 비교적 간단하게 처리할 수 있다.&lt;/li&gt;
&lt;li&gt;assert는 디버그 모드에서 오류가 생길수 있는 부분을 검사할 수 있는 매크로이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문법 요약&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예외가 발생할 수 있는 코드를 try 블록으로 감싼다.&lt;/li&gt;
&lt;li&gt;예외가 발생하면 프로그램의 제어는 즉시 try 블록 다음에 오는 catch 블록으로 넘어간다.&lt;/li&gt;
&lt;li&gt;throw 키워드로 예외를 던진다. throw 다음에 지정하는 예외 형식은 모든 유형이 될 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;catch 블록에는 throw로 던진 예외 형식으로 매개변수를 선언한다. 이 매개변수로 오류 메시지 같은 예외 정보에 접근한다.&lt;/li&gt;
&lt;li&gt;단일 try 블록에 catch 블록이 여러 개 있을 수 있으며, 각 블록은 서로 다른 유형의 예외를 처리하도록 정의한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1746629788148&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try {
 // 예외가 발생할 수 있는 코드 영역
 	throw exception_value; // 예외를 강제로 발생시키는 코드
} catch (exception_type e) {
	// 예외가 발생했을 때 실행되는 코드 블록
    // e에는 발생한 예외의 정보가 담겨 있음
    // exception_type은 실제 예외 형식에 따라 정의
 }&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;05-2 예외 처리 생략과 실패 대응&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예외 처리 메커니즘도 때로는 예외를 처리하지 못할 수 있음.&lt;/li&gt;
&lt;li&gt;noexcept와 set_terminate를 활용해 예외 처리와 예외 처리 실패에 대응하고 프로그램의 안정성과 신뢰성을 높이는 방법을 알아봄&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예외 처리 생략 - noexcept&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수에서 문제가 발생할 때, 반드시 예외를 발생시켜야하는 것음 아님.&lt;/li&gt;
&lt;li&gt;당연히 오류와 예외는 다름.&lt;/li&gt;
&lt;li&gt;값이나 실행 흐름을 충분히 예측할 수 있을 때는 if문으로 처리하는 것이 성능 면에서 훨씬 이득.&lt;/li&gt;
&lt;li&gt;함수가 예외를 던지지 않음을 나타낼 때는 다음처럼, noexcept 키워드로 명시할 수 있음.&lt;/li&gt;
&lt;li&gt;noexcept를 사용하여 &lt;b&gt;함수가 예외를 던지지 않음&lt;/b&gt;을 명시하면 컴파일러가 코드를 최적화하고 빠르게 실행하는데 도움이 됨.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-cpp&quot;&gt;int func() noexcept // 함수가 예외를 던지지 않음을 명시&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ 이 글은 직접 구매한『Do it! C++ 완전 정복』(문종채, 조규남 저, 성안당)을 참고하여 개인적으로 학습한 내용을 정리한 것입니다. 본문에 사용된 내용 및 예제 코드는 책의 내용을 기반으로 하되, 이해를 돕기 위한 개인적인 해석과 실습 결과를 포함하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고한&amp;nbsp;예제&amp;nbsp;코드:&amp;nbsp;[GitHub&amp;nbsp;-&amp;nbsp;mystous/DoItCPP](&lt;a href=&quot;https://github.com/mystous/DoItCPP)&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/mystous/DoItCPP)&lt;/a&gt;&amp;nbsp;&lt;br /&gt;예제&amp;nbsp;코드의&amp;nbsp;일부는&amp;nbsp;위&amp;nbsp;오픈소스&amp;nbsp;저장소를&amp;nbsp;참고하거나&amp;nbsp;수정하여&amp;nbsp;활용했으며,&amp;nbsp;해당&amp;nbsp;저장소는&amp;nbsp;학습용으로&amp;nbsp;공개되어&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발 언어/C++</category>
      <category>assert</category>
      <category>C++</category>
      <category>catch</category>
      <category>cpp</category>
      <category>Throw</category>
      <category>TRY</category>
      <category>예외처리</category>
      <author>lidarmansiwon</author>
      <guid isPermaLink="true">https://lidarmansiwon.tistory.com/5</guid>
      <comments>https://lidarmansiwon.tistory.com/5#entry5comment</comments>
      <pubDate>Wed, 7 May 2025 23:59:54 +0900</pubDate>
    </item>
    <item>
      <title>[C++ 기초부터 심화까지 Chapter 04. 실행 흐름 제어]</title>
      <link>https://lidarmansiwon.tistory.com/4</link>
      <description>&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이번 장에서는 특정 조건에 따라 프로그램의 실행 흐름을 제어하는 방법을 알아봄&lt;/li&gt;
&lt;li&gt;조건문과 반복문은 흐름을 조절하고 반복되는 작업을 자동화하는데 필수&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;04-1 조건문으로 흐름 제어&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;if 문으로 분기하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if문의 동작 방식은 매우 익숙함.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;if ()
{
    // 실행문1
}
else if()
{
    // 실행문2
}
else
{
    // 실행문3
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;switch 문으로 분기하기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;if else 문으로 처리할 수 있지만, 갈래가 많아지면 코드를 읽기가 어려워짐&lt;/li&gt;
&lt;li&gt;따라서 switch 문을 사용할 수 있음&lt;/li&gt;
&lt;li&gt;switch 문은 여러 경우의 수 가운데 하나를 선택해 실행할 때 사용함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;switch&lt;/b&gt;문의 동작 방식&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;switch 문은 하나의 변수나 표현식을 평가한 결과에 따라 case 중 하나를 선택해 해당 코드를 실행.&lt;/li&gt;
&lt;li&gt;이떄 label은 경우의 수를 나타내는 &lt;b&gt;상수&lt;/b&gt;임!!&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1746629037824&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;switch (표현식)
{
case 상수1 :
  // 상수 1일때 실행할 코드
  break;
case 상수2 :
  // 상수 2일때 실행할 코드
  break;
default :
  // 어떤 case에도 해당하지 않을 때, 실행할 코드
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;04-2 반목문으로 흐름 제어&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;do~while 문의 동작 방식&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;do~while은 while문처럼 동작하지만 코드블록을 먼저 실행한 다음, 조건을 검사함. 최소한 한 번은 실행되도록 보장&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt; 
using namespace std;

int main()
{
    int count = 0;
    do {
        cout &amp;lt;&amp;lt; count &amp;lt;&amp;lt; endl;
        count ++
    } while (count &amp;lt; 5);
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;for 문으로 반복하기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반복 횟수를 정확히 제어해야할 때 효과적으로 사용할 수 있음.&lt;/li&gt;
&lt;li&gt;초기화, 조건식, 증감식을 모두 한곳에서 관리하여 코드가 더 간결함.&lt;/li&gt;
&lt;li&gt;for문 형식&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1746629072377&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (초기화; 조건식; 증감식){
  // 반복 실행 코드
}&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;초기화: 반복이 시작되기 전에 한번만 실행되며, 변수를 선언하고 초기화&lt;/li&gt;
&lt;li&gt;조건식: 반복이 실행되기 전에 매번 평과됨. 참이면 반복 실행.&lt;/li&gt;
&lt;li&gt;증감식: 반복한 후에 실행되며 조건식에 사용된 변수를 업데이트.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;break 키워드로 원할 때, 반복문은 빠져나올 수 있음. (for, while, do~while 모두 적용 가능)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;04-3 표현식과 구문의 차이&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;표현식과 구문은 중요한 개념. 각각 다르게 사용됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;표현식(expression)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나 이상의 변수, 연산자, 리터럴을 조합해 값을 평가하고 결과를 반환.&lt;/li&gt;
&lt;li&gt;수학에서 수식과 같으며 &lt;b&gt;결과값&lt;/b&gt;이 항상 나오는 코드&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1746629091893&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 표현식 예시
a = b + c;
result = func();&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구문(statement)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나 이상의 연산을 수행하거나 동작을 실행하는 &lt;b&gt;명령문의 집합&lt;/b&gt;으로, 값을 할당하거나 프로그램의 실행 흐름을 제어함.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1746628927101&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 구문 예시
int a = 0;
while (true)
{
++a;
if (a &amp;gt; 10)
break;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ 이 글은 직접 구매한『Do it! C++ 완전 정복』(문종채, 조규남 저, 성안당)을 참고하여 개인적으로 학습한 내용을 정리한 것입니다. 본문에 사용된 내용 및 예제 코드는 책의 내용을 기반으로 하되, 이해를 돕기 위한 개인적인 해석과 실습 결과를 포함하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고한&amp;nbsp;예제&amp;nbsp;코드:&amp;nbsp;[GitHub&amp;nbsp;-&amp;nbsp;mystous/DoItCPP](&lt;a href=&quot;https://github.com/mystous/DoItCPP)&quot;&gt;https://github.com/mystous/DoItCPP)&lt;/a&gt;&amp;nbsp;&lt;br /&gt;예제&amp;nbsp;코드의&amp;nbsp;일부는&amp;nbsp;위&amp;nbsp;오픈소스&amp;nbsp;저장소를&amp;nbsp;참고하거나&amp;nbsp;수정하여&amp;nbsp;활용했으며,&amp;nbsp;해당&amp;nbsp;저장소는&amp;nbsp;학습용으로&amp;nbsp;공개되어&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발 언어/C++</category>
      <category>C++</category>
      <category>cpp</category>
      <category>do it! c++ 완전 정복</category>
      <category>실행 흐름 제어</category>
      <author>lidarmansiwon</author>
      <guid isPermaLink="true">https://lidarmansiwon.tistory.com/4</guid>
      <comments>https://lidarmansiwon.tistory.com/4#entry4comment</comments>
      <pubDate>Wed, 7 May 2025 23:39:17 +0900</pubDate>
    </item>
    <item>
      <title>[Fossen 리뷰] Handbook of Marine Craft Hydrodynamics and Motion Control 정리</title>
      <link>https://lidarmansiwon.tistory.com/3</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;해양 공학, 자율운항 선박, 동역학 제어 시스템에 관심이 있는 분들이라면 한 번쯤 들어봤을 책,&lt;br /&gt;Thor I. Fossen의 Handbook of Marine Craft Hydrodynamics and Motion Control.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;549&quot; data-start=&quot;460&quot; data-ke-size=&quot;size16&quot;&gt;이 책은 해양 선박 및 수중체의 운동 해석, 모델링, 제어 이론까지 폭넓게 다루고 있으며, 자율운항시스템을 연구하거나 개발하는 데 있어 거의 필독서로 꼽힘.&lt;/p&gt;
&lt;p data-end=&quot;549&quot; data-start=&quot;460&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;699&quot; data-start=&quot;551&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;왜 이 책을 공부하는가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;699&quot; data-start=&quot;551&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;현재 자율운항 선박의 제어 및 경로 계획 알고리즘을 연구하고 있음. 이 책은 그 이론적 기반이 되는 수학 모델과 실제 적용 예제를 폭넓게 담고 있어, 보다 체계적인 이해를 위해 본격적으로 공부하고 정리하려고 함.&lt;/p&gt;
&lt;p data-end=&quot;699&quot; data-start=&quot;551&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;732&quot; data-start=&quot;701&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;앞으로 포스팅할 예정 글&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;844&quot; data-start=&quot;733&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;750&quot; data-start=&quot;733&quot;&gt;각 장의 핵심 이론 요약&lt;/li&gt;
&lt;li data-end=&quot;770&quot; data-start=&quot;751&quot;&gt;중요한 수식 및 모델링 해설&lt;/li&gt;
&lt;li data-end=&quot;817&quot; data-start=&quot;771&quot;&gt;실제 응용 사례나 코드 분석 (필요시 Python/Simulink 등 활용)&lt;/li&gt;
&lt;li data-end=&quot;844&quot; data-start=&quot;818&quot;&gt;개인적인 이해를 돕기 위한 정리 및 해설&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;916&quot; data-start=&quot;846&quot; data-ke-size=&quot;size16&quot;&gt;이 과정을 통해 이 책의 내용을 보다 쉽게 풀어내고, 관련 분야를 공부하는 분들에게도 도움이 되는 자료를 만들어가고자 합니다.&lt;/p&gt;
&lt;p data-end=&quot;992&quot; data-start=&quot;975&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;992&quot; data-start=&quot;975&quot; data-ke-size=&quot;size16&quot;&gt;참고 도서: *Handbook of Marine Craft Hydrodynamics and Motion Control* by Thor I. Fossen&amp;nbsp;&amp;nbsp;&lt;br /&gt;이&amp;nbsp;글은&amp;nbsp;위&amp;nbsp;도서를&amp;nbsp;개인적으로&amp;nbsp;학습하고&amp;nbsp;이해한&amp;nbsp;내용을&amp;nbsp;기반으로&amp;nbsp;정리한&amp;nbsp;것이며,&amp;nbsp;책의&amp;nbsp;원문이나&amp;nbsp;도표,&amp;nbsp;수식&amp;nbsp;등을&amp;nbsp;무단&amp;nbsp;복제하지&amp;nbsp;않았습니다.&amp;nbsp;저작권은&amp;nbsp;저자&amp;nbsp;및&amp;nbsp;출판사에&amp;nbsp;있으며,&amp;nbsp;이&amp;nbsp;글은&amp;nbsp;학술적&amp;nbsp;참고&amp;nbsp;및&amp;nbsp;비영리&amp;nbsp;학습&amp;nbsp;목적으로&amp;nbsp;작성되었습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-end=&quot;992&quot; data-start=&quot;975&quot; data-ke-size=&quot;size16&quot;&gt;T.&amp;nbsp;I.&amp;nbsp;Fossen&amp;nbsp;and&amp;nbsp;T.&amp;nbsp;Perez&amp;nbsp;(2004).&amp;nbsp;Marine&amp;nbsp;Systems&amp;nbsp;Simulator&amp;nbsp;(MSS) &lt;br /&gt;URL:&amp;nbsp;&lt;a href=&quot;https://github.com/cybergalactic/MSS&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/cybergalactic/MSS&lt;/a&gt;&lt;br /&gt;실습&amp;nbsp;예제&amp;nbsp;및&amp;nbsp;코드&amp;nbsp;일부는&amp;nbsp;위의&amp;nbsp;오픈소스&amp;nbsp;저장소를&amp;nbsp;참고하거나&amp;nbsp;수정하여&amp;nbsp;활용하였으며,&amp;nbsp;저장소는&amp;nbsp;MIT&amp;nbsp;라이선스로&amp;nbsp;공개되어&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;992&quot; data-start=&quot;975&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;992&quot; data-start=&quot;975&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>이론 정리/Thor I. Fossen 리뷰</category>
      <category>fossen</category>
      <category>marinecraft</category>
      <category>maritimerobotics</category>
      <category>motioncontrol</category>
      <category>navigationcontrol</category>
      <category>자율선박</category>
      <category>자율운항</category>
      <category>해양공학</category>
      <author>lidarmansiwon</author>
      <guid isPermaLink="true">https://lidarmansiwon.tistory.com/3</guid>
      <comments>https://lidarmansiwon.tistory.com/3#entry3comment</comments>
      <pubDate>Wed, 7 May 2025 20:31:06 +0900</pubDate>
    </item>
  </channel>
</rss>