<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>happy_lee0_0.log</title>
        <link>https://velog.io/</link>
        <description>#자기공부 #틀린것도많음 #자기개발 여러분 인생이 힘들다 하더라도 그것을 깨는 순간 큰 희열감으로 옵니다~</description>
        <lastBuildDate>Mon, 28 Sep 2026 06:21:03 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>happy_lee0_0.log</title>
            <url>https://velog.velcdn.com/images/happy_lee0_0/profile/730560aa-03c4-4ab3-9e65-81b1710ecdb1/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. happy_lee0_0.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/happy_lee0_0" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[KL divergence]]></title>
            <link>https://velog.io/@happy_lee0_0/KL-divergence</link>
            <guid>https://velog.io/@happy_lee0_0/KL-divergence</guid>
            <pubDate>Mon, 28 Sep 2026 06:21:03 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가기에-앞서">들어가기에 앞서</h2>
<h3 id="정보-엔트로피information-entropy">정보 엔트로피(information entropy)</h3>
<h4 id="step1-정보량">step1. 정보량</h4>
<ul>
<li>어떤 사건이 자주 일어나면 별로 놀랍지 않고, 거의 안 일어나는 사건이 발생하면 놀라움</li>
<li>정보이론에서는 이 “놀라움의 정도”를 정보량이라고 봄</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/6331ca2d-963f-4de1-b7be-9c08502dfc81/image.png" alt=""></p>
<ul>
<li>어떤 사건의 확률이 0.5라면 정보량은 비교적 작고, 확률이 0.01이라면 정보량은 훨씬 큼 그러면 아래와 같이</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/7f90cc47-32d6-4972-a321-31104a3bc614/image.png" alt=""></p>
<h4 id="step2-왜--log-px를-사용할까">step2. 왜 -(log P(x))를 사용할까?</h4>
<ul>
<li>확룰이 작을수록 정보량이 커져야함</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/70d48145-dbe8-417f-87b0-58f8dd33f314/image.png" alt=""></p>
<ul>
<li>100% 일어나는 사건은 새로운 정보가 없음을 의미</li>
</ul>
<p>하지만</p>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/439feff1-e3fe-4726-ab4d-986fce78ac1d/image.png" alt=""></p>
<p>드문 사건일수록 정보량이 커짐</p>
<h4 id="step3-왜-로그를-쓰는가">step3. 왜 로그를 쓰는가?</h4>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/2d1623b6-ce7e-4d1e-9efe-363d6bf9a1ba/image.png" alt=""></p>
<p>로그 성질때문에 이렇게 진행이 됨</p>
<h4 id="step4-그러면-엔트로피는-무엇인가---정보량의-평균값">step4. 그러면 엔트로피는 무엇인가? -&gt; 정보량의 평균값</h4>
<ul>
<li>각 사건마다 정보량이 다르기 때문에 전체 시스템이 평균적으로 얼마나 많은 정보를 가지는지를 계산함</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/c6985ab5-ac7f-4791-97fa-0064367b67d4/image.png" alt=""></p>
<blockquote>
<p>발생 확률 × 그 사건의 정보량</p>
</blockquote>
<h5 id="step5-주사위로-보면">step5. 주사위로 보면</h5>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/82581b43-36d6-4caf-9896-f821b00e746a/image.png" alt=""></p>
<ul>
<li><p>해당 부분에서 머신러닝을 진행할때 softmax가 확률을 출력함</p>
</li>
<li><p>모델이 얼마나 확신하고 있는지 판단하는 데 사용할 수 있음</p>
</li>
<li><p>bit를 사용한 것은 log_{2}를 사용했을 떄의 정보량 단위임</p>
</li>
</ul>
<p>ex)
빨강 100%
파랑   0%</p>
<p>꺼내기 전에도
“무조건 빨간색이겠네.”</p>
<p>라고 알 수 있습니다.
별로 놀랍지 않습니다.
그래서 Entropy가 낮습니다.</p>
<p>빨간 사탕 50개, 파란 사탕 50개라면?
빨강 50%
파랑 50%</p>
<p>뽑기 전에는 무슨 색인지 모릅니다.
“빨간색일까? 파란색일까?”</p>
<p>불확실성이 큽니다.
그래서 Entropy가 높습니다.
즉, entropy는 결과가 얼마나 불확실한가를 본다.</p>
<hr>
<h3 id="크로스-엔트로피cross-entropy">크로스 엔트로피(cross entropy)</h3>
<p>실제 분포 (P)를 기준으로 봤을 때, 모델이 예측한 분포 (Q)가 얼마나 잘못되었는지를 나타내는 값
-&gt; 예측과 달라서 생기는 깜놀도(즉 정보량)을 의미함</p>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/29def015-173d-41a7-8592-cfbee0e1f668/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/8189e462-347e-4739-9fcc-bfcf7a4de540/image.png" alt=""></p>
<ul>
<li>핵심은 정답이 실제로 발생했는데, 모델이 그 정답에 낮은 확률을 줬다면 큰 손실을 준다는 것</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/6560ee4e-6ee4-4f97-b1a6-87e33366eb35/image.png" alt=""></p>
<ul>
<li>정답이 0 또는 1인 이진 분류에서는 다음과 같이 씀</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/b5c0e873-afb6-4f30-af0e-210a42a55fce/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/77ef8874-8657-408e-bd2e-c2603eea096b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/f433158b-2c86-4bcd-afbc-a5e651d08a01/image.png" alt=""></p>
<p>여기서 보이듯이</p>
<ul>
<li>정답이 높은 확률을 부여하면 -&gt; cross entropy가 작음</li>
<li>정답에 낮은 확률을 부여하면 -&gt; cross entropy 큼</li>
</ul>
<p>ex)
실제 정답은
고양이</p>
<p>입니다.
AI A
고양이 90%
강아지 10%</p>
<p>라고 예측했습니다.
정답인 고양이에 높은 확률을 줬죠.
그래서
“잘 맞췄네.”</p>
<p>Cross Entropy가 작습니다</p>
<p>AI B
고양이 1%
강아지 99%</p>
<p>라고 예측했습니다.
그런데 실제로는 고양이입니다.
AI는
“강아지라고 거의 확신했는데 고양이라고?”</p>
<p>하게 됩니다.
이런 경우 Cross Entropy가 매우 큽니다</p>
<p>정답과 예측이 얼마나 안맞는가를 봄</p>
<h3 id="entropy와-cross-entropy의-차이">Entropy와 Cross Entropy의 차이</h3>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/b40ef91f-ba56-4154-9dfe-34d439b3091d/image.png" alt=""></p>
<p>-&gt; 실제 분포 (P) 자체가 얼마나 불확실한가 = entropy</p>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/a39a0899-4ec7-4c1c-90de-8cbdb12e9614/image.png" alt=""></p>
<p>-&gt; 실제는 (P)인데 모델이 (Q)라고 생각했을 때 필요한 평균 정보량 = Cross Entropy</p>
<hr>
<h3 id="kl-divergence는">KL Divergence는?</h3>
<ul>
<li>KL Divergence는 두 확률분포 (P)와 (Q)가 얼마나 다른지를 정보량 관점에서 나타낸 값</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/58c91c6a-669b-4559-8d6f-bf00352dfd2b/image.png" alt=""><img src="https://velog.velcdn.com/images/happy_lee0_0/post/0b1fba23-1169-4efb-92c4-b1de873ba688/image.png" alt=""><img src="https://velog.velcdn.com/images/happy_lee0_0/post/34b3e5ae-8c90-460f-be83-f9dce873249b/image.png" alt=""><img src="https://velog.velcdn.com/images/happy_lee0_0/post/dc6ce1be-5155-426f-be2a-668f1c1652c1/image.png" alt=""></p>
<p>즉 해당 것은 모델 떄문에 추가로 발생한 차이임
두 생각의 차이가 얼마나 큰가로</p>
<p>ex)
이제 두 사람이 사탕 상자를 보고 있다고 합시다.
실제 상자는
빨강 80%
파랑 20%</p>
<p>입니다.
이게 실제 확률 분포 (P)입니다.
그런데 철수는
빨강 70%
파랑 30%</p>
<p>이라고 생각합니다.
실제와 꽤 비슷합니다.
그래서 KL Divergence가 작습니다.
그런데 영희는
빨강 10%
파랑 90%</p>
<p>이라고 생각합니다.
실제는 빨강이 대부분인데 완전히 반대로 생각하고 있습니다.
그래서 KL Divergence가 큽니다.
-&gt; 원래 문제 자체도 어려움
        +
내 예측도 틀림
        =
Cross Entropy가 큼</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MSE, MAE, RMSE]]></title>
            <link>https://velog.io/@happy_lee0_0/MSE-MAE-RMSE</link>
            <guid>https://velog.io/@happy_lee0_0/MSE-MAE-RMSE</guid>
            <pubDate>Mon, 28 Sep 2026 03:35:32 GMT</pubDate>
            <description><![CDATA[<h1 id="msemean-squared-error---평균-제곱-오차">MSE(Mean Squared Error) - 평균 제곱 오차</h1>
<h2 id="mse에-간단-개념">MSE에 간단 개념</h2>
<ul>
<li>오차(error)를 제곱한 값의 평균임</li>
<li>오차란 알고리즘이 예측한 값과 실제 정답과의 차이를 의미함</li>
<li>알고리즘이 정답을 잘 맞출수록 MSE 값은 작음</li>
<li>MSE 값은 작을수록 알고리즘의 성능이 좋다고 볼 수 있음</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/03120f90-90fa-4a96-8911-07372a61060d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/3595a57c-eead-4871-9ddc-3184afc0c9cd/image.png" alt=""></p>
<h2 id="오차-대비-큰-손실-함수의-증가폭">오차 대비 큰 손실 함수의 증가폭</h2>
<ul>
<li>MSE는 오차가 커질수록 손실 함수 값이 빠르게 증가하는 특징</li>
<li>하단 그림은 MSE를 좌표평면에 나타낸 것</li>
<li>손실 함수(E)의 크기는 오차의 제곱에 비례하여 변하는 것을 볼 수 있음</li>
<li>그만큼 미분값이 일정하지 않고 오차가 커질수록 미분값 역시 커지는 것을 알 수 있음</li>
<li>MSE와는 다르게, 평균절대오차(MAE)는 오차가 커질수록 손실 함수가 선형적으로 증가함</li>
<li>MAE와 비교했을 때, MSE가 비교적 오차의 변화량에 따라 손실 함수 값이 크게 변한다는 것을 알 수 있음</li>
<li>MSE는 회귀(Regression) 문제에 자주 활용 오차를 줄여가면서 업데이트</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/305a6358-5825-4f61-9c06-195d0f7c273f/image.png" alt=""></p>
<hr>
<h1 id="maemean-absolute-error---평균-절대-오차">MAE(Mean Absolute Error) - 평균 절대 오차</h1>
<h2 id="mae에-간단-개념">MAE에 간단 개념</h2>
<ul>
<li>오차(error)를 절댓값을 계산한 뒤 평균한 값임</li>
<li>오차란 알고리즘이 예측한 값과 실제 정답과의 차이를 의미함</li>
<li>알고리즘이 정답을 잘 맞출수록 MAE 값은 작고, MAE 값이 작을수록 알고리즘의 성능이 좋다고 볼 수 있음</li>
<li>MSE와 달리 오차를 제곱하지 않기 때문에 큰 오차에 상대적으로 덜 민감함</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/82759e36-99ae-4677-84d8-accbe2d6f9c3/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/ce9df104-60dc-4469-9689-84a07e0671cc/image.png" alt=""></p>
<hr>
<h1 id="rmseroot-mean-squared-error---평균-제곱근-오차">RMSE(Root Mean Squared Error) - 평균 제곱근 오차</h1>
<ul>
<li>오차를 제곱하여 평균한 뒤 다시 제곱근을 취한 값임</li>
<li>오차란 알고리즘이 예측한 값과 실제 정답과의 차이를 의미함</li>
<li>알고리즘이 정답을 잘 맞출수록 RMSE 값은 작고, RMSE 값이 작을수록 알고리즘의 성능이 좋다고 볼 수 있음</li>
<li>오차를 제곱하기 때문에 큰 오차에 더 큰 페널티를 부여하는 특징이 있음</li>
<li>마지막에 제곱근을 취하기 때문에 실제 데이터와 동일한 단위로 해석할 수 있음</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/d5472c2d-a324-4c2e-944e-117ef852b84c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/b6ce0fee-38b3-4f8f-9825-0af50356a134/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Making a csv file]]></title>
            <link>https://velog.io/@happy_lee0_0/Making-a-csv-file</link>
            <guid>https://velog.io/@happy_lee0_0/Making-a-csv-file</guid>
            <pubDate>Thu, 20 Aug 2026 19:35:31 GMT</pubDate>
            <description><![CDATA[<h1 id="pcd에서-로봇-본딩-경로-생성하기">PCD에서 로봇 본딩 경로 생성하기</h1>
<h2 id="step-2-경로-검출과-step-2-1-정밀-보정-알고리즘-정리">Step 2 경로 검출과 Step 2-1 정밀 보정 알고리즘 정리</h2>
<blockquote>
<p><strong>핵심 키워드</strong>
Point Cloud, PCD, Top-envelope, 경계 검출, Gaussian Filter, Median Filter, Savitzky-Golay Filter, MAD, Percentile, Arc-length Parameterization, Tangent Vector</p>
</blockquote>
<hr>
<h2 id="1-들어가며">1. 들어가며</h2>
<p>이번 글에서는 안경 프레임의 PCD(Point Cloud Data)에서 얇은 본딩 면을 검출하고, 로봇이 접착제를 도포할 수 있는 3차원 중앙 경로를 만드는 과정을 정리한다.</p>
<p>전체 알고리즘은 크게 두 단계로 구성된다.</p>
<ul>
<li><strong>Step 2:</strong> 원본 PCD에서 본딩 면의 Outer/Inner 경계를 검출하고 기본 중앙 경로 생성</li>
<li><strong>Step 2-1:</strong> 기본 경로의 짧은 Wave와 이상점을 제거하고 실제 PCD 단면을 이용해 정밀 보정</li>
</ul>
<p>Step 2의 구현 순서는 PCD의 XY 격자 투영, Outer/Inner 경계 검출, 중앙 경로 계산, 필터링, 일정 간격 재샘플링 및 결과 저장으로 구성된다.</p>
<p>Step 2-1은 안경 프레임의 큰 형상은 유지하면서 국부적인 경계 돌출, 중앙선 진동, 면 폭의 급격한 변화를 제거한다. 실제 로봇의 안전 높이, 즉 World (+Z) 방향 Offset은 Step 2나 Step 2-1이 아니라 Step 3에서만 적용한다.</p>
<hr>
<h2 id="2-전체-데이터-흐름">2. 전체 데이터 흐름</h2>
<pre><code class="language-text">AI_Glass_Front_0.010mm.pcd
        │
        ▼
Step 2: generate_global_path.py
        │
        ├─ XY Top-envelope 생성
        ├─ Outer/Inner edge 검출
        ├─ 얇은 본딩 면 추출
        ├─ 기본 중앙 경로 생성
        └─ 0.5 mm 간격 재샘플링
        │
        ├─ step2_thin_bonding_face.pcd
        ├─ step2_global_path.csv
        ├─ step2_global_path.pcd
        └─ step2_global_path.npz
        │
        ▼
Step 2-1: smooth_global_path.py
        │
        ├─ 이상점 제거
        ├─ Y/Z 경로 평활화
        ├─ 실제 PCD 단면 재측정
        ├─ 중앙 경로 재보정
        ├─ 면 폭 및 방향 재구성
        └─ 최종 접선 재계산
        │
        ├─ step2_1_accurate_global_path.csv
        ├─ step2_1_accurate_global_path.pcd
        ├─ step2_1_accurate_global_path.npz
        └─ step2_1_accuracy_metrics.json
        │
        ▼
Step 3: Quaternion trajectory 및 IK</code></pre>
<p>수학적으로는 다음과 같이 표현할 수 있다.</p>
<p>$$
\mathcal{P}
\xrightarrow{\text{Rasterization}}
(A,Z_{\text{top}})
\xrightarrow{\text{Edge Detection}}
(\mathbf O,\mathbf I)
\xrightarrow{\text{Centerline}}
\mathbf C
\xrightarrow{\text{Regularization}}
\hat{\mathbf C}
$$</p>
<p>여기서 각 기호는 다음을 의미한다.</p>
<table>
<thead>
<tr>
<th>기호</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>(\mathcal P)</td>
<td>원본 PCD 점군</td>
</tr>
<tr>
<td>(A)</td>
<td>XY 격자의 점 존재 여부</td>
</tr>
<tr>
<td>(Z_{\text{top}})</td>
<td>XY 위치별 윗면 높이</td>
</tr>
<tr>
<td>(\mathbf O)</td>
<td>Outer edge</td>
</tr>
<tr>
<td>(\mathbf I)</td>
<td>Inner edge</td>
</tr>
<tr>
<td>(\mathbf C)</td>
<td>Step 2 기본 중앙 경로</td>
</tr>
<tr>
<td>(\hat{\mathbf C})</td>
<td>Step 2-1 최종 보정 경로</td>
</tr>
</tbody></table>
<hr>
<h1 id="3-기본-수학-표현">3. 기본 수학 표현</h1>
<p>원본 PCD는 다음과 같은 3차원 점들의 집합이다.</p>
<p>$$
\mathcal P
==========</p>
<p>\left{
\mathbf p_n
===========</p>
<p>\begin{bmatrix}
x_n<br>y_n<br>z_n
\end{bmatrix}
\right}_{n=1}^{N}
$$</p>
<p>최종적으로 생성하고 싶은 경로는 경로 거리 (s)에 따른 3차원 곡선이다.</p>
<p>$$
\mathbf C(s)
============</p>
<p>\begin{bmatrix}
x(s)<br>y(s)<br>z(s)
\end{bmatrix}
$$</p>
<p>본딩 면의 양쪽 경계를 각각 다음과 같이 정의한다.</p>
<p>$$
\mathbf O(s)
============</p>
<p>\begin{bmatrix}
O_x(s)<br>O_y(s)<br>O_z(s)
\end{bmatrix}
$$</p>
<p>$$
\mathbf I(s)
============</p>
<p>\begin{bmatrix}
I_x(s)<br>I_y(s)<br>I_z(s)
\end{bmatrix}
$$</p>
<p>양 경계의 중앙, 폭, 방향은 다음과 같이 계산할 수 있다.</p>
<h3 id="중앙-경로">중앙 경로</h3>
<p>$$
\mathbf C(s)
============</p>
<p>\frac{\mathbf O(s)+\mathbf I(s)}{2}
$$</p>
<h3 id="본딩-면의-폭">본딩 면의 폭</h3>
<p>$$
w(s)
====</p>
<p>\left|
\mathbf O(s)-\mathbf I(s)
\right|_2
$$</p>
<h3 id="outer-방향-단위벡터">Outer 방향 단위벡터</h3>
<p>$$
\mathbf d(s)
============</p>
<p>\frac{
\mathbf O(s)-\mathbf I(s)
}{
\left|
\mathbf O(s)-\mathbf I(s)
\right|_2
}
$$</p>
<h3 id="경로-단위-접선">경로 단위 접선</h3>
<p>$$
\mathbf t(s)
============</p>
<p>\frac{
d\mathbf C(s)/ds
}{
\left|
d\mathbf C(s)/ds
\right|_2
}
$$</p>
<p>이 정보는 이후 Step 3에서 노즐의 위치와 회전 자세를 계산할 때 사용된다.</p>
<hr>
<h1 id="4-step-2-pcd에서-기본-경로-생성">4. Step 2: PCD에서 기본 경로 생성</h1>
<h2 id="41-pcd를-xy-raster로-변환하는-이유">4.1 PCD를 XY Raster로 변환하는 이유</h2>
<p>PCD는 점들이 불규칙한 간격으로 존재하는 비정형 데이터이다.</p>
<p>이 상태에서 바로 경계 검출이나 미분을 수행하면 점 밀도와 분포에 따라 결과가 크게 달라질 수 있다. 따라서 XY 평면을 일정한 크기의 격자로 나누고, 각 셀에 대표 높이를 저장한다.</p>
<p>현재 Raster 해상도는 다음과 같다.</p>
<p>$$
\Delta=0.02\text{ mm}
$$</p>
<p>점 ((x_n,y_n))이 속하는 격자 인덱스는 다음과 같이 계산한다.</p>
<p>$$
u_n
===</p>
<p>\left\lfloor
\frac{x_n-x_{\min}}{\Delta}
\right\rfloor
$$</p>
<p>$$
v_n
===</p>
<p>\left\lfloor
\frac{y_n-y_{\min}}{\Delta}
\right\rfloor
$$</p>
<p>Raster 해상도가 (0.02\text{ mm})이므로 셀 중심 기준 좌표 양자화 오차는 이론적으로 약 다음 범위에 존재한다.</p>
<p>$$
\epsilon_x,\epsilon_y
\approx
\pm\frac{0.02}{2}
=================</p>
<p>\pm0.01\text{ mm}
$$</p>
<hr>
<h2 id="42-occupancy-map과-top-envelope">4.2 Occupancy Map과 Top-envelope</h2>
<p>각 XY 셀에 점이 존재하는지를 다음과 같이 정의한다.</p>
<p>$$
A(v,u)
======</p>
<p>\begin{cases}
1, &amp; \text{셀 내부에 PCD 점이 존재}<br>0, &amp; \text{셀 내부에 PCD 점이 없음}
\end{cases}
$$</p>
<p>그리고 각 셀에서 물리적으로 가장 위쪽에 있는 점을 대표 높이로 저장한다.</p>
<p>현재 Isaac 좌표 변환에서는 Local Z 방향이 물리적 높이 방향과 반대로 정의되어 있기 때문에, Local Z가 가장 작은 점이 실제 윗면에 해당한다.</p>
<p>$$
Z_{\text{top}}(v,u)
===================</p>
<p>\min_{\mathbf p_n\in\text{cell}(v,u)}z_n
$$</p>
<p>코드에서는 PCD를 Chunk 단위로 읽고, 각 셀의 점 존재 여부와 최소 Local Z를 저장한다.</p>
<p>실제 Raster 인덱스 계산과 최소 Z 저장은 다음 처리에 대응한다.</p>
<pre><code class="language-python">px = floor((x - x_min) / raster)
py = floor((y - y_min) / raster)

occupied[py, px] = True
min_z[py, px] = min(min_z[py, px], z)</code></pre>
<p>구현에서는 <code>np.minimum.at()</code>을 이용해 셀별 최소 Z를 계산한다.</p>
<p>이 결과는 완전한 3차원 모델이라기보다 다음 형태의 <strong>2.5D Height Map</strong>이다.</p>
<p>$$
z=f(x,y)
$$</p>
<p>즉, 하나의 XY 위치에 하나의 대표 높이를 저장하는 방식이다.</p>
<hr>
<h2 id="43-비어-있는-셀의-높이-보완">4.3 비어 있는 셀의 높이 보완</h2>
<p>중앙 경로의 XY 위치에서 PCD 셀이 비어 있을 수 있다.</p>
<p>이 경우 현재 셀 주변의 (5\times5) 영역을 확인하고, 유효 높이들의 중앙값을 사용한다.</p>
<p>$$
\hat Z_{\text{top}}(u,v)
========================</p>
<p>\operatorname{median}
\left{
Z_{\text{top}}(u+i,v+j)
\right}
$$</p>
<p>$$
i,j\in{-2,-1,0,1,2}
$$</p>
<p>평균이 아니라 중앙값을 사용하는 이유는 일부 이상 높이점의 영향을 줄이기 위해서이다.</p>
<p>코드에서도 비어 있는 위치를 주변 (5\times5) 패치의 유효 높이 중앙값으로 보완한다.</p>
<hr>
<h1 id="5-outer-edge-검출">5. Outer edge 검출</h1>
<p>각 X 열에서 Occupied 상태인 Y 좌표 중 가장 큰 값을 Outer edge로 선택한다.</p>
<p>$$
O_y(x_j)
========</p>
<p>\max
\left{
y_k
\mid
A(y_k,x_j)=1
\right}
$$</p>
<p>즉, 위에서 바라본 XY Occupancy Map에서 (+Y) 방향으로 가장 바깥쪽에 있는 윤곽을 찾는 방식이다.</p>
<pre><code class="language-text">Y 증가 방향
     ↑
     │       Outer edge
     │       ●────────●
     │       │ 본딩 면 │
     │       ●────────●
     │       Inner edge
     └──────────────────→ X</code></pre>
<p>이 방식에는 다음 가정이 포함되어 있다.</p>
<blockquote>
<p>안경 프레임의 바깥쪽 방향이 현재 좌표계에서 (+Y) 방향이다.</p>
</blockquote>
<p>제품의 방향이 반대로 배치되면 <code>max Y</code>가 아니라 <code>min Y</code>를 사용해야 한다.</p>
<hr>
<h1 id="6-inner-edge-검출">6. Inner edge 검출</h1>
<h2 id="61-검색-범위-설정">6.1 검색 범위 설정</h2>
<p>Inner edge는 Outer edge에서 안쪽으로 다음 범위에서 탐색한다.</p>
<p>$$
d_{\min}=0.35\text{ mm}
$$</p>
<p>$$
d_{\max}=2.80\text{ mm}
$$</p>
<p>따라서 검색 영역은 다음과 같다.</p>
<p>$$
O_y(x_j)-2.80
\le y\le
O_y(x_j)-0.35
$$</p>
<p>이 범위는 Inner edge가 존재할 것으로 예상되는 공간적 사전정보이다.</p>
<hr>
<h2 id="62-y-방향-z-프로파일">6.2 Y 방향 Z 프로파일</h2>
<p>특정 X 위치 (x_j)에서 Y 방향 높이 분포를 다음과 같이 정의한다.</p>
<p>$$
z_j(y)
======</p>
<p>Z_{\text{top}}(x_j,y)
$$</p>
<p>Inner edge는 본딩 윗면과 홈 사이에서 높이가 급격히 바뀌는 지점이다.</p>
<p>하지만 원본 프로파일에는 PCD 노이즈와 Raster 계단이 존재하므로 미분 전에 Gaussian filter를 적용한다.</p>
<hr>
<h2 id="63-gaussian-filter">6.3 Gaussian Filter</h2>
<p>Gaussian 함수는 다음과 같다.</p>
<p>$$
G_\sigma(\tau)
==============</p>
<p>\frac{1}{\sqrt{2\pi}\sigma}
\exp
\left(
-\frac{\tau^2}{2\sigma^2}
\right)
$$</p>
<p>평활화된 높이 프로파일은 다음과 같다.</p>
<p>$$
\tilde z_j(y)
=============</p>
<p>(G_\sigma*z_j)(y)
$$</p>
<p>현재 사용되는 물리적 표준편차는 다음과 같다.</p>
<p>$$
\sigma=0.03\text{ mm}
$$</p>
<p>Raster 간격이 (0.02\text{ mm})이므로 셀 단위 표준편차는 약 다음과 같다.</p>
<p>$$
\sigma_{\text{cell}}
====================</p>
<h1 id="frac003002">\frac{0.03}{0.02}</h1>
<p>1.5
$$</p>
<p>즉, 약 1.5셀 범위의 짧은 측정 노이즈를 완화한다.</p>
<hr>
<h2 id="64-높이-미분으로-경계-찾기">6.4 높이 미분으로 경계 찾기</h2>
<p>Gaussian filter를 적용한 높이를 Y 방향으로 미분한다.</p>
<p>$$
g_j(y)
======</p>
<p>\frac{d\tilde z_j(y)}{dy}
$$</p>
<p>이산 데이터에서는 다음 중앙 차분과 유사한 방식으로 계산할 수 있다.</p>
<p>$$
g_j(y_k)
\approx
\frac{
\tilde z_j(y_{k+1})</p>
<hr>
<p>\tilde z_j(y_{k-1})
}{
2\Delta
}
$$</p>
<p>현재 Local Z가 물리 높이와 반대이기 때문에 가장 음의 방향으로 강하게 변화하는 위치를 선택한다.</p>
<p>$$
k^*
===</p>
<p>\arg\min_k g_j(y_k)
$$</p>
<p>$$
I_y(x_j)
========</p>
<p>y_{k^*}
$$</p>
<p>경계 강도는 다음과 같이 정의된다.</p>
<p>$$
E_j
===</p>
<p>-g_j(y_{k^*})
$$</p>
<p>실제 구현에서도 Gaussian filter 이후 <code>np.gradient()</code>로 미분하고, <code>argmin()</code> 위치를 Inner edge로 선택한다.</p>
<hr>
<h2 id="65-유효-경계-판정">6.5 유효 경계 판정</h2>
<p>Outer와 Inner 사이의 폭은 다음과 같다.</p>
<p>$$
w_j
===</p>
<p>O_y(x_j)-I_y(x_j)
$$</p>
<p>검출된 경계는 다음 조건을 만족해야 한다.</p>
<p>$$
0.35
\le
w_j
\le
2.80
$$</p>
<p>$$
E_j
\ge
0.15
$$</p>
<p>폭이나 경계 강도 조건을 만족하지 않는 X 열은 무효 처리한 뒤, 인접한 유효 열을 이용해 보간한다.</p>
<p>또한 전체 X 열 중 유효 경계쌍이 80% 미만이면 경로 생성에 실패한 것으로 판단한다.</p>
<hr>
<h1 id="7-경계-평활화">7. 경계 평활화</h1>
<p>검출된 Outer/Inner edge에는 Raster 계단과 국부적인 돌출점이 포함될 수 있다.</p>
<p>따라서 다음 순서로 평활화한다.</p>
<pre><code class="language-text">검출 경계
   │
   ▼
Median Filter
   │
   ▼
Savitzky-Golay Filter
   │
   ▼
평활화된 경계</code></pre>
<hr>
<h2 id="71-median-filter">7.1 Median Filter</h2>
<p>길이 (2K+1)의 Median filter는 다음과 같다.</p>
<p>$$
\hat y_i
========</p>
<p>\operatorname{median}
\left(
y_{i-K},
\ldots,
y_i,
\ldots,
y_{i+K}
\right)
$$</p>
<p>예를 들어 다음과 같은 데이터가 있다고 가정하자.</p>
<pre><code class="language-text">1.01, 1.00, 1.02, 1.85, 1.01, 1.00</code></pre>
<p>여기서 <code>1.85</code>는 국부적인 이상점이다.</p>
<p>평균 필터는 <code>1.85</code>의 영향을 받지만 Median filter는 주변의 정상값을 기준으로 이상점을 제거할 수 있다.</p>
<hr>
<h2 id="72-savitzky-golay-filter">7.2 Savitzky-Golay Filter</h2>
<p>Savitzky-Golay filter는 주변 데이터를 단순 평균하지 않고 저차 다항식으로 근사한다.</p>
<p>3차 다항식을 사용하면 다음과 같다.</p>
<p>$$
p_i(\tau)
=========</p>
<p>a_0+a_1\tau+a_2\tau^2+a_3\tau^3
$$</p>
<p>각 중심점 주변에서 다음 최소제곱 문제를 해결한다.</p>
<p>$$
\min_{a_0,a_1,a_2,a_3}
\sum_{k=-K}^{K}
\left[
y_{i+k}</p>
<hr>
<p>p_i(k\Delta)
\right]^2
$$</p>
<p>평활화된 중심값은 다음과 같다.</p>
<p>$$
\hat y_i
========</p>
<h1 id="p_i0">p_i(0)</h1>
<p>a_0
$$</p>
<p>이 방식은 이동평균보다 곡선의 경사와 곡률을 잘 유지한다.</p>
<p>코드에서는 Median filter 이후 3차 Savitzky-Golay filter를 적용한다.</p>
<hr>
<h2 id="73-step-2-필터-파라미터">7.3 Step 2 필터 파라미터</h2>
<table>
<thead>
<tr>
<th>적용 대상</th>
<th align="right">Median 창</th>
<th align="right">SG 창</th>
<th align="right">다항식 차수</th>
</tr>
</thead>
<tbody><tr>
<td>Outer/Inner Y</td>
<td align="right">0.50 mm</td>
<td align="right">3.00 mm</td>
<td align="right">3</td>
</tr>
<tr>
<td>중앙 표면 Z</td>
<td align="right">0.40 mm</td>
<td align="right">1.20 mm</td>
<td align="right">3</td>
</tr>
</tbody></table>
<p>경계와 높이에 서로 다른 필터 창이 설정되어 있다.</p>
<p>XY 경계에는 비교적 긴 창을 사용해 Raster wave를 충분히 제거한다.</p>
<p>반면 Z에 긴 창을 적용하면 실제 급경사 형상이 평탄해질 수 있으므로 더 짧은 창을 사용한다.</p>
<hr>
<h1 id="8-중앙-경로-계산">8. 중앙 경로 계산</h1>
<p>평활화된 Outer와 Inner의 Y 좌표를 이용해 중앙 Y를 계산한다.</p>
<p>$$
C_x(x_j)=x_j
$$</p>
<p>$$
C_y(x_j)
========</p>
<p>\frac{
O_y(x_j)+I_y(x_j)
}{2}
$$</p>
<p>중앙 Z는 Outer와 Inner의 Z를 단순 평균하지 않는다.</p>
<p>중앙 XY 위치에서 Top-envelope 높이를 다시 측정한다.</p>
<p>$$
C_z(x_j)
========</p>
<p>Z_{\text{top}}
\left(
x_j,C_y(x_j)
\right)
$$</p>
<p>따라서 중앙점은 다음과 같다.</p>
<p>$$
\mathbf C_j
===========</p>
<p>\begin{bmatrix}
x_j<br>\dfrac{O_y(x_j)+I_y(x_j)}{2}<br>Z_{\text{top}}\left(x_j,C_y(x_j)\right)
\end{bmatrix}
$$</p>
<p>코드에서도 <code>center_y=(outer_y+inner_y)/2</code>를 계산한 후 해당 중앙 위치의 Top-envelope Z를 읽어 사용한다.</p>
<p>이 방식의 핵심은 다음과 같다.</p>
<blockquote>
<p>XY는 양쪽 경계의 중앙을 사용하고, Z는 그 중앙 위치에서 측정한 실제 윗면 높이를 사용한다.</p>
</blockquote>
<hr>
<h1 id="9-얇은-본딩-면-pcd-추출">9. 얇은 본딩 면 PCD 추출</h1>
<p>모든 Top-envelope 점을 사용하는 것이 아니라, 양쪽 경계 사이의 실제 얇은 윗면만 별도의 PCD로 추출한다.</p>
<p>첫 번째 조건은 점이 Inner와 Outer 사이에 존재하는 것이다.</p>
<p>$$
I_y(x)
\le
y
\le
O_y(x)
$$</p>
<p>두 번째 조건은 점의 높이가 중앙 높이와 (0.15\text{ mm}) 이내에 존재하는 것이다.</p>
<p>$$
\left|
z-C_z(x)
\right|
\le
0.15\text{ mm}
$$</p>
<p>따라서 얇은 면 점군은 다음과 같다.</p>
<p>$$
\mathcal F
==========</p>
<p>\left{
(x,y,z)
\in
\mathcal P_{\text{top}}
;\middle|;
I_y(x)\le y\le O_y(x),
\left|z-C_z(x)\right|\le0.15
\right}
$$</p>
<p>코드에서도 경계 사이에 존재하고 중앙 Z에서 (\pm0.15\text{ mm}) 이내인 점만 얇은 본딩 면 PCD로 저장한다.</p>
<p>이 조건은 홈 내부나 옆면처럼 높이가 다른 점을 제거하는 역할을 한다.</p>
<hr>
<h1 id="10-양-끝-직각-전환부-제거">10. 양 끝 직각 전환부 제거</h1>
<p>안경 프레임 양 끝에는 본딩 경로가 아닌 수평 탭이나 급격한 직각 전환부가 존재할 수 있다.</p>
<p>이를 제거하기 위해 인접 경로점의 XY 이동량과 Z 변화량을 계산한다.</p>
<p>$$
\Delta r_{xy,i}
===============</p>
<p>\sqrt{
(\Delta x_i)^2+
(\Delta y_i)^2
}
$$</p>
<p>$$
\Delta z_i
==========</p>
<p>z_{i+1}-z_i
$$</p>
<p>XY 평면 기준 경사각은 다음과 같다.</p>
<p>$$
\theta_i
========</p>
<p>\operatorname{atan2}
\left(
|\Delta z_i|,
\sqrt{
(\Delta x_i)^2+
(\Delta y_i)^2
}
\right)
$$</p>
<p>각도를 Degree로 변환하면 다음과 같다.</p>
<p>$$
\theta_{i,\deg}
===============</p>
<p>\theta_i
\frac{180}{\pi}
$$</p>
<p>기본적으로 다음 조건을 만족하는 첫 구간과 마지막 구간을 탐색한다.</p>
<p>$$
\theta_{i,\deg}
\ge
55^\circ
$$</p>
<p>이 (55^\circ)는 안경의 설계각이 아니다.</p>
<p>로봇 자세가 갑자기 변하는 양 끝 전환부를 검출하기 위한 임계값이다.</p>
<p>CLI 실행에서는 <code>--no-endpoint-trim</code>을 사용하지 않는 한 양 끝 전환부 제거가 활성화된다.</p>
<hr>
<h1 id="11-일정-간격-경로-재샘플링">11. 일정 간격 경로 재샘플링</h1>
<p>경계에서 계산한 중앙점들은 X Raster 간격으로 생성되었기 때문에 3차원 실제 거리 간격은 일정하지 않다.</p>
<p>따라서 먼저 3차원 누적 거리를 계산한다.</p>
<p>인접 경로점 사이의 거리는 다음과 같다.</p>
<p>$$
\ell_i
======</p>
<p>\left|
\mathbf C_i-\mathbf C_{i-1}
\right|_2
$$</p>
<p>누적 거리는 다음과 같다.</p>
<p>$$
s_0=0
$$</p>
<p>$$
s_i
===</p>
<p>\sum_{k=1}^{i}\ell_k
$$</p>
<p>최종 경로 길이는 다음과 같다.</p>
<p>$$
L=s_N
$$</p>
<p>목표 경로 간격은 다음과 같다.</p>
<p>$$
\Delta s
========</p>
<p>0.5\text{ mm}
$$</p>
<p>목표 Station은 다음과 같이 생성한다.</p>
<p>$$
s_j^*
=====</p>
<p>j\Delta s
$$</p>
<p>$$
j
=</p>
<p>0,1,\ldots,
\left\lfloor
\frac{L}{\Delta s}
\right\rfloor
$$</p>
<hr>
<h2 id="111-선형-보간">11.1 선형 보간</h2>
<p>목표 거리 (s_j^*)가 다음 범위에 존재한다고 가정한다.</p>
<p>$$
s_i
\le
s_j^*
\le
s_{i+1}
$$</p>
<p>보간 비율은 다음과 같다.</p>
<p>$$
\alpha
======</p>
<p>\frac{
s_j^*-s_i
}{
s_{i+1}-s_i
}
$$</p>
<p>보간된 경로점은 다음과 같다.</p>
<p>$$
\mathbf C(s_j^*)
================</p>
<p>(1-\alpha)\mathbf C_i
+
\alpha\mathbf C_{i+1}
$$</p>
<p>코드에서는 중앙선의 3차원 누적 거리를 기준으로 X, Y, Z를 각각 선형 보간하여 기본 (0.5\text{ mm}) 간격의 경로를 만든다.</p>
<hr>
<h1 id="12-접선-벡터-계산">12. 접선 벡터 계산</h1>
<p>연속적인 곡선의 접선은 다음과 같다.</p>
<p>$$
\frac{d\mathbf C}{ds}
$$</p>
<p>이산 경로에서는 중앙 차분으로 근사할 수 있다.</p>
<p>$$
\mathbf v_i
\approx
\frac{
\mathbf C_{i+1}-\mathbf C_{i-1}
}{
s_{i+1}-s_{i-1}
}
$$</p>
<p>접선의 크기를 1로 만들기 위해 정규화한다.</p>
<p>$$
\mathbf t_i
===========</p>
<p>\frac{
\mathbf v_i
}{
|\mathbf v_i|_2
}
$$</p>
<p>따라서 다음 조건을 만족한다.</p>
<p>$$
|\mathbf t_i|_2=1
$$</p>
<p>코드에서는 누적 거리 기준으로 <code>np.gradient()</code>를 적용하고, 각 미분 벡터를 정규화한다.</p>
<p>이 접선은 Step 3에서 로봇 Tool의 진행 방향을 구성할 때 사용된다.</p>
<hr>
<h1 id="13-kd-tree를-이용한-경로-검증">13. KD-tree를 이용한 경로 검증</h1>
<p>생성된 중앙 경로가 실제 얇은 면 PCD 위에 위치하는지를 검증한다.</p>
<p>각 중앙점 (\mathbf C_i)에서 얇은 면 PCD까지의 최근접 거리를 다음과 같이 계산한다.</p>
<p>$$
d_i
===</p>
<p>\min_{\mathbf q\in\mathcal F}
\left|
\mathbf C_i-\mathbf q
\right|_2
$$</p>
<p>모든 PCD 점과 직접 거리를 계산하면 연산량이 크기 때문에 KD-tree를 사용한다.</p>
<p>코드에서도 <code>cKDTree</code>를 이용해 각 중앙점에서 얇은 면 PCD까지의 최근접 거리를 계산한다.</p>
<p>해석 방법은 다음과 같다.</p>
<ul>
<li>(d_i)가 작다: 경로가 실제 추출 면 위에 잘 위치함</li>
<li>(d_i)가 크다: 경로가 표면에서 이탈했거나 PCD가 희박함</li>
</ul>
<p>평균값만 보는 것보다 평균, P95, 최댓값을 함께 확인하는 것이 좋다.</p>
<hr>
<h1 id="14-step-2-1-정밀-보정">14. Step 2-1: 정밀 보정</h1>
<p>Step 2에서 생성된 경로에는 다음 오차가 남을 수 있다.</p>
<ul>
<li>Raster 격자에 의한 짧은 계단</li>
<li>Inner edge 순간 오검출</li>
<li>PCD 이상점에 의한 중앙선 돌출</li>
<li>Y 방향의 짧은 Wave</li>
<li>Z 방향의 국부적인 측정 진동</li>
<li>본딩 면 폭의 급격한 변화</li>
</ul>
<p>Step 2-1은 전체 안경 형상을 바꾸지 않고 이러한 국부적인 문제만 제거하는 단계이다.</p>
<hr>
<h1 id="15-필터-길이를-샘플-수로-변환">15. 필터 길이를 샘플 수로 변환</h1>
<p>Step 2-1의 필터 크기는 샘플 개수가 아니라 mm 단위로 설정된다.</p>
<p>평균 경로 간격을 (\Delta s), 원하는 물리적 필터 길이를 (W)라고 하면 필요한 샘플 수는 다음과 같다.</p>
<p>$$
N
=</p>
<p>\operatorname{round}
\left(
\frac{W}{\Delta s}
\right)
$$</p>
<p>Savitzky-Golay filter는 중심 대칭 창을 사용하므로 (N)은 홀수여야 한다.</p>
<p>따라서 (N)이 짝수이면 다음과 같이 보정한다.</p>
<p>$$
N\leftarrow N+1
$$</p>
<p>코드에서는 실제 경로의 중앙 간격을 계산한 뒤 mm 단위 창을 홀수 샘플 창으로 변환한다.</p>
<p>경로 간격을 약 (0.5\text{ mm})라고 가정하면 다음과 같다.</p>
<table>
<thead>
<tr>
<th>처리 대상</th>
<th align="right">물리적 창</th>
<th align="right">예상 샘플 수</th>
</tr>
</thead>
<tbody><tr>
<td>Median 기준선</td>
<td align="right">1.5 mm</td>
<td align="right">약 3개</td>
</tr>
<tr>
<td>Y 경로 평활화</td>
<td align="right">7.0 mm</td>
<td align="right">약 15개</td>
</tr>
<tr>
<td>Z 경로 평활화</td>
<td align="right">3.5 mm</td>
<td align="right">약 7개</td>
</tr>
<tr>
<td>면 폭 평활화</td>
<td align="right">9.0 mm</td>
<td align="right">약 19개</td>
</tr>
<tr>
<td>폭 방향 평활화</td>
<td align="right">7.0 mm</td>
<td align="right">약 15개</td>
</tr>
<tr>
<td>PCD 재중앙화</td>
<td align="right">7.0 mm</td>
<td align="right">약 15개</td>
</tr>
</tbody></table>
<p>실제 샘플 수는 경로의 실제 간격과 전체 점 개수에 따라 달라질 수 있다.</p>
<hr>
<h1 id="16-robust-low-pass-filter">16. Robust Low-pass Filter</h1>
<p>Step 2-1의 핵심 평활화 과정은 다음과 같다.</p>
<pre><code class="language-text">원본 신호
   │
   ▼
1.5 mm Median 기준선
   │
   ▼
Residual 계산
   │
   ▼
MAD 기반 이상치 제한
   │
   ▼
Savitzky-Golay Low-pass</code></pre>
<hr>
<h2 id="161-median-기준선">16.1 Median 기준선</h2>
<p>입력 신호를 (v_i)라고 하면 Median 기준선은 다음과 같다.</p>
<p>$$
m_i
===</p>
<p>\operatorname{median}
\left{
v_{i-K},\ldots,v_{i+K}
\right}
$$</p>
<hr>
<h2 id="162-residual-계산">16.2 Residual 계산</h2>
<p>원본 신호와 기준선의 차이를 계산한다.</p>
<p>$$
r_i
===</p>
<p>v_i-m_i
$$</p>
<p>짧은 돌출점은 큰 Residual을 만든다.</p>
<hr>
<h2 id="163-mad-기반-robust-sigma">16.3 MAD 기반 Robust Sigma</h2>
<p>Residual의 중앙값은 다음과 같다.</p>
<p>$$
r_c
===</p>
<p>\operatorname{median}(r_i)
$$</p>
<p>Median Absolute Deviation은 다음과 같다.</p>
<p>$$
\operatorname{MAD}
==================</p>
<p>\operatorname{median}
\left(
|r_i-r_c|
\right)
$$</p>
<p>정규분포의 표준편차와 유사한 척도로 변환하기 위해 다음 계수를 사용한다.</p>
<p>$$
\hat\sigma
==========</p>
<p>1.4826
\cdot
\operatorname{MAD}
$$</p>
<p>일반적인 표준편차는 큰 이상치의 제곱에 영향을 많이 받는다.</p>
<p>반면 MAD는 Median 기반이므로 소수의 큰 PCD 이상점에 강하다.</p>
<hr>
<h2 id="164-35sigma-이상치-제한">16.4 (3.5\sigma) 이상치 제한</h2>
<p>Residual의 허용 범위는 다음과 같다.</p>
<p>$$
r_c-3.5\hat\sigma
\le
r_i
\le
r_c+3.5\hat\sigma
$$</p>
<p>범위를 벗어난 Residual은 다음과 같이 제한한다.</p>
<p>$$
r_i^{\text{clip}}
=================</p>
<p>\operatorname{clip}
\left(
r_i,
r_c-3.5\hat\sigma,
r_c+3.5\hat\sigma
\right)
$$</p>
<p>이상치가 제거된 신호는 다음과 같다.</p>
<p>$$
v_i^{\text{despike}}
====================</p>
<p>m_i+r_i^{\text{clip}}
$$</p>
<p>마지막으로 Savitzky-Golay filter를 적용한다.</p>
<p>$$
\hat v_i
========</p>
<p>\operatorname{SG}
\left(
v_i^{\text{despike}}
\right)
$$</p>
<p>코드에서는 Median 기준선, Residual, MAD 기반 Sigma, (3.5\sigma) Clipping, SG filter 순서로 구현되어 있다.</p>
<hr>
<h1 id="17-y와-z에-서로-다른-필터-적용">17. Y와 Z에 서로 다른 필터 적용</h1>
<p>경로의 Y와 Z는 서로 다른 특성을 가진다.</p>
<p>Y에는 Raster 및 경계 검출에 의한 짧은 좌우 Wave가 주로 존재한다.</p>
<p>Z에는 실제 안경 형상의 높이 변화와 급경사가 포함된다.</p>
<p>따라서 다음과 같이 서로 다른 창을 사용한다.</p>
<p>$$
\hat C_y(s)
===========</p>
<p>\operatorname{RobustLP}_{7.0}
\left(
C_y(s)
\right)
$$</p>
<p>$$
\hat C_z(s)
===========</p>
<p>\operatorname{RobustLP}_{3.5}
\left(
C_z(s)
\right)
$$</p>
<p>Step 2-1의 주요 파라미터는 다음과 같다.</p>
<table>
<thead>
<tr>
<th>파라미터</th>
<th align="right">값</th>
</tr>
</thead>
<tbody><tr>
<td>Median window</td>
<td align="right">1.5 mm</td>
</tr>
<tr>
<td>Y lateral window</td>
<td align="right">7.0 mm</td>
</tr>
<tr>
<td>Z height window</td>
<td align="right">3.5 mm</td>
</tr>
<tr>
<td>Width window</td>
<td align="right">9.0 mm</td>
</tr>
<tr>
<td>Direction window</td>
<td align="right">7.0 mm</td>
</tr>
<tr>
<td>Outlier threshold</td>
<td align="right">3.5 sigma</td>
</tr>
<tr>
<td>Polynomial order</td>
<td align="right">3</td>
</tr>
</tbody></table>
<p>이 값들은 <code>AccuracyConfig</code>에 정의되어 있다.</p>
<p>Z의 창을 Y보다 짧게 설정한 이유는 실제 급경사 형상을 최대한 유지하기 위해서이다.</p>
<hr>
<h1 id="18-원본-경로-대비-보정량-제한">18. 원본 경로 대비 보정량 제한</h1>
<p>평활화를 과도하게 적용하면 실제 안경 형상이 바뀔 수 있다.</p>
<p>따라서 원본 경로에서 최종 보정 경로까지의 이동량을 다음 범위로 제한한다.</p>
<p>$$
M=0.30\text{ mm}
$$</p>
<p>원본 경로와 보정 후보 경로의 차이를 다음과 같이 정의한다.</p>
<p>$$
\Delta\mathbf C_i
=================</p>
<h2 id="mathbf-c_itextcandidate">\mathbf C_i^{\text{candidate}}</h2>
<p>\mathbf C_i^{\text{raw}}
$$</p>
<p>Scale은 다음과 같다.</p>
<p>$$
\alpha_i
========</p>
<p>\min
\left(
1,
\frac{M}{
|\Delta\mathbf C_i|_2
}
\right)
$$</p>
<p>최종 제한 경로는 다음과 같다.</p>
<p>$$
\mathbf C_i^{\text{limited}}
============================</p>
<p>\mathbf C_i^{\text{raw}}
+
\alpha_i
\Delta\mathbf C_i
$$</p>
<p>따라서 항상 다음 조건을 만족한다.</p>
<p>$$
\left|
\mathbf C_i^{\text{limited}}</p>
<hr>
<p>\mathbf C_i^{\text{raw}}
\right|_2
\le
0.30\text{ mm}
$$</p>
<p>코드에서도 보정 방향은 유지하면서 보정 벡터의 길이만 최대 (0.30\text{ mm})로 제한한다.</p>
<hr>
<h1 id="19-실제-pcd-단면-재측정">19. 실제 PCD 단면 재측정</h1>
<p>Step 2-1에서는 Step 2가 계산한 경계만 믿지 않고, 각 경로점 주변의 얇은 면 PCD를 다시 측정한다.</p>
<p>경로점 (\mathbf C_i) 주변에서 다음 조건을 만족하는 PCD 점을 선택한다.</p>
<p>$$
|x_n-C_{x,i}|
\le
0.08\text{ mm}
$$</p>
<p>$$
|z_n-C_{z,i}|
\le
0.08\text{ mm}
$$</p>
<p>$$
|y_n-C_{y,i}|
\le
2.00\text{ mm}
$$</p>
<p>선택된 점들의 Y 좌표 집합을 다음과 같이 정의한다.</p>
<p>$$
\mathcal Y_i
============</p>
<p>\left{
y_n
;\middle|;
\begin{aligned}
&amp;|x_n-C_{x,i}|\le0.08<br>&amp;|z_n-C_{z,i}|\le0.08<br>&amp;|y_n-C_{y,i}|\le2.00
\end{aligned}
\right}
$$</p>
<p>이 단면은 경로 접선에 수직인 회전 단면이 아니라, X·Y·Z 좌표축에 정렬된 직육면체 영역이다.</p>
<p>즉, 현재 알고리즘은 다음을 가정한다.</p>
<ul>
<li>경로의 주 진행 방향은 대체로 X 방향</li>
<li>본딩 면의 폭 방향은 대체로 Y 방향</li>
</ul>
<hr>
<h2 id="191-percentile을-사용하는-이유">19.1 Percentile을 사용하는 이유</h2>
<p>선택된 Y 값의 최솟값과 최댓값을 사용하면 노이즈 점 하나가 전체 면 폭을 크게 왜곡할 수 있다.</p>
<p>따라서 다음 두 Percentile을 사용한다.</p>
<p>$$
q_{0.02,i}
==========</p>
<p>Q_{2%}(\mathcal Y_i)
$$</p>
<p>$$
q_{0.98,i}
==========</p>
<p>Q_{98%}(\mathcal Y_i)
$$</p>
<p>측정된 면 중앙은 다음과 같다.</p>
<p>$$
C_{y,i}^{\text{PCD}}
====================</p>
<p>\frac{
q_{0.02,i}
+
q_{0.98,i}
}{2}
$$</p>
<p>측정된 면 폭은 다음과 같다.</p>
<p>$$
w_i^{\text{PCD}}
================</p>
<h2 id="q_098i">q_{0.98,i}</h2>
<p>q_{0.02,i}
$$</p>
<p>코드에서는 단면에 최소 10개 이상의 점이 있어야 해당 측정을 사용한다. 또한 전체 경로점 중 최소 80%에서 유효한 단면이 확보되어야 한다.</p>
<p>유효하지 않은 일부 구간은 인접한 유효 측정값으로 보간한다.</p>
<hr>
<h1 id="20-pcd-기반-중앙-경로-재보정">20. PCD 기반 중앙 경로 재보정</h1>
<p>현재 평활화 경로와 실제 PCD 단면 중앙의 Y 차이를 계산한다.</p>
<p>$$
e_i
===</p>
<h2 id="c_yitextpcd">C_{y,i}^{\text{PCD}}</h2>
<p>\hat C_{y,i}
$$</p>
<p>이 오차에 다시 Robust low-pass를 적용한다.</p>
<p>$$
\bar e_i
========</p>
<p>\operatorname{RobustLP}_{7.0}(e_i)
$$</p>
<p>단일 재중앙화량은 다음 범위로 제한한다.</p>
<p>$$
-0.35
\le
\bar e_i
\le
0.35
$$</p>
<p>즉,</p>
<p>$$
\bar e_i^{\text{clip}}
======================</p>
<p>\operatorname{clip}
\left(
\bar e_i,
-0.35,
0.35
\right)
$$</p>
<p>경로 Y는 다음과 같이 이동한다.</p>
<p>$$
\hat C_{y,i}
\leftarrow
\hat C_{y,i}
+
\bar e_i^{\text{clip}}
$$</p>
<p>이후 원본 경로 대비 최종 3차원 이동량을 다시 (0.30\text{ mm})로 제한한다.</p>
<p>여기서 두 제한값의 역할은 다르다.</p>
<table>
<thead>
<tr>
<th>제한값</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>(\pm0.35\text{ mm})</td>
<td>PCD 중앙으로 이동시키는 Y 보정량 제한</td>
</tr>
<tr>
<td>(0.30\text{ mm})</td>
<td>원본 경로 대비 최종 3차원 보정량 제한</td>
</tr>
</tbody></table>
<p>따라서 최종 결과는 항상 원본 경로에서 3차원 거리 기준 (0.30\text{ mm}) 이내에 존재한다.</p>
<hr>
<h1 id="21-면-폭과-방향-재구성">21. 면 폭과 방향 재구성</h1>
<p>원본 Outer와 Inner의 차이를 폭 벡터로 정의한다.</p>
<p>$$
\mathbf r_i
===========</p>
<p>\mathbf O_i-\mathbf I_i
$$</p>
<p>원본 폭은 다음과 같다.</p>
<p>$$
w_i^{\text{raw}}
================</p>
<p>|\mathbf r_i|_2
$$</p>
<p>원본 폭 방향 단위벡터는 다음과 같다.</p>
<p>$$
\mathbf d_i^{\text{raw}}
========================</p>
<p>\frac{
\mathbf r_i
}{
|\mathbf r_i|_2
}
$$</p>
<hr>
<h2 id="211-폭-평활화">21.1 폭 평활화</h2>
<p>Percentile 단면에서 측정한 폭에 (9.0\text{ mm}) Robust low-pass를 적용한다.</p>
<p>$$
\hat w_i
========</p>
<p>\operatorname{RobustLP}_{9.0}
\left(
w_i^{\text{PCD}}
\right)
$$</p>
<p>폭이 0 또는 음수가 되지 않도록 최소 폭을 설정한다.</p>
<p>$$
\hat w_i
\ge
0.05\text{ mm}
$$</p>
<hr>
<h2 id="212-폭-방향-평활화">21.2 폭 방향 평활화</h2>
<p>폭 방향 벡터의 X, Y, Z 성분을 각각 평활화한다.</p>
<p>$$
\tilde{\mathbf d}_i
===================</p>
<p>\begin{bmatrix}
\operatorname{LP}(d_{x,i})<br>\operatorname{LP}(d_{y,i})<br>\operatorname{LP}(d_{z,i})
\end{bmatrix}
$$</p>
<p>성분별 필터링 이후에는 벡터의 크기가 1이 아닐 수 있으므로 다시 정규화한다.</p>
<p>$$
\hat{\mathbf d}_i
=================</p>
<p>\frac{
\tilde{\mathbf d}_i
}{
|\tilde{\mathbf d}_i|_2
}
$$</p>
<hr>
<h2 id="213-outer와-inner-재구성">21.3 Outer와 Inner 재구성</h2>
<p>최종 중앙 경로를 기준으로 폭의 절반만큼 양쪽으로 이동한다.</p>
<p>$$
\hat{\mathbf O}_i
=================</p>
<p>\hat{\mathbf C}_i
+
\frac{\hat w_i}{2}
\hat{\mathbf d}_i
$$</p>
<p>$$
\hat{\mathbf I}_i
=================</p>
<h2 id="hatmathbf-c_i">\hat{\mathbf C}_i</h2>
<p>\frac{\hat w_i}{2}
\hat{\mathbf d}_i
$$</p>
<p>이 구조에서는 항상 다음 관계가 성립한다.</p>
<p>$$
\frac{
\hat{\mathbf O}_i
+
\hat{\mathbf I}_i
}{2}
====</p>
<p>\hat{\mathbf C}_i
$$</p>
<p>그리고 재구성된 경계 사이의 거리는 다음과 같다.</p>
<p>$$
\left|
\hat{\mathbf O}_i</p>
<hr>
<p>\hat{\mathbf I}_i
\right|_2
=========</p>
<p>\hat w_i
$$</p>
<p>코드에서도 PCD 단면 폭과 평활화된 원본 폭 방향을 이용해 Outer와 Inner를 중앙 경로 기준으로 대칭 재구성한다.</p>
<hr>
<h1 id="22-최종-접선-재계산">22. 최종 접선 재계산</h1>
<p>경로의 Y와 Z가 변경되었으므로 Step 2의 접선을 그대로 사용할 수 없다.</p>
<p>먼저 보정된 경로의 누적 거리를 다시 계산한다.</p>
<p>$$
\hat s_0=0
$$</p>
<p>$$
\hat s_i
========</p>
<p>\sum_{k=1}^{i}
\left|
\hat{\mathbf C}_k</p>
<hr>
<p>\hat{\mathbf C}_{k-1}
\right|_2
$$</p>
<p>이 거리 기준으로 경로를 미분한다.</p>
<p>$$
\hat{\mathbf v}_i
=================</p>
<p>\frac{
d\hat{\mathbf C}
}{
d\hat s
}
$$</p>
<p>단위 접선은 다음과 같다.</p>
<p>$$
\hat{\mathbf t}_i
=================</p>
<p>\frac{
\hat{\mathbf v}_i
}{
|\hat{\mathbf v}_i|_2
}
$$</p>
<p>코드에서도 보정된 경로의 누적 거리와 접선을 다시 계산한 뒤 정규화한다.</p>
<p>이 접선이 Step 3의 Quaternion trajectory 계산에 전달된다.</p>
<hr>
<h1 id="23-accuracy-metric-해석">23. Accuracy Metric 해석</h1>
<p>Step 2-1은 단순히 경로를 저장하는 것뿐 아니라 보정 효과를 수치로 평가한다.</p>
<hr>
<h2 id="231-중앙-경로-보정량">23.1 중앙 경로 보정량</h2>
<p>각 점의 보정 크기는 다음과 같다.</p>
<p>$$
c_i
===</p>
<p>\left|
\hat{\mathbf C}_i</p>
<hr>
<p>\mathbf C_i^{\text{raw}}
\right|_2
$$</p>
<p>확인할 값은 다음과 같다.</p>
<ul>
<li>Mean</li>
<li>P95</li>
<li>Maximum</li>
</ul>
<p>Maximum은 설정상 (0.30\text{ mm})를 초과하지 않아야 한다.</p>
<hr>
<h2 id="232-pcd-면-중앙-오차">23.2 PCD 면 중앙 오차</h2>
<p>보정 전 중앙 오차는 다음과 같다.</p>
<p>$$
e_i^{\text{before}}
===================</p>
<h2 id="c_yitextpcd-1">C_{y,i}^{\text{PCD}}</h2>
<p>C_{y,i}^{\text{raw}}
$$</p>
<p>보정 후 중앙 오차는 다음과 같다.</p>
<p>$$
e_i^{\text{after}}
==================</p>
<h2 id="c_yitextpcd-2">C_{y,i}^{\text{PCD}}</h2>
<p>\hat C_{y,i}
$$</p>
<p>Signed mean은 전체 경로가 어느 방향으로 치우쳐 있는지를 나타낸다.</p>
<p>$$
\bar e
======</p>
<p>\frac1N
\sum_i e_i
$$</p>
<p>절댓값 P95는 전체 경로점 중 95%의 중앙 오차가 어느 범위 안에 있는지를 나타낸다.</p>
<p>$$
P_{95}
\left(
|e_i|
\right)
$$</p>
<hr>
<h2 id="233-고주파-rms">23.3 고주파 RMS</h2>
<p>경로 신호에서 긴 SG 추세선을 제거한다.</p>
<p>$$
h_i
===</p>
<p>y_i-\bar y_i
$$</p>
<p>고주파 RMS는 다음과 같다.</p>
<p>$$
\operatorname{RMS}_{HF}
=======================</p>
<p>\sqrt{
\frac1N
\sum_i h_i^2
}
$$</p>
<p>보정 후 RMS가 감소하면 짧은 Y 방향 Wave가 줄었다는 의미이다.</p>
<p>코드에서도 신호와 Savitzky-Golay 추세의 차이에 대한 RMS를 계산한다.</p>
<hr>
<h2 id="234-xy-heading-total-variation">23.4 XY Heading Total Variation</h2>
<p>각 경로 구간의 XY 진행각은 다음과 같다.</p>
<p>$$
\psi_i
======</p>
<p>\operatorname{atan2}
\left(
y_{i+1}-y_i,
x_{i+1}-x_i
\right)
$$</p>
<p>각도를 Unwrap한 뒤 총 변화량을 계산한다.</p>
<p>$$
TV_\psi
=======</p>
<p>\sum_i
\left|
\psi_{i+1}-\psi_i
\right|
$$</p>
<p>이 값이 감소하면 로봇 진행 방향의 좌우 흔들림이 줄었다는 의미이다.</p>
<hr>
<h2 id="235-본딩-면-폭-통계">23.5 본딩 면 폭 통계</h2>
<p>면 폭 평균은 다음과 같다.</p>
<p>$$
\bar w
======</p>
<p>\frac1N
\sum_iw_i
$$</p>
<p>면 폭 표준편차는 다음과 같다.</p>
<p>$$
\sigma_w
========</p>
<p>\sqrt{
\frac1N
\sum_i
(w_i-\bar w)^2
}
$$</p>
<p>보정 후 표준편차가 줄어들면 순간적인 폭 변화가 제거되었다는 의미이다.</p>
<p>다만 실제 제품의 폭이 위치에 따라 변하는 구조라면 표준편차가 지나치게 작아지는 것도 과평활화일 수 있다.</p>
<p>코드에서는 보정량, PCD 중앙 오차, 고주파 RMS, Heading 변화량 및 폭 통계를 JSON으로 저장한다.</p>
<hr>
<h1 id="24-간단한-계산-예제">24. 간단한 계산 예제</h1>
<p>어떤 X 위치에서 Outer와 Inner가 다음과 같이 검출되었다고 가정한다.</p>
<p>$$
\mathbf O_i
===========</p>
<p>\begin{bmatrix}
10.0<br>20.8<br>-3.25
\end{bmatrix}
\text{ mm}
$$</p>
<p>$$
\mathbf I_i
===========</p>
<p>\begin{bmatrix}
10.0<br>19.4<br>-3.25
\end{bmatrix}
\text{ mm}
$$</p>
<p>중앙점은 다음과 같다.</p>
<p>$$
\mathbf C_i
===========</p>
<p>\frac{
\mathbf O_i+\mathbf I_i
}{2}
====</p>
<p>\begin{bmatrix}
10.0<br>20.1<br>-3.25
\end{bmatrix}
\text{ mm}
$$</p>
<p>폭은 다음과 같다.</p>
<p>$$
w_i
===</p>
<h1 id="mathbf-o_i-mathbf-i_i_2">|\mathbf O_i-\mathbf I_i|_2</h1>
<p>1.4\text{ mm}
$$</p>
<p>다음 경로점이 다음과 같다고 가정한다.</p>
<p>$$
\mathbf C_{i+1}
===============</p>
<p>\begin{bmatrix}
10.5<br>20.12<br>-2.95
\end{bmatrix}
$$</p>
<p>변화량은 다음과 같다.</p>
<p>$$
\Delta x=0.5,\qquad
\Delta y=0.02,\qquad
\Delta z=0.30
$$</p>
<p>XY 이동량은 다음과 같다.</p>
<p>$$
\Delta r_{xy}
=============</p>
<p>\sqrt{
0.5^2+0.02^2
}
\approx
0.5004\text{ mm}
$$</p>
<p>경사각은 다음과 같다.</p>
<p>$$
\theta
======</p>
<p>\operatorname{atan2}
\left(
0.30,
0.5004
\right)
\approx
30.94^\circ
$$</p>
<p>따라서 (55^\circ)보다 작으므로 양 끝 직각 전환부로 판정되지 않는다.</p>
<hr>
<h2 id="241-percentile-단면-예제">24.1 Percentile 단면 예제</h2>
<p>PCD 단면 측정 결과가 다음과 같다고 가정한다.</p>
<p>$$
Y_{2%}
======</p>
<p>19.45\text{ mm}
$$</p>
<p>$$
Y_{98%}
=======</p>
<p>20.75\text{ mm}
$$</p>
<p>측정 중앙은 다음과 같다.</p>
<p>$$
C_y^{\text{PCD}}
================</p>
<p>\frac{
19.45+20.75
}{2}
====</p>
<p>20.10\text{ mm}
$$</p>
<p>측정 폭은 다음과 같다.</p>
<p>$$
w^{\text{PCD}}
==============</p>
<h1 id="2075-1945">20.75-19.45</h1>
<p>1.30\text{ mm}
$$</p>
<p>현재 평활화된 경로 Y가 (20.18\text{ mm})라면 중앙 오차는 다음과 같다.</p>
<p>$$
e
=</p>
<h1 id="2010-2018">20.10-20.18</h1>
<p>-0.08\text{ mm}
$$</p>
<p>따라서 경로는 Y 음의 방향으로 약 (0.08\text{ mm}) 이동하게 된다.</p>
<hr>
<h1 id="25-결과-그림-해석">25. 결과 그림 해석</h1>
<h2 id="251-xy-top-view">25.1 XY Top View</h2>
<p>위에서 본 그림에서는 다음 요소를 확인한다.</p>
<ul>
<li>높이에 따라 색상이 지정된 얇은 본딩 면 PCD</li>
<li>보정된 Outer edge</li>
<li>보정된 Inner edge</li>
<li>최종 중앙 경로</li>
<li>Robust 경계 밖에 존재하는 원본 PCD 점</li>
</ul>
<p>2%와 98% Percentile을 사용하므로 소수의 원본 점이 보정 경계 밖에 남는 것은 정상이다.</p>
<p>최종 시각화 코드에서도 Robust 경계 내부의 점은 높이 색상으로 표시하고, 경계 밖의 점은 회색으로 분리한다.</p>
<hr>
<h2 id="252-xz-side-view">25.2 XZ Side View</h2>
<p>XZ 그림에서는 경로 높이와 구간별 경사각을 확인한다.</p>
<p>각 구간의 경사각은 다음과 같다.</p>
<p>$$
\theta_i
========</p>
<p>\operatorname{atan2}
\left(
|\Delta Z_i|,
\sqrt{
(\Delta X_i)^2+
(\Delta Y_i)^2
}
\right)
$$</p>
<p>경로 선분의 색상이 경사각을 나타내므로 다음을 확인할 수 있다.</p>
<ul>
<li>급경사 구간의 위치</li>
<li>좌우 끝단 전환부</li>
<li>평활화 이후 실제 높이 형상이 유지되었는지</li>
<li>경로 중간에 비정상적인 경사 급변이 존재하는지</li>
</ul>
<p>최종 시각화에서도 XZ 경로의 선분 색상을 XY 평면 기준 경사각으로 표시한다.</p>
<hr>
<h1 id="26-주요-파라미터-튜닝-가이드">26. 주요 파라미터 튜닝 가이드</h1>
<table>
<thead>
<tr>
<th>파라미터</th>
<th>역할</th>
<th>너무 작을 때</th>
<th>너무 클 때</th>
</tr>
</thead>
<tbody><tr>
<td>Raster 0.02 mm</td>
<td>XY 높이 맵 해상도</td>
<td>계산량과 메모리 증가</td>
<td>경계 계단 및 위치 오차 증가</td>
</tr>
<tr>
<td>Inner search 0.35~2.80 mm</td>
<td>Inner edge 탐색 범위</td>
<td>실제 경계 누락</td>
<td>다른 홈을 경계로 오검출</td>
</tr>
<tr>
<td>Gaussian sigma 0.03 mm</td>
<td>미분 전 노이즈 완화</td>
<td>미분 노이즈 증가</td>
<td>경계가 지나치게 퍼짐</td>
</tr>
<tr>
<td>Edge strength 0.15</td>
<td>최소 경계 기울기</td>
<td>약한 노이즈도 경계로 검출</td>
<td>완만한 실제 경계 누락</td>
</tr>
<tr>
<td>Edge SG 3.0 mm</td>
<td>XY 경계 평활화</td>
<td>Raster wave 잔존</td>
<td>실제 XY 곡률 왜곡</td>
</tr>
<tr>
<td>Height SG 1.2 mm</td>
<td>Step 2 높이 평활화</td>
<td>Z 진동 잔존</td>
<td>급경사 형상 완화</td>
</tr>
<tr>
<td>Top-face tolerance 0.15 mm</td>
<td>얇은 면 높이 범위</td>
<td>면 PCD가 부족해짐</td>
<td>홈과 옆면 점 포함</td>
</tr>
<tr>
<td>Output spacing 0.5 mm</td>
<td>Waypoint 간격</td>
<td>IK 계산량 증가</td>
<td>곡선 재현 성능 저하</td>
</tr>
<tr>
<td>Y window 7.0 mm</td>
<td>Step 2-1 좌우 Wave 제거</td>
<td>짧은 Wave 잔존</td>
<td>실제 Y 곡률 평탄화</td>
</tr>
<tr>
<td>Z window 3.5 mm</td>
<td>Step 2-1 높이 진동 제거</td>
<td>Z 노이즈 잔존</td>
<td>실제 급경사 손실</td>
</tr>
<tr>
<td>Width window 9.0 mm</td>
<td>폭 변화 안정화</td>
<td>폭 급변 잔존</td>
<td>실제 폭 변화 손실</td>
</tr>
<tr>
<td>Outlier 3.5 sigma</td>
<td>이상치 제한</td>
<td>정상 변화까지 제한</td>
<td>큰 돌출점 잔존</td>
</tr>
<tr>
<td>Recenter ±0.35 mm</td>
<td>단면 중앙 추종 한계</td>
<td>큰 초기 오차 미보정</td>
<td>잘못 측정한 단면을 과도하게 추종</td>
</tr>
<tr>
<td>Final correction 0.30 mm</td>
<td>원본 형상 보존</td>
<td>충분한 재보정이 어려움</td>
<td>원본 경로에서 크게 이탈 가능</td>
</tr>
</tbody></table>
<hr>
<h1 id="27-알고리즘의-주요-가정과-한계">27. 알고리즘의 주요 가정과 한계</h1>
<h2 id="271-하나의-xy-위치에-하나의-표면만-존재">27.1 하나의 XY 위치에 하나의 표면만 존재</h2>
<p>Top-envelope는 하나의 XY 위치에 하나의 Z만 저장한다.</p>
<p>$$
z=f(x,y)
$$</p>
<p>따라서 Overhang처럼 하나의 XY 위치에 여러 층의 표면이 존재하는 구조는 정확하게 표현하기 어렵다.</p>
<hr>
<h2 id="272-최소-local-z가-실제-윗면이라는-가정">27.2 최소 Local Z가 실제 윗면이라는 가정</h2>
<p>현재 Isaac 좌표 변환에서는 최소 Local Z를 윗면으로 사용한다.</p>
<p>좌표 변환이 바뀌어 Local Z 방향이 물리 높이 방향과 같아지면 다음과 같이 변경해야 할 수 있다.</p>
<p>$$
Z_{\text{top}}
==============</p>
<p>\max z
$$</p>
<hr>
<h2 id="273-outer-edge가-y-방향이라는-가정">27.3 Outer edge가 (+Y) 방향이라는 가정</h2>
<p>현재 Outer edge는 각 X 열의 최대 Y이다.</p>
<p>$$
O_y(x)
======</p>
<p>\max Y
$$</p>
<p>제품이 회전하거나 반대로 놓이면 올바른 경계를 검출하지 못할 수 있다.</p>
<hr>
<h2 id="274-inner-edge-미분-부호-의존성">27.4 Inner edge 미분 부호 의존성</h2>
<p>현재는 다음 위치를 Inner edge로 선택한다.</p>
<p>$$
\arg\min
\frac{dZ}{dY}
$$</p>
<p>좌표계가 반전되면 다음 방식이 필요할 수 있다.</p>
<p>$$
\arg\max
\frac{dZ}{dY}
$$</p>
<p>또는</p>
<p>$$
\arg\max
\left|
\frac{dZ}{dY}
\right|
$$</p>
<hr>
<h2 id="275-축-정렬-단면-사용">27.5 축 정렬 단면 사용</h2>
<p>Step 2-1의 PCD 단면은 경로 접선에 수직인 단면이 아니다.</p>
<p>현재는 다음과 같은 고정 좌표축 기준 단면이다.</p>
<pre><code class="language-text">X = ±0.08 mm
Z = ±0.08 mm
Y = ±2.00 mm</code></pre>
<p>경로 방향이 X축에서 크게 회전하는 형상이라면 접선 기반 회전 단면을 사용하는 것이 더 일반적이다.</p>
<p>폭 방향 (\mathbf d_i)를 접선 (\mathbf t_i)에 직교화하면 다음과 같다.</p>
<p>$$
\mathbf b_i
===========</p>
<p>\frac{
\mathbf d_i</p>
<hr>
<p>(\mathbf d_i\cdot\mathbf t_i)\mathbf t_i
}{
\left|
\mathbf d_i</p>
<hr>
<p>(\mathbf d_i\cdot\mathbf t_i)\mathbf t_i
\right|_2
}
$$</p>
<p>이 (\mathbf b_i) 방향으로 PCD를 투영하면 경로 방향이 바뀌더라도 일관된 폭을 측정할 수 있다.</p>
<p>현재 코드에는 이 방식이 직접 적용되어 있지 않다.</p>
<hr>
<h2 id="276-step-2-1에서-z를-percentile로-재측정하지-않음">27.6 Step 2-1에서 Z를 Percentile로 재측정하지 않음</h2>
<p>Step 2-1의 단면 재측정으로 얻는 값은 다음 두 가지이다.</p>
<ul>
<li>Y 방향 실제 면 중앙</li>
<li>Y 방향 실제 면 폭</li>
</ul>
<p>Z는 PCD Percentile로 다시 계산하지 않고, Step 2의 Z 경로를 Robust low-pass로 평활화한다.</p>
<p>따라서 Step 2에서 Z가 구조적으로 잘못 검출된 경우 Step 2-1만으로 완전히 수정하기는 어렵다.</p>
<hr>
<h1 id="28-step-3과의-연결">28. Step 3과의 연결</h1>
<p>Step 2-1은 최종 위치와 접선을 제공한다.</p>
<p>$$
\hat{\mathbf C}_i
$$</p>
<p>$$
\hat{\mathbf t}_i
$$</p>
<p>하지만 접선 하나만으로는 3차원 회전 자세를 완전히 결정할 수 없다.</p>
<p>접선에 수직인 회전 방향이 무한히 존재하기 때문이다.</p>
<p>따라서 Step 3에서는 폭 방향 또는 표면 Normal과 같은 추가 방향이 필요하다.</p>
<p>폭 방향을 접선에 직교화하면 다음과 같다.</p>
<p>$$
\mathbf b_i
===========</p>
<p>\frac{
\hat{\mathbf d}_i</p>
<hr>
<p>(\hat{\mathbf d}_i\cdot\hat{\mathbf t}_i)
\hat{\mathbf t}_i
}{
\left|
\hat{\mathbf d}_i</p>
<hr>
<p>(\hat{\mathbf d}_i\cdot\hat{\mathbf t}_i)
\hat{\mathbf t}_i
\right|_2
}
$$</p>
<p>표면 Normal은 외적으로 계산할 수 있다.</p>
<p>$$
\mathbf n_i
===========</p>
<p>\hat{\mathbf t}_i
\times
\mathbf b_i
$$</p>
<p>이 세 축으로 회전행렬을 만들 수 있다.</p>
<p>$$
R_i
===</p>
<p>\begin{bmatrix}
\hat{\mathbf t}_i&amp;
\mathbf b_i&amp;
\mathbf n_i
\end{bmatrix}
$$</p>
<p>이후 회전행렬을 Quaternion으로 변환하고, 로봇의 연속 IK를 수행한다.</p>
<p>Step 2에서는 Local Z Offset을 기본적으로 0으로 유지하며, 실제 위쪽 안전 높이는 Step 3의 <code>surface_offset_mm</code>에서 World (+Z) 방향으로 적용한다.</p>
<p>최종 로봇 위치는 개념적으로 다음과 같다.</p>
<p>$$
\mathbf P_i^{\text{robot}}
==========================</p>
<p>T_{\text{world}\leftarrow\text{local}}
\left(
\hat{\mathbf C}<em>i
\right)
+
h</em>{\text{safe}}
\begin{bmatrix}
0<br>0<br>1
\end{bmatrix}_{\text{world}}
$$</p>
<p>여기서 (h_{\text{safe}})가 Step 3의 <code>surface_offset_mm</code>이다.</p>
<hr>
<h1 id="29-실행-방법">29. 실행 방법</h1>
<h2 id="전체-파이프라인-실행">전체 파이프라인 실행</h2>
<pre><code class="language-bash">cd /home/happy/modelsoultion

./lee_ws/step2_pcd_path/run_step2_pipeline.sh</code></pre>
<h2 id="저장된-결과를-이용해-png만-다시-생성">저장된 결과를 이용해 PNG만 다시 생성</h2>
<pre><code class="language-bash">python3 lee_ws/step2_pcd_path/make_step2_result_figures.py</code></pre>
<h2 id="단계별-개별-실행">단계별 개별 실행</h2>
<pre><code class="language-bash">python3 lee_ws/step2_pcd_path/generate_global_path.py

python3 lee_ws/step2_pcd_path/smooth_global_path.py

python3 lee_ws/step2_pcd_path/make_step2_result_figures.py</code></pre>
<hr>
<h1 id="30-출력-파일-정리">30. 출력 파일 정리</h1>
<h2 id="step-2-출력">Step 2 출력</h2>
<pre><code class="language-text">step2_global_path.csv
step2_global_path.pcd
step2_global_path.npz
step2_thin_bonding_face.pcd
step2_global_path_metadata.json</code></pre>
<p>Step 2 코드에는 기본 경로 CSV, PCD, NPZ, 얇은 면 PCD 및 Metadata 출력 파일이 정의되어 있다.</p>
<h2 id="step-2-1-출력">Step 2-1 출력</h2>
<pre><code class="language-text">step2_1_accurate_global_path.csv
step2_1_accurate_global_path.pcd
step2_1_accurate_global_path.npz
step2_1_accuracy_metrics.json</code></pre>
<p>Step 2-1 코드에서도 보정 경로와 Accuracy metric 출력 파일이 별도로 정의되어 있다.</p>
<hr>
<h1 id="31-공부해야-할-핵심-이론">31. 공부해야 할 핵심 이론</h1>
<p>이번 알고리즘은 단순한 경로 생성 코드가 아니라 여러 분야의 개념이 함께 사용된 구조이다.</p>
<h2 id="point-cloud-processing">Point Cloud Processing</h2>
<pre><code class="language-text">Point Cloud
    ↓
ROI
    ↓
Rasterization
    ↓
Occupancy Map
    ↓
Top-envelope</code></pre>
<p>관련 개념은 다음과 같다.</p>
<ul>
<li>PCD 데이터 구조</li>
<li>ROI</li>
<li>Rasterization</li>
<li>Height Map</li>
<li>Occupancy Map</li>
<li>KD-tree 최근접 탐색</li>
</ul>
<hr>
<h2 id="digital-signal-processing">Digital Signal Processing</h2>
<p>다음 데이터들은 1차원 신호로 볼 수 있다.</p>
<p>$$
z(y),\qquad
y(s),\qquad
z(s),\qquad
w(s)
$$</p>
<p>관련 개념은 다음과 같다.</p>
<ul>
<li>Gaussian Low-pass Filter</li>
<li>Median Filter</li>
<li>Savitzky-Golay Filter</li>
<li>수치 미분</li>
<li>고주파와 저주파</li>
<li>필터 창 길이</li>
<li>위상 지연</li>
</ul>
<hr>
<h2 id="robust-statistics">Robust Statistics</h2>
<p>이번 알고리즘에서는 평균과 표준편차만 사용하는 대신 다음 개념을 사용한다.</p>
<p>$$
\operatorname{Median}
$$</p>
<p>$$
\operatorname{MAD}
$$</p>
<p>$$
Q_{2%},\quad Q_{98%}
$$</p>
<p>$$
P_{95}
$$</p>
<p>이러한 통계량은 소수의 큰 PCD 이상점에 강하다.</p>
<hr>
<h2 id="differential-geometry">Differential Geometry</h2>
<p>경로를 3차원 곡선으로 바라보면 다음 개념이 중요하다.</p>
<p>$$
\mathbf C(s)
$$</p>
<p>$$
\mathbf t(s)
============</p>
<p>\frac{
d\mathbf C/ds
}{
|d\mathbf C/ds|_2
}
$$</p>
<p>$$
\theta
======</p>
<p>\operatorname{atan2}
\left(
|dZ|,
\sqrt{dX^2+dY^2}
\right)
$$</p>
<p>Step 3에서는 접선, 폭 방향, Normal을 사용해 로봇의 3차원 자세를 계산한다.</p>
<hr>
<h1 id="32-마무리">32. 마무리</h1>
<p>이번 Step 2와 Step 2-1 파이프라인의 핵심은 다음과 같이 정리할 수 있다.</p>
<h2 id="step-2">Step 2</h2>
<p>$$
\boxed{
\text{원본 PCD에서 본딩 면의 기본 중앙 경로를 검출}
}
$$</p>
<p>핵심 수식은 다음과 같다.</p>
<p>$$
Z_{\text{top}}(x,y)
===================</p>
<p>\min z
$$</p>
<p>$$
O_y(x)
======</p>
<p>\max
\left{
y
\mid
occupied(x,y)
\right}
$$</p>
<p>$$
I_y(x)
======</p>
<p>\arg\min_y
\frac{
d\tilde z(x,y)
}{
dy
}
$$</p>
<p>$$
C_y(x)
======</p>
<p>\frac{
O_y(x)+I_y(x)
}{2}
$$</p>
<p>$$
C_z(x)
======</p>
<p>Z_{\text{top}}
\left(
x,C_y(x)
\right)
$$</p>
<hr>
<h2 id="step-2-1">Step 2-1</h2>
<p>$$
\boxed{
\text{경로의 국부 진동을 제거하고 실제 PCD 면 중앙으로 제한 보정}
}
$$</p>
<p>핵심 수식은 다음과 같다.</p>
<p>$$
\hat\sigma
==========</p>
<p>1.4826
\operatorname{median}
\left(
|r_i-\operatorname{median}(r)|
\right)
$$</p>
<p>$$
C_y^{\text{PCD}}
================</p>
<p>\frac{
Q_{2%}(Y)+Q_{98%}(Y)
}{2}
$$</p>
<p>$$
w^{\text{PCD}}
==============</p>
<p>Q_{98%}(Y)-Q_{2%}(Y)
$$</p>
<p>$$
\left|
\hat{\mathbf C}_i</p>
<hr>
<p>\mathbf C_i^{\text{raw}}
\right|_2
\le
0.30\text{ mm}
$$</p>
<p>$$
\hat{\mathbf O}
===============</p>
<p>\hat{\mathbf C}
+
\frac{\hat w}{2}
\hat{\mathbf d}
$$</p>
<p>$$
\hat{\mathbf I}
===============</p>
<h2 id="hatmathbf-c">\hat{\mathbf C}</h2>
<p>\frac{\hat w}{2}
\hat{\mathbf d}
$$</p>
<p>결론적으로 이 알고리즘은 다음 기술이 결합된 경로 생성 파이프라인이다.</p>
<pre><code class="language-text">Point Cloud Processing
        +
Edge Detection
        +
Digital Signal Processing
        +
Robust Statistics
        +
3D Curve Geometry
        +
Robot Path Planning</code></pre>
<p>Step 2는 PCD에서 기하학적인 초기 경로를 생성하고, Step 2-1은 실제 PCD 단면을 이용해 해당 경로를 강건하게 보정한다.</p>
<p>이후 Step 3에서는 최종 중앙 경로와 접선을 이용해 Quaternion trajectory와 연속 IK를 계산하고, 실제 로봇이 본딩 면을 따라 이동할 수 있는 자세 궤적을 생성한다.</p>
<hr>
<p><code>#PointCloud</code> <code>#PCD</code> <code>#RobotPathPlanning</code> <code>#Robotics</code> <code>#SavitzkyGolay</code> <code>#RobustStatistics</code> <code>#Quaternion</code> <code>#InverseKinematics</code></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[GLIM]]></title>
            <link>https://velog.io/@happy_lee0_0/GLIM</link>
            <guid>https://velog.io/@happy_lee0_0/GLIM</guid>
            <pubDate>Fri, 07 Aug 2026 13:51:16 GMT</pubDate>
            <description><![CDATA[<ol>
<li><p><a href="https://github.com/koide3/glim">https://github.com/koide3/glim</a></p>
</li>
<li><p>happy@happy-B650M-PG-Lightning:~/glim_ws$ ros2 run glim_ros glim_rosbag ~/ai_study_math/0807_first</p>
</li>
<li><p>다되면 끄기</p>
</li>
<li><p>happy@happy-B650M-PG-Lightning:/tmp$ cd dump/
happy@happy-B650M-PG-Lightning:/tmp/dump$ ls
000000  000012  000024  000036  000048  000060  000072  000084  000096  000108  000120  000132  000144  000156  000168  000180  000192  000204  000216  000228  000240  000252        odom_lidar.txt
000001  000013  000025  000037  000049  000061  000073  000085  000097  000109  000121  000133  000145  000157  000169  000181  000193  000205  000217  000229  000241  000253        traj_imu.txt
000002  000014  000026  000038  000050  000062  000074  000086  000098  000110  000122  000134  000146  000158  000170  000182  000194  000206  000218  000230  000242  000254        traj_lidar.txt
000003  000015  000027  000039  000051  000063  000075  000087  000099  000111  000123  000135  000147  000159  000171  000183  000195  000207  000219  000231  000243  000255        values.bin
000004  000016  000028  000040  000052  000064  000076  000088  000100  000112  000124  000136  000148  000160  000172  000184  000196  000208  000220  000232  000244  000256
000005  000017  000029  000041  000053  000065  000077  000089  000101  000113  000125  000137  000149  000161  000173  000185  000197  000209  000221  000233  000245  000257
000006  000018  000030  000042  000054  000066  000078  000090  000102  000114  000126  000138  000150  000162  000174  000186  000198  000210  000222  000234  000246  000258
000007  000019  000031  000043  000055  000067  000079  000091  000103  000115  000127  000139  000151  000163  000175  000187  000199  000211  000223  000235  000247  000259
000008  000020  000032  000044  000056  000068  000080  000092  000104  000116  000128  000140  000152  000164  000176  000188  000200  000212  000224  000236  000248  config
000009  000021  000033  000045  000057  000069  000081  000093  000105  000117  000129  000141  000153  000165  000177  000189  000201  000213  000225  000237  000249  graph.bin
000010  000022  000034  000046  000058  000070  000082  000094  000106  000118  000130  000142  000154  000166  000178  000190  000202  000214  000226  000238  000250  graph.txt
000011  000023  000035  000047  000059  000071  000083  000095  000107  000119  000131  000143  000155  000167  000179  000191  000203  000215  000227  000239  000251  odom_imu.txt</p>
</li>
</ol>
<p>이렇게 생김</p>
<ol start="5">
<li><p>happy@happy-B650M-PG-Lightning:~$ mv /tmp/dump ~/ai_study_math/glim_first</p>
</li>
<li><p>happy@happy-B650M-PG-Lightning:<del>/ai_study_math/glim_first$ ls
000000  000012  000024  000036  000048  000060  000072  000084  000096  000108  000120  000132  000144  000156  000168  000180  000192  000204  000216  000228  000240  000252        odom_lidar.txt
000001  000013  000025  000037  000049  000061  000073  000085  000097  000109  000121  000133  000145  000157  000169  000181  000193  000205  000217  000229  000241  000253        traj_imu.txt
000002  000014  000026  000038  000050  000062  000074  000086  000098  000110  000122  000134  000146  000158  000170  000182  000194  000206  000218  000230  000242  000254        traj_lidar.txt
000003  000015  000027  000039  000051  000063  000075  000087  000099  000111  000123  000135  000147  000159  000171  000183  000195  000207  000219  000231  000243  000255        values.bin
000004  000016  000028  000040  000052  000064  000076  000088  000100  000112  000124  000136  000148  000160  000172  000184  000196  000208  000220  000232  000244  000256
000005  000017  000029  000041  000053  000065  000077  000089  000101  000113  000125  000137  000149  000161  000173  000185  000197  000209  000221  000233  000245  000257
000006  000018  000030  000042  000054  000066  000078  000090  000102  000114  000126  000138  000150  000162  000174  000186  000198  000210  000222  000234  000246  000258
000007  000019  000031  000043  000055  000067  000079  000091  000103  000115  000127  000139  000151  000163  000175  000187  000199  000211  000223  000235  000247  000259
000008  000020  000032  000044  000056  000068  000080  000092  000104  000116  000128  000140  000152  000164  000176  000188  000200  000212  000224  000236  000248  config
000009  000021  000033  000045  000057  000069  000081  000093  000105  000117  000129  000141  000153  000165  000177  000189  000201  000213  000225  000237  000249  graph.bin
000010  000022  000034  000046  000058  000070  000082  000094  000106  000118  000130  000142  000154  000166  000178  000190  000202  000214  000226  000238  000250  graph.txt
000011  000023  000035  000047  000059  000071  000083  000095  000107  000119  000131  000143  000155  000167  000179  000191  000203  000215  000227  000239  000251  odom_imu.txt
happy@happy-B650M-PG-Lightning:</del>/ai_study_math/glim_first$ </p>
</li>
</ol>
<hr>
<p>하나 끝</p>
<hr>
<p>happy@happy-B650M-PG-Lightning:~/glim_ws$ ros2 run glim_ros offline_viewer </p>
<p>이거 viewer 라이더 값을 저장해서 합치는 것임</p>
<p>그거 좌측 눌려서 starte -&gt; end 해서 합칠수 있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[USB port 고정]]></title>
            <link>https://velog.io/@happy_lee0_0/USB-port-%EA%B3%A0%EC%A0%95</link>
            <guid>https://velog.io/@happy_lee0_0/USB-port-%EA%B3%A0%EC%A0%95</guid>
            <pubDate>Sat, 01 Aug 2026 05:05:29 GMT</pubDate>
            <description><![CDATA[<h2 id="1-ls-devttyusb">1. ls /dev/ttyUSB*</h2>
<h2 id="2-lsusb">2. lsusb</h2>
<ul>
<li><p>10c4:ea60(CP2102)</p>
</li>
<li><p>1a86:7523(1a86:7523)</p>
</li>
</ul>
<p>ex)
unicon@unicon-desktop:~/nav_ws$ lsusb
Bus 002 Device 002: ID 0bda:0489 Realtek Semiconductor Corp. 4-Port USB 3.0 Hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 003: ID 13d3:3549 IMC Networks Bluetooth Radio
Bus 001 Device 005: ID 10c4:ea60 Silicon Labs CP210x UART Bridge
Bus 001 Device 002: ID 0bda:5489 Realtek Semiconductor Corp. 4-Port USB 2.0 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub</p>
<h2 id="3-udevadm-info--a-devttyusb0--grep-serial">3. udevadm info -a /dev/ttyUSB0 | grep &quot;{serial}&quot;</h2>
<p>unicon@unicon-desktop:~/nav_ws$ udevadm info -a /dev/ttyUSB0 | grep &quot;{serial}&quot;
    ATTRS{serial}==&quot;0001&quot;
    ATTRS{serial}==&quot;3610000.usb&quot;</p>
<h2 id="4-cd-etcudevrulesd">4. cd /etc/udev/rules.d</h2>
<h2 id="5-sudo-gedit-99_usbrules">5. sudo gedit 99_usb.rules</h2>
<pre><code>SUBSYSTEM==&quot;tty&quot;, \
ATTRS{idVendor}==&quot;10c4&quot;, \
ATTRS{idProduct}==&quot;ea60&quot;, \
ATTRS{serial}==&quot;0001&quot;, \
SYMLINK+=&quot;imu&quot;</code></pre><h2 id="6-sudo-service-udev-restart">6. sudo service udev restart</h2>
<h2 id="7-port를-빼고-꼽음">7. port를 빼고 꼽음</h2>
]]></description>
        </item>
        <item>
            <title><![CDATA[건설업 관련 꿀직업]]></title>
            <link>https://velog.io/@happy_lee0_0/%EA%B1%B4%EC%84%A4%EC%97%85-%EA%B4%80%EB%A0%A8-%EA%BF%80%EC%A7%81%EC%97%85</link>
            <guid>https://velog.io/@happy_lee0_0/%EA%B1%B4%EC%84%A4%EC%97%85-%EA%B4%80%EB%A0%A8-%EA%BF%80%EC%A7%81%EC%97%85</guid>
            <pubDate>Fri, 20 Mar 2026 00:39:55 GMT</pubDate>
            <description><![CDATA[<ol>
<li>불보고 불이 날 것 같은지 확인하는 작업</li>
</ol>
<p>→ 화재감시자 (Fire Watcher)</p>
<p>용접·용단 등 화기 작업 시 화재 발생 여부를 감시</p>
<p>잔불 및 불꽃 비산 여부 확인</p>
<ol start="2">
<li>야광봉을 들고 흔들면서 길 안내하는 사람</li>
</ol>
<p>→ 신호수 (Flagman / Traffic Controller)</p>
<p>차량 및 장비 이동 시 신호를 통해 안전 유도</p>
<p>야간에는 야광봉(라이트 배턴) 사용</p>
<ol start="3">
<li>사람을 보고 따라오라고 안내하는 사람</li>
</ol>
<p>→ 유도원 (Guide / Banksman)</p>
<p>작업자 또는 장비를 특정 위치까지 안전하게 유도</p>
<p>협소 공간, 후진 작업 등에서 필수 역할</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[4 - 1,2. 이 세상은 객체로 이루어져 있다 / 클래스의 세계로 오신 것을 환영합니다. (함수의 오버로딩, 생성자)  ]]></title>
            <link>https://velog.io/@happy_lee0_0/4-12.-%EC%9D%B4-%EC%84%B8%EC%83%81%EC%9D%80-%EA%B0%9D%EC%B2%B4%EB%A1%9C-%EC%9D%B4%EB%A3%A8%EC%96%B4%EC%A0%B8-%EC%9E%88%EB%8B%A4-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%9D%98-%EC%84%B8%EA%B3%84%EB%A1%9C-%EC%98%A4%EC%8B%A0-%EA%B2%83%EC%9D%84-%ED%99%98%EC%98%81%ED%95%A9%EB%8B%88%EB%8B%A4.-%ED%95%A8%EC%88%98%EC%9D%98-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%94%A9-%EC%83%9D%EC%84%B1%EC%9E%90</link>
            <guid>https://velog.io/@happy_lee0_0/4-12.-%EC%9D%B4-%EC%84%B8%EC%83%81%EC%9D%80-%EA%B0%9D%EC%B2%B4%EB%A1%9C-%EC%9D%B4%EB%A3%A8%EC%96%B4%EC%A0%B8-%EC%9E%88%EB%8B%A4-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%9D%98-%EC%84%B8%EA%B3%84%EB%A1%9C-%EC%98%A4%EC%8B%A0-%EA%B2%83%EC%9D%84-%ED%99%98%EC%98%81%ED%95%A9%EB%8B%88%EB%8B%A4.-%ED%95%A8%EC%88%98%EC%9D%98-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%94%A9-%EC%83%9D%EC%84%B1%EC%9E%90</guid>
            <pubDate>Wed, 18 Mar 2026 23:14:11 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>해당 내용은 씹어먹는 C++ 책을 기반으로 하단 블로그의 내용을 공부하기위해 적성한 글임을 알립니다.</p>
</blockquote>
<h1 id="4---1-이-세상은-객체로-이루어져-있다">4 - 1. 이 세상은 객체로 이루어져 있다</h1>
<h2 id="객체란">객체란</h2>
<pre><code>typedef struct Animal {
  char name[30];  // 이름
  int age;        // 나이

  int health;  // 체력
  int food;    // 배부른 정도
  int clean;   // 깨끗한 정도
} Animal;</code></pre><ul>
<li><p>전에 작성한  Animal 구조체를 정의한 후, animal 변수를 만들어서 이를 필요로 하는 함수를 작성함</p>
</li>
<li><p>이 때 객체가 현실 세계에서의 존재하는 것들을 나타내기 위해서는 추상화(abstraction)라는 과정이 필요</p>
</li>
<li><p>animal.food += 100; 자체는 외부에서 animal 이라는 &#39;객체&#39; 의 &#39;인스턴스 변수&#39; 에 &#39;직접&#39; 접근하는 것이기 때문에 불가능</p>
</li>
<li><p>animal.increase_food(100); 의 경우 animal 객체의 &#39;인스턴스 함수&#39; 를 통해서 값을 수정하는 것이기 때문에가능한 것이지요. 이와 같이 외부에서 직접 인스턴스 변수의 값을 바꿀 수 없고 항상 인스턴스 메소드를 통해서 간접적으로 조절하는 것을 캡슐화(Encapsulation)라고 함</p>
</li>
</ul>
<hr>
<h2 id="클래스란">클래스란</h2>
<pre><code>class Animal {
 private:
  int food;
  int weight;

 public:
  void set_animal(int _food, int _weight) {
    food = _food;
    weight = _weight;
  }
  void increase_food(int inc) {
    food += inc;
    weight += (inc / 3);
  }
  void view_stat() {
    std::cout &lt;&lt; &quot;이 동물의 food   : &quot; &lt;&lt; food &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;이 동물의 weight : &quot; &lt;&lt; weight &lt;&lt; std::endl;
  }
};</code></pre><ul>
<li>Animal 클래스 상에서 이들을 지칭할 때 각각 멤버 변수(member variable) 과 멤버 함수(member function) 라고 부름</li>
</ul>
<pre><code>private:
int food;
int weight;</code></pre><ul>
<li>아래에 쓰여진 것들은 모두 객체 내에서 보호되고 있다 라는 의미</li>
<li>모든 것들은 자기 객체 안에서만 접근할 수 있을 뿐 객체 외부에서는 접근할 수 없게 됨</li>
</ul>
<pre><code>public:
void set_animal(int _food, int _weight) {
  food = _food;
  weight = _weight;
}
void increase_food(int inc) {
  food += inc;
  weight += (inc / 3);
}
void view_stat() {
  std::cout &lt;&lt; &quot;이 동물의 food   : &quot; &lt;&lt; food &lt;&lt; std::endl;
  std::cout &lt;&lt; &quot;이 동물의 weight : &quot; &lt;&lt; weight &lt;&lt; std::endl;
}</code></pre><ul>
<li>public 이라는 것은 말 그대로 공개된 것으로 외부에서 마음껏 이용할 수 있게 됨<pre><code>animal.set_animal(100, 50);
animal.increase_food(30);
animal.view_stat();</code></pre></li>
<li>이런식으로 다 지원하게 됨</li>
<li>만일 멤버 함수들을 private 로 설정해버렸다면 어떨까요. public 키워드를 지워봄<blockquote>
<p>&#39;Animal::set_animal&#39; : cannot access private member declared in class &#39;Animal&#39;</p>
</blockquote>
</li>
</ul>
<p>이런 에러가 발생</p>
<pre><code>class Animal {
  int food;
  int weight;
  // ... 생략</code></pre><ul>
<li>이런식으로 작성하면 다 private으로 작성하게됨</li>
</ul>
<h1 id="4---2-클래스의-세계로-오신-것을-환영합니다-함수의-오버로딩-생성자">4 - 2. 클래스의 세계로 오신 것을 환영합니다. (함수의 오버로딩, 생성자)</h1>
<h2 id="함수의-오버로딩-overloading">함수의 오버로딩 (Overloading)</h2>
<ol>
<li><p>과적하다</p>
</li>
<li><p>sb (with sth) 너무 많이 주다[부과하다]</p>
</li>
<li><p>(컴퓨터・전기 시스템 등에) 과부하가 걸리게 하다 </p>
</li>
</ol>
<p>이런뜻으로 들어감</p>
<pre><code>/* 함수의 오버로딩 */
#include &lt;iostream&gt;

void print(int x) { std::cout &lt;&lt; &quot;int : &quot; &lt;&lt; x &lt;&lt; std::endl; }
void print(char x) { std::cout &lt;&lt; &quot;char : &quot; &lt;&lt; x &lt;&lt; std::endl; }
void print(double x) { std::cout &lt;&lt; &quot;double : &quot; &lt;&lt; x &lt;&lt; std::endl; }

int main() {
  int a = 1;
  char b = &#39;c&#39;;
  double c = 3.2f;

  print(a);
  print(b);
  print(c);

  return 0;
}</code></pre><ul>
<li>해당 코드에서 신기한 점은 함수의 이름이 동일하다는것임</li>
<li>하지만 해당것은 오류가 안나오고 코드가 compile 됨</li>
</ul>
<p>하지만</p>
<pre><code>/* 함수의 오버로딩 */
#include &lt;iostream&gt;

void print(int x) { std::cout &lt;&lt; &quot;int : &quot; &lt;&lt; x &lt;&lt; std::endl; }
void print(double x) { std::cout &lt;&lt; &quot;double : &quot; &lt;&lt; x &lt;&lt; std::endl; }

int main() {
  int a = 1;
  char b = &#39;c&#39;;
  double c = 3.2f;

  print(a);
  print(b);
  print(c);

  return 0;
}</code></pre><p>이는 지금 print로 int와 double로 두개의 함수를 작성하지만 main함수에서는 char가 있음 이에 결과는 </p>
<blockquote>
<p>int : 1
int : 99
double : 3.2</p>
</blockquote>
<p>이상하게 결과가 나옴</p>
<p>오버로딩의 과정 같은 경우</p>
<ul>
<li><p>1 단계
자신과 타입이 정확히 일치하는 함수를 찾음</p>
</li>
<li><p>2 단계
정확히 일치하는 타입이 없는 경우 아래와 같은 형변환을 통해서 일치하는 함수를 찾아봄</p>
<p>Char, unsigned char, short 는 int 로 변한왐</p>
<p>Unsigned short 는 int 의 크기에 따라 int 혹은 unsigned int 로 변환됨</p>
<p>Float 은 double 로 변환됨</p>
<p>Enum 은 int 로 변환됨</p>
</li>
<li><p>3 단계
위와 같이 변환해도 일치하는 것이 없다면 아래의 좀더 포괄적인 형변환을 통해 일치하는 함수를 찾음</p>
<p>임의의 숫자(numeric) 타입은 다른 숫자 타입으로 변환됨 
ex) float -&gt; int</p>
<p>Enum 도 임의의 숫자 타입으로 변환ehla ex) Enum -&gt; double</p>
<p>0 은 포인터 타입이나 숫자 타입으로 변환된 0 은 포인터 타입이나 숫자 타입으로 변환됨</p>
<p>포인터는 void 포인터로 변환됨</p>
</li>
<li><p>4 단계
유저 정의된 타입 변환으로 일치하는 것을 찾음 </p>
</li>
</ul>
<p>(만약에 컴파일러가 위 과정을 통하더라도 일치하는 함수를 찾을 수 없거나 같은 단계에서 두 개 이상이 일치하는 경우에 모호하다 (ambiguous) 라고 판단해서 오류를 발생하게 됨)</p>
<pre><code>// 모호한 오버로딩
#include &lt;iostream&gt;

void print(int x) { std::cout &lt;&lt; &quot;int : &quot; &lt;&lt; x &lt;&lt; std::endl; }
void print(char x) { std::cout &lt;&lt; &quot;double : &quot; &lt;&lt; x &lt;&lt; std::endl; }

int main() {
  int a = 1;
  char b = &#39;c&#39;;
  double c = 3.2f;

  print(a);
  print(b);
  print(c);

  return 0;
}</code></pre><blockquote>
<p> error C2668: &#39;print&#39; : ambiguous call to overloaded function
 could be &#39;void print(char)&#39;
 or       &#39;void print(int)&#39;
while trying to match the argument list &#39;(double)&#39;</p>
</blockquote>
<ul>
<li>일단 위 소스에서는 함수가 print (int x) 와 print (char x) 밖에 없으므로 관건은 print(c); 를 했을 때 어떠한 함수가 호출되어야 하는지 결정하는 것인데요, print(c) 를 했을 때 1 단계에서는 명백하게 일치하는 것이 없음</li>
<li>그래서 아무것도 없어서 오류가 나옴</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[3. C++ 의 세계로 오신 것을 환영합니다. (new, delete)]]></title>
            <link>https://velog.io/@happy_lee0_0/3.-C-%EC%9D%98-%EC%84%B8%EA%B3%84%EB%A1%9C-%EC%98%A4%EC%8B%A0-%EA%B2%83%EC%9D%84-%ED%99%98%EC%98%81%ED%95%A9%EB%8B%88%EB%8B%A4.-new-delete</link>
            <guid>https://velog.io/@happy_lee0_0/3.-C-%EC%9D%98-%EC%84%B8%EA%B3%84%EB%A1%9C-%EC%98%A4%EC%8B%A0-%EA%B2%83%EC%9D%84-%ED%99%98%EC%98%81%ED%95%A9%EB%8B%88%EB%8B%A4.-new-delete</guid>
            <pubDate>Wed, 18 Mar 2026 22:50:28 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>해당 내용은 씹어먹는 C++ 책을 기반으로 하단 블로그의 내용을 공부하기위해 적성한 글임을 알립니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/cae7e275-c8a1-4551-a4ee-7e7c7c169603/image.png" alt=""></p>
<h3 id="힙">힙</h3>
<ul>
<li>동적으로 할당된 변수가 할당되는 영역(Dynamic Memory Allocation)</li>
<li>프로그래머가 직접 공간을 할당, 해제하는 메모리 공간</li>
</ul>
<h3 id="스택">스택</h3>
<ul>
<li>프로그램이 자동으로 사용하는 임시 메모리 영역</li>
</ul>
<hr>
<ul>
<li><p>함수 호출 시 생성되는 지역 변수와 매개 변수가 저장되는 영역으로, 함수 호출이 완료되면 저장된 메모리도 해제됨</p>
</li>
<li><p>C 언어에서는 malloc 과 free 함수를 지원하여 힙 상에서의 메모리 할당을 지원함</p>
</li>
<li><p>C++ 에서도 마찬가지로 malloc 과 free 함수를 사용할 수 있는데 그것은  언어 차원에서 지원하는 것으로 바로 new 와 delete 라고 할 수 있음</p>
</li>
</ul>
<p><strong>new(동적 메모리 할당)</strong></p>
<ul>
<li>프로그램 실행 중에 heap 영역에 메모리를 할당</li>
<li>할당된 메모리의 <strong>주소(pointer)</strong>를 반환</li>
</ul>
<p><strong>delete (동적 메모리 해제)</strong></p>
<ul>
<li>new로 할당한 메모리를 반드시 해제</li>
<li>해제하지 않으면 → 메모리 누수(memory leak) 발생</li>
</ul>
<pre><code>/* new 와 delete 의 사용 */
#include &lt;iostream&gt;

int main() {
  int* p = new int;
  *p = 10;

  std::cout &lt;&lt; *p &lt;&lt; std::endl;

  delete p;
  return 0;
}</code></pre><h3 id="사용방법">사용방법</h3>
<p>int* p = new int; 로 p 에 집어 넣었음을 알 수 있음
T* pointer = new T; 에 T 에는 임의의 타입이 들어가고 *p = 10;
를 통해서 값을 집어넣었고 이를 출력함</p>
<p>위와 같이 delete p 를 하게 되면 p 에 할당된 공간이 해제 가능 </p>
<p>delete 로 해제할 수 있는 메모리 공간은 사용자가 new 를 통해서 할당한 공간만 가능</p>
<pre><code>/* 지역 변수 delete 하기 */
#include &lt;iostream&gt;

int main() {
  int a = 5;

  delete &amp;a;

  return 0;
}</code></pre><p> 이런경우 delete 로 해제해버리려 한다면 Heap 이 아닌 공간을 해제하려고 한다는 경고 메세지가 나타나게 됨</p>
<pre><code>/* new 로 배열 할당하기 */

#include &lt;iostream&gt;

int main() {
  int arr_size;
  std::cout &lt;&lt; &quot;array size : &quot;;
  std::cin &gt;&gt; arr_size;
  int *list = new int[arr_size];
  for (int i = 0; i &lt; arr_size; i++) {
    std::cin &gt;&gt; list[i];
  }
  for (int i = 0; i &lt; arr_size; i++) {
    std::cout &lt;&lt; i &lt;&lt; &quot;th element of list : &quot; &lt;&lt; list[i] &lt;&lt; std::endl;
  }
  delete[] list;
  return 0;
}</code></pre><ul>
<li>*list = new int[arr_size]; 해당것은 이걸로 new로 힙에 메모리를 할당한것을 주소로 반환한것을 포인터에 저장한것</li>
</ul>
<hr>
<pre><code>#include &lt;iostream&gt;

typedef struct Animal {
  char name[30];  // 이름
  int age;        // 나이

  int health;  // 체력
  int food;    // 배부른 정도
  int clean;   // 깨끗한 정도
} Animal;

void create_animal(Animal *animal) {
  std::cout &lt;&lt; &quot;동물의 이름? &quot;;
  std::cin &gt;&gt; animal-&gt;name;

  std::cout &lt;&lt; &quot;동물의 나이? &quot;;
  std::cin &gt;&gt; animal-&gt;age;

  animal-&gt;health = 100;
  animal-&gt;food = 100;
  animal-&gt;clean = 100;
}

void play(Animal *animal) {
  animal-&gt;health += 10;
  animal-&gt;food -= 20;
  animal-&gt;clean -= 30;
}
void one_day_pass(Animal *animal) {
  // 하루가 지나면
  animal-&gt;health -= 10;
  animal-&gt;food -= 30;
  animal-&gt;clean -= 20;
}
void show_stat(Animal *animal) {
  std::cout &lt;&lt; animal-&gt;name &lt;&lt; &quot;의 상태&quot; &lt;&lt; std::endl;
  std::cout &lt;&lt; &quot;체력    : &quot; &lt;&lt; animal-&gt;health &lt;&lt; std::endl;
  std::cout &lt;&lt; &quot;배부름 : &quot; &lt;&lt; animal-&gt;food &lt;&lt; std::endl;
  std::cout &lt;&lt; &quot;청결    : &quot; &lt;&lt; animal-&gt;clean &lt;&lt; std::endl;
}
int main() {
  Animal *list[10];
  int animal_num = 0;

  for (;;) {
    std::cout &lt;&lt; &quot;1. 동물 추가하기&quot; &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;2. 놀기 &quot; &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;3. 상태 보기 &quot; &lt;&lt; std::endl;

    int input;
    std::cin &gt;&gt; input;

    switch (input) {
      int play_with;
      case 1:
        list[animal_num] = new Animal;
        create_animal(list[animal_num]);

        animal_num++;
        break;
      case 2:
        std::cout &lt;&lt; &quot;누구랑 놀게? : &quot;;
        std::cin &gt;&gt; play_with;

        if (play_with &lt; animal_num) play(list[play_with]);

        break;

      case 3:
        std::cout &lt;&lt; &quot;누구껄 보게? : &quot;;
        std::cin &gt;&gt; play_with;
        if (play_with &lt; animal_num) show_stat(list[play_with]);
        break;
    }

    for (int i = 0; i != animal_num; i++) {
      one_day_pass(list[i]);
    }
  }
  for (int i = 0; i != animal_num; i++) {
    delete list[i];
  }
}</code></pre><ul>
<li>해당것은 동물 관련으로 진행하는 미니게임 형식임</li>
</ul>
<pre><code>typedef struct Animal {
  char name[30];  // 이름
  int age;        // 나이

  int health;  // 체력
  int food;    // 배부른 정도
  int clean;   // 깨끗한 정도
} Animal;</code></pre><ul>
<li><p>이부분은 구조체로 서로 다른 타입의 데이터를 하나로 묶으로 지정하는 사용자 정의 자료형임</p>
</li>
<li><p>void one_day_pass, play etc, 이것들은 함수로 지정하여 switch 로 지정했을때 그것을 할당하는것을 의미하는것임</p>
</li>
<li><p>키 포인트는 동물을 포인터로 만들어 그것을 선택하면 동물값을 입력하는 구조로 만듬</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2. C++ 참조자(레퍼런스)의 도입]]></title>
            <link>https://velog.io/@happy_lee0_0/2.-C-%EC%B0%B8%EC%A1%B0%EC%9E%90%EB%A0%88%ED%8D%BC%EB%9F%B0%EC%8A%A4%EC%9D%98-%EB%8F%84%EC%9E%85</link>
            <guid>https://velog.io/@happy_lee0_0/2.-C-%EC%B0%B8%EC%A1%B0%EC%9E%90%EB%A0%88%ED%8D%BC%EB%9F%B0%EC%8A%A4%EC%9D%98-%EB%8F%84%EC%9E%85</guid>
            <pubDate>Wed, 18 Mar 2026 00:48:06 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>해당 내용은 씹어먹는 C++ 책을 기반으로 하단 블로그의 내용을 공부하기위해 적성한 글임을 알립니다.</p>
</blockquote>
<h3 id="레퍼런스의-개념">레퍼런스의 개념</h3>
<p>int x = 10;
int&amp; r = x;</p>
<ul>
<li><p>r과 x는 완전히 동일한 객체</p>
</li>
<li><p>r을 바꾸면 x가 바뀜</p>
</li>
<li><p>선언과 동시에 반드시 초기화 필요</p>
</li>
<li><p>한 번 바인딩되면 다른 변수로 변경 불가</p>
</li>
<li><p>포인터: 주소를 저장 (int* p)</p>
</li>
<li><p>래퍼런스: 변수의 별명 (int&amp; r)</p>
</li>
</ul>
<blockquote>
<p>래퍼런스 = “복사가 아닌 원본 자체를 직접 다루는 별명”</p>
</blockquote>
<pre><code>#include &lt;iostream&gt;

int change_val(int *p) {
  *p = 3;

  return 0;
}
int main() {
  int number = 5;

  std::cout &lt;&lt; number &lt;&lt; std::endl;
  change_val(&amp;number);
  std::cout &lt;&lt; number &lt;&lt; std::endl;
}</code></pre><p>change_val 함수의 인자 p 에 number 의 주소값을 전달하여, *p 를 통해 number 를 참조하여 number 의 값을 3 으로 바꿈</p>
<blockquote>
<p>int&amp; another_a = a;</p>
</blockquote>
<p>그 후에 우리는 a 의 참조자 another_a 를 정의하였습니다. 이 때 참조자를 정하는 방법은, 가리키고자 하는 타입 뒤에 &amp; 를 붙이면 됨</p>
<pre><code>int a = 10;
int* p = &amp;a; // p 는 a 를 가리킨다.

int b = 3;
p = &amp;b // 이제 p 는 a 를 버리고 b 를 가리킨다</code></pre><ul>
<li>p는 a를 가리키는것 즉 a가 현재 주소값을 보여 그것을 가리킴</li>
<li>b는 3의 주소값을 가리킴</li>
</ul>
<pre><code>#include &lt;iostream&gt;

int main() {
  int &amp;ref = 4;

  std::cout &lt;&lt; ref &lt;&lt; std::endl;
}</code></pre><ul>
<li>오류가나옴</li>
<li>위 상수 값 자체는 리터럴 이기 때문에</li>
<li>프로그래밍 언어에서 리터럴(literal)이란, 소스 코드 상에서 고정된 값을 가지는 것을 일컫습니다</li>
</ul>
<pre><code>int a, b;
int&amp; arr[2] = {a, b};</code></pre><ul>
<li>주소값이 존재한다라는 의미는 해당 원소가 메모리 상에서 존재한다 라는 의미와 같습니다. 하지만 레퍼런스는 특별한 경우가 아닌 이상 메모리 상에서 공간을 차지 하지 않습니다. 따라서 이러한 모순 때문에 레퍼런스들의 배열을 정의하는 것은 언어 차원에서 금지가 됨</li>
</ul>
<pre><code>int&amp; function() {
  int a = 2;
  return a;
}

int main() {
  int b = function();
  b = 3;
  return 0;
}</code></pre><p>-&gt; [1]    7170 segmentation fault (core dumped)  ./test 이런 오류가 나옴</p>
<pre><code>int&amp; ref = a;

// 근데 a 가 사라짐
int b = ref;  // !!!</code></pre><ul>
<li>레퍼런스가 필요한데 사라지게됨 이래서 문제임</li>
<li>따라서 위 처럼 레퍼런스를 리턴하는 함수에서 지역 변수의 레퍼런스를 리턴하지 않도록 조심해야함</li>
</ul>
<pre><code>int&amp; function(int&amp; a) {
  a = 5;
  return a;
}

int main() {
  int b = 2;
  int c = function(b);
  return 0;
}</code></pre><ul>
<li>이경우는 래퍼런스 값이 b로 연결이 되어 그대로 레퍼런스와 연결이 됨 그래서 오류가 안나옴<pre><code>#include &lt;iostream&gt;
</code></pre></li>
</ul>
<p>int function() {
  int a = 5;
  return a;
}</p>
<p>int main() {
  const int&amp; c = function();
  std::cout &lt;&lt; &quot;c : &quot; &lt;&lt; c &lt;&lt; std::endl;
  return 0;
}</p>
<pre><code>이런 경우 const로 예외 상수로 받아 상수 레퍼런스로 리턴값을 받게 되면 해당 리턴값의 생명이 연장됨

| 함수 반환              | 받는 방식                | 가능 여부  | 동작 설명                         | 주의사항            |
| ------------------ | -------------------- | ------ | ----------------------------- | --------------- |
| `int f()` (값 반환)   | `int a = f()`        | 가능     | 값 복사됨                         | 없음              |
| `int f()` (값 반환)   | `int&amp; a = f()`       | 컴파일 오류 | rvalue를 non-const 참조로 받을 수 없음 | -               |
| `int f()` (값 반환)   | `const int&amp; a = f()` | 가능     | 임시 객체를 const 참조로 바인딩          | lifetime 연장     |
| `int&amp; f()` (참조 반환) | `int a = f()`        | 가능     | 값 복사됨                         | 참조 의미 사라짐       |
| `int&amp; f()` (참조 반환) | `int&amp; a = f()`       | 가능     | 원본 변수 참조                      | **지역 변수 반환 금지** |
| `int&amp; f()` (참조 반환) | `const int&amp; a = f()` | 가능     | const 참조로 바인딩                 | **지역 변수 반환 금지** |




---
### 참고
https://modoocode.com/141</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[1-2 / 1-3 첫 C++ 프로그램 분석 및 공통점]]></title>
            <link>https://velog.io/@happy_lee0_0/1-2-1-3-%EC%B2%AB-C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EB%B6%84%EC%84%9D-%EB%B0%8F-%EA%B3%B5%ED%86%B5%EC%A0%90</link>
            <guid>https://velog.io/@happy_lee0_0/1-2-1-3-%EC%B2%AB-C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EB%B6%84%EC%84%9D-%EB%B0%8F-%EA%B3%B5%ED%86%B5%EC%A0%90</guid>
            <pubDate>Sun, 15 Mar 2026 15:45:28 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>해당 내용은 씹어먹는 C++ 책을 기반으로 하단 블로그의 내용을 공부하기위해 적성한 글임을 알립니다.</p>
</blockquote>
<h2 id="씹어먹는-c---1---2-첫-c-프로그램-분석하기">씹어먹는 C++ - &lt;1 - 2. 첫 C++ 프로그램 분석하기&gt;</h2>
<pre><code>#include &lt;iostream&gt;

int main() {
  std::cout &lt;&lt; &quot;Hello, World!!&quot; &lt;&lt; std::endl;
  return 0;
}</code></pre><blockquote>
<p>#include <iostream></p>
</blockquote>
<ul>
<li>해당 부분은 C++ 에서 표준 입출력에 대한 필요한것을 포함함</li>
</ul>
<blockquote>
<p>int main()</p>
</blockquote>
<ul>
<li>C 와 마찬가지로 C++ 에서의 main 함수는 프로그램이 실행될 때 가장 먼저 실행되는 함수</li>
</ul>
<blockquote>
<p>std::cout &lt;&lt; &quot;Hello, World!!&quot; &lt;&lt; std::endl;
return 0;  </p>
</blockquote>
<ul>
<li><p>std::cout 은 화면에 무언가 출력시켜주는 것 같은데, printf 와 다르게 사용된 것을 보니 함수 같지는 않네요. 그리고 화면에 출력된 것을 대충 보면 &quot;계속하려면 아무 키나 누르세요&quot; 가 한 줄 개행되어서 나온 것을 보니 std::endl 은 한 줄 엔터를 쳐서 나타내라는 표시 </p>
<ul>
<li>main 함수에서도 return</li>
</ul>
</li>
</ul>
<blockquote>
<p>std::cout</p>
</blockquote>
<ul>
<li>std 는 C++ 표준 라이브러리의 모든 함수, 객체 등이 정의된 이름 공간(namespace) </li>
<li>std 라는 이름 공간에 정의되어 있는 cout 을 의미 합니다. 만약에 std:: 없이 그냥 cout 이라고 한다면 컴파일러가 cout 을 찾지 못합니다. 서울에 사는 철수인지 부산에 사는 철수 인지 알 길이 없기 때문</li>
</ul>
<pre><code>#include &quot;header1.h&quot;
#include &quot;header2.h&quot;

namespace header1 {
int func() {
  foo();           // 알아서 header1::foo() 가 실행된다.
  header2::foo();  // header2::foo() 가 실행된다.
}
}  // namespace header1</code></pre><ul>
<li>소속을 밝히면 이게 이거를 불러오게 됨<pre><code>#include &quot;header1.h&quot;
#include &quot;header2.h&quot;
using namespace header1;
</code></pre></li>
</ul>
<p>int main() {
  header2::foo();  // header2 에 있는 함수를 호출
  foo();           // header1 에 있는 함수를 호출
}</p>
<pre><code>어쩃든 호출하는것임

---

  다시 돌아가서
</code></pre><p>int main() {
  std::cout &lt;&lt; &quot;Hello, World!!&quot; &lt;&lt; std::endl;
  return 0;
}</p>
<pre><code>두 iostream 헤더파일의 std 라는 이름 공간에 정의되어 있는 것을 불러옴
</code></pre><p>#include <iostream>
using namespace std;</p>
<p>int main() {
  cout &lt;&lt; &quot;Hello, World!!&quot; &lt;&lt; endl;
  return 0;
}</p>
<pre><code>
귀찮은면 이렇게 함

---
## 씹어먹는 C++ - &lt;1 - 3. C++ 은 C 친구 - C 와 공통점&gt;
</code></pre><p>// 변수의 정의
#include <iostream></p>
<p>int main() {
  int i;
  char c;
  double d;
  float f;</p>
<p>  return 0;
}</p>
<pre><code>변수 정의
</code></pre><p>int arr[10];
int *parr = arr;</p>
<p>int i;
int *pi = &i;</p>
<pre><code>- 포인터로 arr[10]이라는 배열이 있음
- int *parr = arr; parr 포인터로 받는 변수를 정해 arr 값의 &amp;arr[0] 주소 값을 저장
 - parr = 1000(&amp;arr[0]의 주소값)
  - *parr = arr[0] 즉 1000이 가리키는 값이 되어 arr[0] 이됨

  - i라는값이 있음
  - i의 주소값을 출력해서 pi포인터에 저장
</code></pre><p>// C++ 의 for 문
#include <iostream></p>
<p>int main() {
  int i;</p>
<p>  for (i = 0; i &lt; 10; i++) {
    std::cout &lt;&lt; i &lt;&lt; std::endl;
  }
  return 0;
}</p>
<pre><code>반복문(for, while)
</code></pre><p>// switch 문 이용하기
#include <iostream></p>
<p>using std::cout;
using std::endl;
using std::cin;</p>
<p>int main() {
  int user_input;
  cout &lt;&lt; &quot;저의 정보를 표시해줍니다&quot; &lt;&lt; endl;
  cout &lt;&lt; &quot;1. 이름 &quot; &lt;&lt; endl;
  cout &lt;&lt; &quot;2. 나이 &quot; &lt;&lt; endl;
  cout &lt;&lt; &quot;3. 성별 &quot; &lt;&lt; endl;
  cin &gt;&gt; user_input;</p>
<p>  switch (user_input) {
    case 1:
      cout &lt;&lt; &quot;Psi ! &quot; &lt;&lt; endl;
      break;</p>
<pre><code>case 2:
  cout &lt;&lt; &quot;99 살&quot; &lt;&lt; endl;
  break;

case 3:
  cout &lt;&lt; &quot;남자&quot; &lt;&lt; endl;
  break;

default:
  cout &lt;&lt; &quot;궁금한게 없군요~&quot; &lt;&lt; endl;
  break;</code></pre><p>  }
  return 0;
}</p>
<p>```
switch 문</p>
<hr>
<p>출처
<a href="https://modoocode.com/136">https://modoocode.com/136</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[1182_부분수열의 합]]></title>
            <link>https://velog.io/@happy_lee0_0/1182%EB%B6%80%EB%B6%84%EC%88%98%EC%97%B4%EC%9D%98-%ED%95%A9</link>
            <guid>https://velog.io/@happy_lee0_0/1182%EB%B6%80%EB%B6%84%EC%88%98%EC%97%B4%EC%9D%98-%ED%95%A9</guid>
            <pubDate>Thu, 12 Feb 2026 02:01:45 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/2d17a7f6-18d4-4b86-9e52-f12420d1574e/image.png" alt=""></p>
<p>예제 입력 1 
5 0
-7 -3 -2 5 8</p>
<p>예제 출력 1 
1</p>
<h2 id="study-part">study part</h2>
<ul>
<li>항상 보는 백트래킹으로 문제를 풀이함</li>
</ul>
<pre><code>    if(arrange_count==input_count)
    {
        return 0;
    }

    if(sum_value+arrange_value[arrange_count]==input_sum)
    {
        output_count+=1;
    }</code></pre><ul>
<li><p>이부분에서는 먼저 기입한 값과 동일하면 끝 그리고 input_sum이 같으면 output_count+1 형식으로 결과를 도출하고 자함</p>
<pre><code>  check_num(sum_value,arrange_count+1);

  check_num(sum_value+arrange_value[arrange_count],arrange_count+1);

  return 0;</code></pre></li>
<li><p>이부분에서는 진행정도와 값이 계속 누적되는것을 추가하는것으로 합해지는것 이렇게 해서 check_num을 만드는것</p>
</li>
</ul>
<h2 id="code">code</h2>
<pre><code>#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;algorithm&gt;
#include &lt;vector&gt;
using namespace std;

int input_count;

int input_sum;

int output_count = 0;

vector &lt;int&gt; arrange_value;

int check_num(int sum_value,int arrange_count)
{
    if(arrange_count==input_count)
    {
        return 0;
    }

    if(sum_value+arrange_value[arrange_count]==input_sum)
    {
        output_count+=1;
    }

    check_num(sum_value,arrange_count+1);

    check_num(sum_value+arrange_value[arrange_count],arrange_count+1);

    return 0;
}

int main()
{
    cin&gt;&gt;input_count&gt;&gt;input_sum;
    arrange_value.resize(input_count);

    for(int i = 0 ; i&lt;input_count;i++)
    {
        cin&gt;&gt;arrange_value[i];
    }

    check_num(0,0);

    cout&lt;&lt;output_count;

}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[1. Control system engineering(Introduction)]]></title>
            <link>https://velog.io/@happy_lee0_0/1.-%EC%84%9C%EB%A1%A0</link>
            <guid>https://velog.io/@happy_lee0_0/1.-%EC%84%9C%EB%A1%A0</guid>
            <pubDate>Mon, 09 Feb 2026 09:41:54 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>해당 글은 개인적으로 공부하기 위한 글로 알아주시면 감사하겠습니다.</p>
</blockquote>
<h1 id="1시스템-구조">1.시스템 구조</h1>
<h2 id="개루프-시스템">개루프 시스템</h2>
<ul>
<li>개루프 시스템은 입력 명령을 제어하기에 적합한 형태로 변환하는 <strong>입력변환기(input transducer)</strong>라는 보조 시스템으로 시작함</li>
<li>입력은 <strong>기준입력(reference)</strong>로 칭하며 출력은 <strong>피제어 변수(controlled variable)</strong>라고 칭함</li>
<li>외란과 같은 신호는 입력되는 신호들을 대수적으로 합하는 접합점(summing junction)에서 제어기나 공정의 출력과 합해짐</li>
<li>질량이 움직일때 일정한 힘을 소모하는 <strong>제동기(damper)</strong>와 질량 및 스프링으로 구성되는 기계 시스템은 또 다른 개루프 시스템의 한 예임</li>
<li>개루프는 외란에 민감하여 외란을 제거하지 못하는 것이 단점</li>
</ul>
<h2 id="폐루프-시스템feedback-system">폐루프 시스템(feedback system)</h2>
<ul>
<li>입력 변환기는 입력 명령을 제어기에 적합한 형태로 변환시키며 출력변환기(output transducer) 또는 센서(sensor)로 출력을 측정하여 제어기에 적합한 형태로 변환</li>
<li>접합점으로 피드백 경로(feedback path)을 통하여 도달하는 출력 신호와 입력 신호를 대수적으로 합함</li>
<li>출력 신호의 부호가 반전되어 신호가 합해지는데 이를 구동신호(actuating signal)이라고 함</li>
<li>실제 입력과 출력의 차가 구동 신호가 되는데이를 오차(error) 라고 함</li>
<li>간단한 이득 조정을 하거나 제어기를 재설계함으로 써 과도 응답과 정상 상태 오차를 개선시키는것을 보상(compensating)이라고 함 재설계된 하드웨어를 보상기(compensator)라고 함</li>
</ul>
<h1 id="2-해석과-설계-목적">2. 해석과 설계 목적</h1>
<h2 id="과도-응답">과도 응답</h2>
<ul>
<li>과도 응답이 너무 빠르면 복구하지 못할 물리적 손상을 시스템에 입힐수 있음<h2 id="정상상태-응답">정상상태 응답</h2>
<h2 id="안정도">안정도</h2>
</li>
<li>과도 응답과 정상 상태 오차에 대한 해석과 설계는 시스템이 안정하지 않는 경우에는 의미가 없음</li>
<li>고유응답(natural response)과 강제응답(foreced responser)의 합으로 표시됨</li>
<li>이런 응답을 각각 동차해(homogeneous solution), 특이해(particular solution)이라함</li>
<li>선형시스템은 전체 응답의 다음과 같이 표현됨
$$
전체 응답 = 고유  응답 + 강제응답
$$<h2 id="사례연구">사례연구</h2>
</li>
<li>이런 진동하거나 정상 상태에서는 고유응답이 0으로 되어 강제응답만 남는 경우 불안정(instability)라고 함</li>
<li>감쇠진동(daped oscillation) : 시간에 따라 크기가 줄어드는 정현파 응답하는 과도 상태를 갖게 되는것<h1 id="3-설계-절차">3. 설계 절차</h1>
<h2 id="1단계--요구조건에-맞는-실제-시스템-선정">1단계 : 요구조건에 맞는 실제 시스템 선정</h2>
</li>
<li>요구 조건에 맞는 실제 시스템(physical system)을 선정함<h2 id="2단계--기능별-블럭선도-작성">2단계 : 기능별 블럭선도 작성</h2>
</li>
<li>정상적으로 표현된 시스템의 각 부분을 기능별 블록선도로 나타내고 이를 블록들을 상호 연결하여 전체 블록선도를 완성함<h2 id="3단계--구조-개요도-도안">3단계 : 구조 개요도 도안</h2>
</li>
<li>예로 직류전동기(dc motor) 구동 시키기 위해 차동 증폭기(differential amplifier)와 전력 증폭기는 각각 이득이나 전력 증폭을 하는 제어기로 사용됨</li>
<li>부하(load)의 경우 회전하는 질량과 베어링 마찰을 가지므로 부하 모델은 자동차의 충격 흡수 장치나 스크린 문 제동기(screen door damper)와 같이 속도와 증가함에 따라 역회전 토크(torque)가 증가되는 관성(inertia)와 점성마찰(viscous damping)으로 표현됨</li>
</ul>
<h2 id="4단계--수학적-모델의-개발">4단계 : 수학적 모델의 개발</h2>
<ul>
<li>시스템 구조에 대한 개요도가 완성되면 전기 회로에 kirchhoff의 법칙과 실제 시스템에 대해 적용되는 Newton의 법칙과 같은 물리적 법칙과 가정을 이용하여 시스템을 모델링함</li>
<li>kirchhoff의 전압 / 전류 법칙, newton의 법칙을 이용하면서 동적 시스템의 입력과 출력 사이의 관계를 나타내는 수학적 모델링을 얻을수 있음</li>
<li>선형 시불변 미분방정식(linear time-invariant differential equation)으로 표시됨</li>
<li>시스템 모델은 차수가 높아지거나 비선형 시변(nonlinear time varying) 또는 편미분 방정식(partial differential equation)으로 표현됨</li>
<li>전달함수(transfer function)은 시스템을 수학적으로 모델링하는 방법으로 전달함수를 laplace변환을 이용하여 선형 시불변 미분방정식으로 유도됨</li>
<li>상태공간(state space)에서도 시스템 모델이 표현될수 있음 이는 모델을 상태 공간에서 표현하려는 이유는 선형 미분 방정식으로 나타낼수 없는 시스템에서도 적용할수 있을 뿐만 아니라 시뮬레이션하기 편한 형태로 나타낼수 있기 때문<h2 id="5단계--블럭선도-단순화">5단계 : 블럭선도 단순화</h2>
<h2 id="6단계--해석과-설계">6단계 : 해석과 설계</h2>
<img src="https://velog.velcdn.com/images/happy_lee0_0/post/9e2c85bc-5573-4819-b00f-89966200956d/image.png" alt=""></li>
<li>계단 함수 : 위치 속도나 가속도가 일정한 크기를 가질때 입력 명령을 표시하는데 사용됨</li>
<li>램프 함수 입력 : 선형으로 증가하는 명령을 나타냄</li>
<li>정현파 입력 : 시스템에 대한 수학적 모델을 유도하기 위하여 실제 시스템을 시험하는데 이용됨</li>
</ul>
<p>감도(sensitivity) : 시스템 파라미터 변동에 대한 성능 변동을 퍼센트로</p>
<hr>
<p>english version</p>
<h1 id="1-system-architecture">1. System Architecture</h1>
<h2 id="11-open-loop-systems">1.1 Open-Loop Systems</h2>
<p>An <strong>open-loop control system</strong> operates without feedback.</p>
<ul>
<li>The system begins with an <strong>input transducer</strong>, which converts the command into a form suitable for the controller/plant.</li>
<li>The input signal is called the <strong>reference input</strong>.</li>
<li>The output is referred to as the <strong>controlled variable</strong>.</li>
<li>Disturbances may enter at a <strong>summing junction</strong>, where signals are algebraically combined.</li>
</ul>
<p><strong>Example:</strong> A mechanical system composed of a mass, spring, and damper.</p>
<ul>
<li>A <strong>damper</strong> dissipates energy (typically proportional to velocity).</li>
</ul>
<p><strong>Limitation:</strong><br>Open-loop systems are sensitive to disturbances and cannot automatically correct for unexpected variations, leading to degraded accuracy and robustness.</p>
<hr>
<h2 id="12-closed-loop-systems-feedback-systems">1.2 Closed-Loop Systems (Feedback Systems)</h2>
<p>A <strong>closed-loop system</strong> uses feedback to reduce error and improve robustness.</p>
<ul>
<li>The <strong>input transducer</strong> converts the reference command for the controller.</li>
<li>A <strong>sensor (output transducer)</strong> measures the output and converts it into a comparable signal form.</li>
<li>The measured output returns through the <strong>feedback path</strong> to the summing junction.</li>
</ul>
<p>The <strong>error (actuating signal)</strong> is:</p>
<p>$$
e(t) = r(t) - y(t)
$$</p>
<p>where:</p>
<ul>
<li>$r(t)$: reference input  </li>
<li>$y(t)$: output  </li>
<li>$e(t)$: error (actuating signal)</li>
</ul>
<h3 id="compensation">Compensation</h3>
<p>Improving transient response or steady-state error by gain adjustment or controller redesign is called <strong>compensation</strong>.  
The redesigned element is called a <strong>compensator</strong>.</p>
<hr>
<h1 id="2-objectives-of-analysis-and-design">2. Objectives of Analysis and Design</h1>
<p>Control design typically focuses on:</p>
<ul>
<li>Transient response  </li>
<li>Steady-state response  </li>
<li>Stability  </li>
</ul>
<hr>
<h2 id="21-transient-response">2.1 Transient Response</h2>
<p>Transient response describes system behavior before reaching steady state.</p>
<p>Key measures include:</p>
<ul>
<li>rise time  </li>
<li>peak time  </li>
<li>overshoot  </li>
<li>settling time  </li>
</ul>
<p><strong>Engineering note:</strong> An excessively fast transient can cause actuator saturation or physical damage in real systems.</p>
<hr>
<h2 id="22-steady-state-response">2.2 Steady-State Response</h2>
<p>Steady-state response describes long-term behavior after transients decay.</p>
<p>A key metric is the <strong>steady-state error</strong>, which depends on:</p>
<ul>
<li>system type  </li>
<li>input class (step, ramp, etc.)</li>
</ul>
<hr>
<h2 id="23-stability">2.3 Stability</h2>
<p>If the system is unstable, transient and steady-state specifications are meaningless.</p>
<p>For an LTI system:</p>
<p>$$
\text{Total Response} = \text{Natural Response} + \text{Forced Response}
$$</p>
<ul>
<li>Natural response = <strong>homogeneous solution</strong></li>
<li>Forced response = <strong>particular solution</strong></li>
</ul>
<p>For a stable system:</p>
<p>$$
\lim_{t \to \infty} \text{Natural Response} = 0
$$</p>
<h3 id="damped-oscillation">Damped Oscillation</h3>
<p>A <strong>damped oscillation</strong> is a transient sinusoidal response whose amplitude decreases over time due to dissipation.</p>
<hr>
<h1 id="3-design-procedure">3. Design Procedure</h1>
<h2 id="step-1-select-the-physical-system">Step 1: Select the Physical System</h2>
<p>Select a physical system that satisfies the given performance requirements, constraints, and operating conditions.</p>
<hr>
<h2 id="step-2-build-the-functional-block-diagram">Step 2: Build the Functional Block Diagram</h2>
<p>Represent each subsystem as a functional block and interconnect them to describe overall signal flow.</p>
<hr>
<h2 id="step-3-draw-the-structural-schematic">Step 3: Draw the Structural Schematic</h2>
<p>Example: DC motor drive system</p>
<ul>
<li>differential amplifier: gain control  </li>
<li>power amplifier: power amplification  </li>
<li>load: inertia + viscous damping  </li>
</ul>
<p>A common load model:</p>
<p>$$
T = J\dot{\omega} + B\omega
$$</p>
<p>where:</p>
<ul>
<li>$J$: inertia</li>
<li>$B$: viscous damping</li>
<li>$\omega$: angular velocity</li>
</ul>
<hr>
<h2 id="step-4-develop-the-mathematical-model">Step 4: Develop the Mathematical Model</h2>
<p>Use physical laws:</p>
<ul>
<li>Kirchhoff’s Voltage/Current Laws (electrical)</li>
<li>Newton’s Second Law (mechanical)</li>
</ul>
<p>Typical result: an <strong>LTI differential equation</strong> relating input and output.</p>
<h3 id="transfer-function">Transfer Function</h3>
<p>Applying Laplace transform:</p>
<p>$$
G(s) = \frac{Y(s)}{R(s)}
$$</p>
<h3 id="state-space-model">State-Space Model</h3>
<p>State equation:</p>
<p>$$
\dot{x} = Ax + Bu
$$</p>
<p>Output equation:</p>
<p>$$
y = Cx + Du
$$</p>
<p><strong>Why state-space?</strong></p>
<ul>
<li>supports MIMO</li>
<li>handles nonlinear extensions</li>
<li>convenient for simulation</li>
<li>foundation of modern control</li>
</ul>
<hr>
<h2 id="step-5-block-diagram-reduction">Step 5: Block Diagram Reduction</h2>
<p>Simplify interconnected blocks using algebraic reduction rules to obtain the overall system form.</p>
<hr>
<h2 id="step-6-analysis-and-controller-design">Step 6: Analysis and Controller Design</h2>
<p>Design the controller to satisfy:</p>
<ul>
<li>stability</li>
<li>transient performance</li>
<li>steady-state accuracy</li>
<li>robustness</li>
</ul>
<hr>
<h1 id="4-standard-test-inputs">4. Standard Test Inputs</h1>
<ul>
<li><strong>Step input:</strong> represents a sudden command (often used in position control evaluation)</li>
<li><strong>Ramp input:</strong> represents a linearly increasing command (useful for tracking analysis)</li>
<li><strong>Sinusoidal input:</strong> used for frequency-response testing and system identification</li>
</ul>
<hr>
<h1 id="5-sensitivity">5. Sensitivity</h1>
<p>Sensitivity measures performance variation due to parameter changes:</p>
<p>$$
S = \frac{% \text{ change in output}}{% \text{ change in parameter}}
$$</p>
<p>Low sensitivity implies strong robustness to modeling uncertainty and component tolerances.</p>
<hr>
<h1 id="6-core-design-balance">6. Core Design Balance</h1>
<p>Control design fundamentally balances:</p>
<ol>
<li>Stability  </li>
<li>Performance  </li>
<li>Robustness  </li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[모터 확인용]]></title>
            <link>https://velog.io/@happy_lee0_0/%EB%AA%A8%ED%84%B0-%ED%99%95%EC%9D%B8%EC%9A%A9</link>
            <guid>https://velog.io/@happy_lee0_0/%EB%AA%A8%ED%84%B0-%ED%99%95%EC%9D%B8%EC%9A%A9</guid>
            <pubDate>Wed, 04 Feb 2026 03:09:05 GMT</pubDate>
            <description><![CDATA[<p>그냥 끄적이고 싶어서 적는용</p>
<p>감속비 : 감속기어를통해 모터의엔진의 입력 회전 속도를 얼마나 줄여서 출력하는지를 나타내는 비율 즉 이거를 하면 기본 모터 단독 스펙에 rpm 자체가 1/14로 줄어들게 됨</p>
<p>허용 부화 : kg/cm 인데 이게 작동하고 나서 유지하는 힘을 기입</p>
<p>순시허용 토크 : 처음 시작할때 확 올라가는 토크임
<img src="https://velog.velcdn.com/images/happy_lee0_0/post/43b6eecd-db13-476a-8dd8-2ed5eabf34fc/image.png" alt="">
이것도 최대 토크는 처음 시작하는 힘 그리고 최대 출력으로는 그냥 유지 될수 있는힘</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[6603_로또]]></title>
            <link>https://velog.io/@happy_lee0_0/6603%EB%A1%9C%EB%98%90</link>
            <guid>https://velog.io/@happy_lee0_0/6603%EB%A1%9C%EB%98%90</guid>
            <pubDate>Fri, 09 Jan 2026 02:12:05 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/bbc7d3cf-4845-4549-9e1b-eab8d0484283/image.png" alt="">
예제 입력 1 
7 1 2 3 4 5 6 7
8 1 2 3 5 8 13 21 34
0
예제 출력 1 
1 2 3 4 5 6
1 2 3 4 5 7
1 2 3 4 6 7
1 2 3 5 6 7
1 2 4 5 6 7
1 3 4 5 6 7
2 3 4 5 6 7</p>
<p>1 2 3 5 8 13
1 2 3 5 8 21
1 2 3 5 8 34
1 2 3 5 13 21
1 2 3 5 13 34
1 2 3 5 21 34
1 2 3 8 13 21
1 2 3 8 13 34
1 2 3 8 21 34
1 2 3 13 21 34
1 2 5 8 13 21
1 2 5 8 13 34
1 2 5 8 21 34
1 2 5 13 21 34
1 2 8 13 21 34
1 3 5 8 13 21
1 3 5 8 13 34
1 3 5 8 21 34
1 3 5 13 21 34
1 3 8 13 21 34
1 5 8 13 21 34
2 3 5 8 13 21
2 3 5 8 13 34
2 3 5 8 21 34
2 3 5 13 21 34
2 3 8 13 21 34
2 5 8 13 21 34
3 5 8 13 21 34</p>
<h2 id="study-code">Study code</h2>
<ul>
<li>해당 문제를 풀면서 이제 이해헀다고 생각했는데 아직 이해를 못했다</li>
<li>결과적으로 또 반복하지만 내 위치를 파악하고 그 위치에 도착하면 결과를 출력해라 이것이 메인임</li>
<li>또 해당 결과가 함축했다가 나오는게 아니라 할때마다 계속나와도 되는 것을 파악하고 0 에 결과가 아니라 그냥 할때 마다 결과를 도출함</li>
<li>그리고 여기서 굳이 2차원을 만들필요도 없었고 그냥 1차원으로 해서 만들어도 되었음</li>
<li>그래서 필요없는 clear를 쓰고 이런거임</li>
<li>결론적으로 도착하면 결과 그리고 layer 자체가 내가 input 한거 보다 이상 넘어갈수 있어서 그게 아니면 return임</li>
<li>추가적으로 void 라서 return 0  이 아니라 return임</li>
<li>백트래킹으로 계혹 들어가는 것과 재귀를 활용함</li>
<li>그래서 값을 계속 저장하고 for은 현재 layer로 이동한것이니 시작은 layer로 잡음</li>
<li>그리고 재귀는 result_function에 i를 넣으면서 해당 루프에 전체를 다 수색하게 함</li>
</ul>
<p>추가적인것 2차원 resize하는거 ㄱemind</p>
<pre><code>int n
cin &gt;&gt;n;

vector &lt;vector&lt;int&gt;&gt; new;

new.reszie(n);

for(int = 0 ; i &lt; n;i++)
{
    new[i].resize(n);
}</code></pre><p>or</p>
<pre><code>vector &lt;vector&lt;int&gt;&gt; new(n, vector&lt;int&gt;(n));</code></pre><h2 id="code">Code</h2>
<pre><code>#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;vector&gt;

using namespace std;

vector&lt;vector&lt;int&gt;&gt; input_list;

int input_value;

int value_result[6];

void result_function(int layer,int count)
{
    if(count==6)
    {
        for(int i=0 ; i&lt;6 ; i++)
        {
            cout&lt;&lt;value_result[i]&lt;&lt;&quot; &quot;;
        }
        cout&lt;&lt;&quot;\n&quot;;
        return;
    }

    if(layer&gt;=input_value) return;

    for(int i=layer ; i&lt;input_value ; i++)
    {
        value_result[count]=input_list[0][i];
        result_function(i+1,count+1);
    }
}

int main()
{
    while(1)
    {
        cin&gt;&gt;input_value;

        if(input_value == 0)
        {
            return 0;
        }

        vector &lt;int&gt; row_value(input_value);

        for(int i = 0; i&lt;input_value ; i++)
        {
            cin&gt;&gt;row_value[i];
        }

        input_list.clear();
        input_list.push_back(row_value);
        result_function(0,0);
        cout&lt;&lt;&quot;\n&quot;;
    }


}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[10971_왼판원 순회2(포인터 배열공부)]]></title>
            <link>https://velog.io/@happy_lee0_0/10971%EC%99%BC%ED%8C%90%EC%9B%90-%EC%88%9C%ED%9A%8C2%ED%8F%AC%EC%9D%B8%ED%84%B0-%EB%B0%B0%EC%97%B4%EA%B3%B5%EB%B6%80</link>
            <guid>https://velog.io/@happy_lee0_0/10971%EC%99%BC%ED%8C%90%EC%9B%90-%EC%88%9C%ED%9A%8C2%ED%8F%AC%EC%9D%B8%ED%84%B0-%EB%B0%B0%EC%97%B4%EA%B3%B5%EB%B6%80</guid>
            <pubDate>Wed, 31 Dec 2025 01:48:45 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/44bee7bb-0f09-489e-b734-c5627bc79feb/image.png" alt="">
예제 입력 1 
4
0 10 15 20
5 0 9 10
6 13 0 12
8 8 9 0</p>
<p>예제 출력 1 
35</p>
<h2 id="study-code">Study code</h2>
<h3 id="part1">Part1</h3>
<ul>
<li>2차원 배열을 만들어야 하는데 vector와 포인터 중에 뭐를 쓸지 고민했음</li>
<li>그래서 오래간만에 포인터를 활용하여 2차원 배열을 만들고자 했음</li>
<li>하지만 gpt나 현업에서는 vector를 사용하는것이 일반적이라고 함</li>
<li>포인터를 쓰게 되면 이렇게 메모리를 만들어줘야함
a
│
├─ a[0] ──▶ int int int int
├─ a[1] ──▶ int int int int
├─ a[2] ──▶ int int int int
└─ a[3] ──▶ int int int int</li>
</ul>
<pre><code>int** a;

    a = new int*[input_num];

    for(int j = 0 ; j &lt; input_num;j++)
    {
        a[j] = new int[input_num];
    }   
</code></pre><p>그래서 이렇게 진행해야 함</p>
<ul>
<li>이렇게 한다음 그냥 2차원 배열 사용하듯이 평법하게 사용하면 됨</li>
<li>다 사용하고 나서 하단같이 메모리를 delete를 해야함</li>
<li>안그러면 메모리 누수 또는 런타임 오류가 나올수 있음</li>
</ul>
<pre><code>    for (int i = 0; i &lt; input_num; i++)
    {
    delete[] a[i];
    }
    delete[] a;</code></pre><ul>
<li>각 행마다의 delete와</li>
<li>모든 행의 delete를 진행하는것</li>
</ul>
<h3 id="part2">Part2</h3>
<ul>
<li>이번 문제를 풀면서 코드를 전체를 이해하기 보다는 흐름을 이해해야하는것으로 초점을 두었음</li>
<li>해당 문제에 대한 개념은 결과적으로 백트래킹으로 계속돌아가고 브루트포스로 숫자를 넣어서 비교하는 것으로 진행되어야 했음</li>
<li>그래서 내가 보았을때는 결론적으로 이렇다.</li>
</ul>
<ol>
<li>해당 계산 함수의 시작은 처음 목표값까지 도달으면 그 결과를 나오게 하는것
1-1. 하지만 결과가 나오게 되는 것까지 특정 부분에서 예외가 있는경우가 있으니 return을 하거나 loop 구분이 있을시 continue로 넘어가는것</li>
</ol>
<ol start="2">
<li>다음것은 결과적으로 모든 것의 코드 그리고 index가 있을것이다. 그것을 빠르게 봐야하는 것, 그리고 방문을 했는지 안했는지 판단하는것이 가장 중요하고 계속해서 반복해야하는 부분은 check() 이부분을 반복해야한다.
2-1. 그래서 일단 그 index가 방문했는지 안했는지 판단하여 loop에 if을 해서 있으면 continue로 해서 넘어가고 아니면 그 다음 스텝을 가는것으로 진행
2-2. 그렇게 다음으로 가는것은 앞서 말한대로 check을 불러오는것을 시작으로 그 값에 추가를 하고 계속 자기자신을 불러옴 그리고 그 밑에는 방문을 했으니 flase로 방문을 끝냄</li>
</ol>
<p>-&gt; 여기서 중요한게  check를 하기전에는 true인지 false인지 check을 하고 false면  true로 만들어주고 끝나면 false로 만들어줌</p>
<pre><code>#include &lt;iostream&gt;
#include &lt;algorithm&gt;
#include &lt;vector&gt;
#include &lt;string&gt;

using namespace std;

int input_num;

bool ch[11] = {false,};

int** a;

int output_result = 1e9;

void check(int start,int index,int cnt,int sum_num)
{
    if (sum_num &gt;= output_result) return; 

    if(cnt==input_num)
    {
    if(a[index][start]==0) return;

    output_result = min(output_result, sum_num + a[index][start]);
    return;
    }

    for(int i = 0;i&lt;input_num;i++)
    {
        if(a[index][i]==0||ch[i]) continue;
        ch[i]=true;
        check(start,i,cnt+1,sum_num+a[index][i]);
        ch[i]=false;
    }
}

int main()
{
    cin&gt;&gt;input_num;
    a = new int*[input_num];

    for(int j = 0 ; j &lt; input_num;j++)
    {
        a[j] = new int[input_num];
    }   

    for(int i = 0 ; i&lt; input_num;i++)
    {
        for(int j = 0 ; j &lt; input_num;j++)
        {
            cin&gt;&gt;a[i][j];
        }   
    }

    for (int i = 0; i &lt; input_num; i++)
    {
        ch[i] = true;
        check(i,i,1,0);
        ch[i] = false;
    }

    cout&lt;&lt;output_result;

    for (int i = 0; i &lt; input_num; i++)
    {
    delete[] a[i];
    }
    delete[] a;

    /*
    메모리 누수 또는 런타임 오류
    */

    return 0;
}


</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[10819_차이를 최대로]]></title>
            <link>https://velog.io/@happy_lee0_0/10819%EC%B0%A8%EC%9D%B4%EB%A5%BC-%EC%B5%9C%EB%8C%80%EB%A1%9C</link>
            <guid>https://velog.io/@happy_lee0_0/10819%EC%B0%A8%EC%9D%B4%EB%A5%BC-%EC%B5%9C%EB%8C%80%EB%A1%9C</guid>
            <pubDate>Mon, 22 Dec 2025 01:47:18 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/b16e2358-ddee-4671-8b4b-818110aff296/image.png" alt=""></p>
<p>예제 입력 1 
6
20 1 15 8 4 10</p>
<p>예제 출력 1 
62</p>
<h2 id="code-study">code study</h2>
<ul>
<li>저기 절대값이 있다는것을 순간적으로 몰라서 멍때리다가 절대값이라길래 아하 하고 풀었음</li>
<li>next_permutation을 활용하였고 저번과 같이 배열을 sort를 하여 순서대로 하고 수식을 넣어 하나하나 비교하면서 풀었음</li>
<li>sort(input_value.begin(),input_value.end()); 이방향으로 진행함</li>
<li>절댓값ㄷ이 abs라는것을 한번더 상기</li>
<li><pre><code>  for(int i = 0; i&lt;input_count-1;i++)
  {
     check_result+=abs(input_value[i]-input_value[i+1]);
  }</code></pre>이부분에서 어디까지라는것을 먼저 확인하고 들어감</li>
</ul>
<h2 id="code">code</h2>
<pre><code>#include &lt;iostream&gt;
#include &lt;algorithm&gt;
#include &lt;string&gt;
#include &lt;vector&gt;
using namespace std;

int input_count;

vector &lt;int&gt; input_value;

int output_result = 0;

int check_result;


void check_num()
{
    do
    {
    for(int i = 0; i&lt;input_count-1;i++)
    {
       check_result+=abs(input_value[i]-input_value[i+1]);
    }
    if(output_result &lt; check_result)
    {
        output_result=check_result;
    }
    check_result=0;
    }while(next_permutation(input_value.begin(),input_value.end()));

    cout&lt;&lt;output_result;
}

int main()
{
    cin&gt;&gt;input_count;

    input_value.resize(input_count);

    for(int i = 0; i&lt;input_count;i++)
    {
        cin&gt;&gt;input_value[i];
    }    

    sort(input_value.begin(),input_value.end());

    check_num();
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[10972_다음 순열]]></title>
            <link>https://velog.io/@happy_lee0_0/10972%EB%8B%A4%EC%9D%8C-%EC%88%9C%EC%97%B4</link>
            <guid>https://velog.io/@happy_lee0_0/10972%EB%8B%A4%EC%9D%8C-%EC%88%9C%EC%97%B4</guid>
            <pubDate>Fri, 19 Dec 2025 01:37:21 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/ba19c760-53fe-4999-a4ef-205e15f8d879/image.png" alt=""></p>
<p>예제 입력 1 
4
1 2 3 4
예제 출력 1 
1 2 4 3</p>
<p>예제 입력 2 
5
5 4 3 2 1
예제 출력 2 
-1</p>
<h2 id="공부내용">공부내용</h2>
<ul>
<li>next_permutation를 배웠는데 이는 순열의 다음것을 출력해주는것임</li>
<li>이는 algorithm을 활용하면 #include가 있어야함</li>
<li>prev_permutation를 활용하면 전꺼가 되니까 기억해라<pre><code>  if(next_permutation(get_num.begin(),get_num.end()))
  {
      for(int i = 0;i&lt;in_num;i++)
      {
          cout&lt;&lt;get_num[i]&lt;&lt;&#39; &#39;;
      }
  }</code></pre>이렇게 하면 다음께 출력이 되고</li>
</ul>
<pre><code>    do{ 
    for(int i = 0;i&lt;in_num;i++) 
    {
        cout&lt;&lt;get_num[i]&lt;&lt;&#39; &#39;;
    }
    }while(next_permutation(get_num.begin(),get_num.end()));```</code></pre><p>이렇게 하면 모든 순열이 출력이 된다</p>
<h2 id="code">code</h2>
<pre><code>#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;algorithm&gt;
#include &lt;vector&gt;

using namespace std;

int in_num;

vector &lt;int&gt; get_num; 


int main()
{
    cin&gt;&gt;in_num;

    get_num.resize(in_num);

    for(int i = 0;i&lt;in_num;i++)
    {
        cin&gt;&gt;get_num[i];
    }


    if(next_permutation(get_num.begin(),get_num.end()))
    {
        for(int i = 0;i&lt;in_num;i++)
        {
            cout&lt;&lt;get_num[i]&lt;&lt;&#39; &#39;;
        }
    }
    else
    {
        cout&lt;&lt;-1;
    }

    return 0;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[1248_guess(어렵구만)]]></title>
            <link>https://velog.io/@happy_lee0_0/1248guess%EC%96%B4%EB%A0%B5%EA%B5%AC%EB%A7%8C</link>
            <guid>https://velog.io/@happy_lee0_0/1248guess%EC%96%B4%EB%A0%B5%EA%B5%AC%EB%A7%8C</guid>
            <pubDate>Thu, 18 Dec 2025 01:26:27 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/fff04b78-41da-4e27-ae6a-889d4d6c95f6/image.png" alt=""></p>
<p>예제 입력 1 
4
-+0++++--+
예제 출력 1 
-2 5 -3 1</p>
<p>예제 입력 2 
2
+++
예제 출력 2 
3 4</p>
<p>예제 입력 3 
5
++0+-+-+--+-+--
예제 출력 3 
1 2 -3 4 -5</p>
<h2 id="공부">공부</h2>
<ul>
<li>해당 코드를 공부하면서 시간을 많이 쓴것 같음</li>
<li>허나재귀 파트의 마지막 문제라서 조금더 집중을 한것같음</li>
<li>해당부분에서 중요한것은 이 세가지라고 생각함</li>
</ul>
<ol>
<li>함수시작에 원하는 개수가 내가 input한 개수와 일치할 경우 그 최종결과를 출력하는것</li>
<li>return과 continue를 활용하여 넘어갈 것은 if을 통해 확인하고 바로 넘어가기 그리고 줄 넘버는 바로 주기</li>
<li>return을 수치를 return 하는것이 아닌 bool을 return함으로써 현재 그 수치가 이 안에 들어와도 되는지 확인하기</li>
</ol>
<p>이렇게 되는것 같은데 </p>
<p>즉 브루토포스로 수치를 다때려 박으면서 재귀를 통해 계속 자기자신을 반복하는것임
그리고 앞서 말할것 처럼 이 방법이 맞는지 확인하고 아니면 바로 돌아가는 것으로 다시 돌아가서 확인하는 백트래킹으로 진행하는것임</p>
<h2 id="코드">코드</h2>
<pre><code>#include &lt;iostream&gt;
#include &lt;algorithm&gt;
#include &lt;string&gt;
#include &lt;vector&gt;

using namespace std;

char input_num_index[10][10];

int count_num;

int result[10];

bool check_num(int count_void)
{
    int sum = 0;
    for(int i = count_void;i&gt;=0;i--)
    {
        sum = sum+result[i];
        if(input_num_index[i][count_void]==&#39;+&#39;&amp;&amp;sum&lt;=0)
        return false;
        if(input_num_index[i][count_void]==&#39;-&#39;&amp;&amp;sum&gt;=0)
        return false;
        if(input_num_index[i][count_void]==&#39;0&#39;&amp;&amp;sum!=0)
        return false;
    }
    return true;
}

void bfs(int count_void)
{
    if(count_void == count_num)
    {
        for(int i = 0 ; i&lt;count_void;i++)
        {
            cout&lt;&lt;result[i]&lt;&lt;&quot; &quot;;
        }
        exit(0);
    }

    for(int i = -10;i&lt;=10;i++)
    {
        if(input_num_index[count_void][count_void]==&#39;+&#39; &amp;&amp; i&lt;=0) continue;
        if(input_num_index[count_void][count_void]==&#39;-&#39;&amp;&amp; i&gt;=0) continue;
        if(input_num_index[count_void][count_void]==&#39;0&#39;&amp;&amp; i!=0) continue;

        result[count_void] = i; 

        if(check_num(count_void)==true)
        {
            bfs(count_void+1);
        }
    }
}

int main()
{
    int input_bo_index = 0;

    string input_bo;

    cin&gt;&gt;count_num;

    cin&gt;&gt;input_bo;

    for(int i = 0;i&lt;count_num;i++)
    {
        for(int j = i; j&lt;count_num;j++)
        {
            input_num_index[i][j]=input_bo[input_bo_index++];
        }
    }

    bfs(0);
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[2529_부등호]]></title>
            <link>https://velog.io/@happy_lee0_0/2529%EB%B6%80%EB%93%B1%ED%98%B8</link>
            <guid>https://velog.io/@happy_lee0_0/2529%EB%B6%80%EB%93%B1%ED%98%B8</guid>
            <pubDate>Thu, 11 Dec 2025 05:16:26 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/d9723d42-14f1-4907-bf4b-197e77e00604/image.png" alt=""></p>
<p>예제 입력 1 
2
&lt; &gt;</p>
<p>예제 출력 1 
897
021</p>
<p>예제 입력 2 
9</p>
<blockquote>
<p>&lt; &lt; &lt; &gt; &gt; &gt; &lt; &lt;</p>
</blockquote>
<p>예제 출력 2 
9567843012
1023765489</p>
<h2 id="공부노트">공부노트</h2>
<ul>
<li><p>bool check(char big_small,int f_num,int sec_num)<br>처음에는 해당 부분에서 bool check 하는 부분이 어떻게 체크를 해야하는지 몰랐음
그래서 보니까 bool로 return을 해야하는것으로 bool로 바꾸고 진짜 이 기호가 이 숫자가 들어가도 된다 안된다를 true false로 한다는 것에서 신선하게 이해개 되었음</p>
</li>
<li><p>results.push_back(num);
처음에 전체 코드가 완성되면 해당 답에 push를 하여 답을 넣어서 출력하는 식이었음</p>
</li>
<li><p>코드의 핵심 알고리즘</p>
<pre><code>  if(visit[i]) continue;

  else if(depth==0)
  {
      visit[i]=true;
      check_num(depth+1,num+to_string(i));
      visit[i]=false;
  }
  else
  {
      int change_int = num[depth-1]-&#39;0&#39;;
      if(check(equality[depth-1],change_int,i))
      {
      visit[i]=true;
      check_num(depth+1,num+to_string(i));
      visit[i]=false;
      }
  }</code></pre><p>이부분에서 </p>
</li>
</ul>
<h2 id="코드">코드</h2>
<pre><code>#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

using namespace std;

int input_value;

char equality[10];

bool visit[10];

vector&lt;string&gt; results;

bool check(char big_small,int f_num,int sec_num)
{
    if(big_small==&#39;&lt;&#39;)
    {
        return f_num &lt; sec_num;
    }
        if(big_small==&#39;&gt;&#39;)
    {
        return f_num &gt; sec_num;
    }
    return false;
} 

void check_num(int depth, string num)
{
    if(depth==input_value+1)
    {
        results.push_back(num);
        return;
    }

    for(int i = 0;i&lt;=9;i++)
    {
        if(visit[i]) continue;

        else if(depth==0)
        {
            visit[i]=true;
            check_num(depth+1,num+to_string(i));
            visit[i]=false;
        }
        else
        {
            int change_int = num[depth-1]-&#39;0&#39;;
            if(check(equality[depth-1],change_int,i))
            {
            visit[i]=true;
            check_num(depth+1,num+to_string(i));
            visit[i]=false;
            }
        }
    }
}

int main()
{   
    cin&gt;&gt;input_value;

    for(int i=0 ;i&lt;input_value;i++)
    {
        cin&gt;&gt;equality[i];
    }

    check_num(0,&quot;&quot;);

    sort(results.begin(),results.end());
    cout&lt;&lt;results.back()&lt;&lt;&quot;\n&quot;;

    cout&lt;&lt;results.front()&lt;&lt;&quot;\n&quot;;    

}


</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[14889,15661_스타트와 링크 및 링크와 스타트]]></title>
            <link>https://velog.io/@happy_lee0_0/1488915661%EC%8A%A4%ED%83%80%ED%8A%B8%EC%99%80-%EB%A7%81%ED%81%AC-%EB%B0%8F-%EB%A7%81%ED%81%AC%EC%99%80-%EC%8A%A4%ED%83%80%ED%8A%B8</link>
            <guid>https://velog.io/@happy_lee0_0/1488915661%EC%8A%A4%ED%83%80%ED%8A%B8%EC%99%80-%EB%A7%81%ED%81%AC-%EB%B0%8F-%EB%A7%81%ED%81%AC%EC%99%80-%EC%8A%A4%ED%83%80%ED%8A%B8</guid>
            <pubDate>Fri, 05 Dec 2025 01:52:19 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/eb737c02-2df1-43e8-8e2a-9eb422e5c87b/image.png" alt="">
예제 입력 1 
4
0 1 2 3
4 0 5 6
7 1 0 2
3 4 5 0
예제 출력 1 
0
예제 입력 2 
6
0 1 2 3 4 5
1 0 2 3 4 5
1 2 0 3 4 5
1 2 3 0 4 5
1 2 3 4 0 5
1 2 3 4 5 0
예제 출력 2 
2
예제 입력 3 
8
0 5 4 5 4 5 4 5
4 0 5 1 2 3 4 5
9 8 0 1 2 3 1 2
9 9 9 0 9 9 9 9
1 1 1 1 0 1 1 1
8 7 6 5 4 0 3 2
9 1 9 1 9 1 0 9
6 5 4 3 2 1 9 0
예제 출력 3 
1</p>
<h2 id="공부노트">공부노트</h2>
<ul>
<li>해당 개념에서 백트래킹을 배우면서 우리가 원하는 값이 안나오면 돌아간다라는 개념을 공부하게 되었음</li>
<li>14889의 문제 같은경우 상대팀과 내 팀의 인원이 같은경우를 생각해야 되어서 n/2를 하며 진행함</li>
<li>그렇게 나누어진 부분을 벡터에 init하고 다음 score값에다가 배열을 하나하나 넣는 방식으로 진행함</li>
<li>그리고 team 부분에 continue 즉 방문을 한 것 우리팀인 것일 경우 continue를 진행하여 다음 for로 가게 만들어 진행함</li>
</ul>
<h2 id="14889코드">14889코드</h2>
<pre><code>#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;vector&gt;
using namespace std;

int line;

int layer[20][20];

int lower_value = 500;

bool team[20];

void teamset(int mem, int fight_mem)
{
    vector&lt;int&gt; start;
    vector&lt;int&gt; link;

    int start_score = 0;

    int link_score = 0;

    if (fight_mem == line/2)
    {
        for(int i = 0 ; i &lt; line ; i++)
        {
            if(team[i]==true)
            {
                start.push_back(i);
            }
            else
            {
                link.push_back(i);
            }
        }

        for(int i = 0 ; i&lt;line/2 ; i++)
        {
            for(int j = 0 ; j&lt;line/2 ; j++)
            {
               start_score+=layer[start[i]][start[j]];
               link_score+=layer[link[i]][link[j]];
            }
        }

        lower_value = min(lower_value,(abs(start_score-link_score)));
        return;
    }

    for(int i = mem ; i &lt; line ; i++)
    {
        if(team[i])
        {
        continue;
        }
        else
        {
            team[i]=true;
            teamset(i,fight_mem+1);
            team[i]=false;
        }
    }
}

int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(NULL);

    cin&gt;&gt;line;

    for(int i = 0 ; i&lt;line;i++)
    {
        for(int j = 0 ; j&lt;line;j++)
        {
            cin&gt;&gt;layer[i][j];
        }
    }
    teamset(0,0);
    cout&lt;&lt;lower_value;
}</code></pre><p><img src="https://velog.velcdn.com/images/happy_lee0_0/post/dc3ec6c8-c1fc-493a-856e-953365e3f967/image.png" alt=""></p>
<p>예제 입력 1 
4
0 1 2 3
4 0 5 6
7 1 0 2
3 4 5 0
예제 출력 1 
0
예제 입력 2 
6
0 1 2 3 4 5
1 0 2 3 4 5
1 2 0 3 4 5
1 2 3 0 4 5
1 2 3 4 0 5
1 2 3 4 5 0
예제 출력 2 
2
예제 입력 3 
8
0 5 4 5 4 5 4 5
4 0 5 1 2 3 4 5
9 8 0 1 2 3 1 2
9 9 9 0 9 9 9 9
1 1 1 1 0 1 1 1
8 7 6 5 4 0 3 2
9 1 9 1 9 1 0 9
6 5 4 3 2 1 9 0
예제 출력 3 
1</p>
<h2 id="공부노트-1">공부노트</h2>
<ul>
<li>이번 문제는 전문제와 다르게 팀수가 제한이 없었음</li>
<li>그래서 일단 모든팀을 true로 만든다음(우리팀으로 만든다음) 내부에 한팀이라도 size가 0이면 return을 하여 다음 함수로 가게 만듬</li>
<li>그리고 link와 start팀은 수가 각각 다르다고 판단하여 size도 나누어 score 값을 추출하고자 함</li>
</ul>
<h2 id="15661코드">15661코드</h2>
<pre><code>#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;string&gt;
using namespace std;

int input_value[20][20];

int input_count;

int output_answer=50;

bool are_you[20];

void teamset(int count)
{
    vector&lt;int&gt; link;
    vector&lt;int&gt; start;

    int link_score = 0;

    int start_score = 0;


    if(count==input_count)
    {
        for(int i=0;i&lt;input_count;i++)
        {
            if(are_you[i]==true)
                link.push_back(i);
            else
                start.push_back(i);
        }

        if(link.size()==0 || start.size()==0)
        {
            return;
        }

        for(int i = 0;i&lt;link.size();i++)
            for(int j = 0;j&lt;link.size();j++)
            {
                link_score+=input_value[link[i]][link[j]];
            }

        for(int i = 0;i&lt;start.size();i++)
            for(int j = 0;j&lt;start.size();j++)
            {
                start_score+=input_value[start[i]][start[j]];
            }
            output_answer = min(output_answer,abs(link_score-start_score));
            return;
    }

    are_you[count]=true;

    teamset(count+1);

    are_you[count]=false;

    teamset(count+1);

}

int main()
{
    cin&gt;&gt;input_count;

    for(int i=0;i&lt;input_count;i++)
    {
        for(int j=0;j&lt;input_count;j++)
        {
            cin&gt;&gt;input_value[i][j];
        }
    }

    teamset(0);

    cout&lt;&lt;output_answer;

}</code></pre>]]></description>
        </item>
    </channel>
</rss>