<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dazi2_2.log</title>
        <link>https://velog.io/</link>
        <description>GoGoSing</description>
        <lastBuildDate>Thu, 07 Mar 2024 10:17:04 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. dazi2_2.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dazi2_2" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[OpenCV] OpenCV 기초 알아보기]]></title>
            <link>https://velog.io/@dazi2_2/OpenCV-OpenCV-%EA%B8%B0%EC%B4%88-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@dazi2_2/OpenCV-OpenCV-%EA%B8%B0%EC%B4%88-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Thu, 07 Mar 2024 10:17:04 GMT</pubDate>
            <description><![CDATA[<h1 id="0-개요">0. 개요</h1>
<p>앞서 OpenCV가 어떤방면에서 사용되는지 , 특징은 어떠한지 알아보았다. 직접 OpenCV를 활용하기 위해서 기초적인 사용법에 대해서 알아보자.</p>
<h1 id="1-도형그리기">1. 도형그리기</h1>
<p>OpenCV에서는 선, 사각형, 원, Text 와 같은 도형을 그리는 기능을 제공한다 아래의 사용법을 살펴보자.</p>
<h3 id="선line-그리기">선(line) 그리기</h3>
<pre><code class="language-python">cv2.line(img, start, end , color, thickness)</code></pre>
<h3 id="사각형rectangle-그리기">사각형(rectangle) 그리기</h3>
<pre><code class="language-python">cv2.rectangle(img, start, end , color, thickness)</code></pre>
<h3 id="원circle-그리기">원(circle) 그리기</h3>
<pre><code class="language-python">cv2.circle(img, center, radius, color, thickness)</code></pre>
<h3 id="텍스트text-그리기">텍스트(Text) 그리기</h3>
<pre><code class="language-python">cv2.text(img, text, org, font, fontScale, color)</code></pre>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/d37ac0bd-cbb9-4eab-a8e9-9ec8fce3a75a/image.png" alt=""></p>
<p>OpenCV를 통해 여러가지 도형 및 텍스트를 그린 모습.
color 같은경우에는 BGR 값으로 정의할 수 있다.</p>
<h1 id="2이미지-파일-다루기">2.이미지 파일 다루기</h1>
<p>컴퓨터 비전에서 이미지 파일을 다루는것은 당연한 일이기 때문에, 이미지 파일을 어떻게 다루는지 아는것은 필수적이다.</p>
<h3 id="이미지-파일-읽기imread">이미지 파일 읽기(imread)</h3>
<pre><code>cv2.imread(filename,flags)</code></pre><p>여기서 flags 에는 이미지를 읽을 때 적용되는 옵션을 지정하는 매개변수이다.
IMREAD_COLOR, IMREAD_GRAYSCALE, IMREAD_UNCHANGED 과 같은 옵션을 선택하여 이미지를 불러올 수 있다.</p>
<h3 id="화면에-이미지를-표시하기">화면에 이미지를 표시하기</h3>
<pre><code>cv2.imshow(window_title,image)</code></pre><p>window title 이름을 지정하고 image를 창으로 띄울 수 있다.</p>
<h3 id="이미지를-파일로-저장하기">이미지를 파일로 저장하기</h3>
<pre><code>cv2.imwrite(filename, image)</code></pre><p>filename으로 이미지를 저장 할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/72ed6396-35fd-4ccc-9edf-bee437641848/image.png" alt=""></p>
<p>IMREAD_COLOR로 생성한 그림.</p>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/bdb54149-1358-4b26-ae0c-38345831dfb4/image.png" alt=""></p>
<p>IMREAD_GRAYSCALE로 생성한 그림.</p>
<h3 id="특정-색상의-픽셀-찾기">특정 색상의 픽셀 찾기</h3>
<p>OpenCV 이미지는 픽셀로 구성되어 있고, 가로선 * 세로선 으로 읽을 수 있다. 아래의 예시코드를 통해 특정 픽셀을 찾는 방법을 알아보자.</p>
<pre><code class="language-python">import cv2
import numpy as np

#img = cv2.imread(&#39;src/ex_codes/opencv_ex/spot.png&#39;, cv2.IMREAD_GRAYSCALE)
img = cv2.imread(&#39;src/ex_codes/opencv_ex/grim.png&#39;, cv2.IMREAD_COLOR)

#height
h = img.shape[0]
w = img.shape[1]
print(&quot;The image dimension is %d x %d&quot; % (w, h))

for i in range(0, h - 1):
   for j in range(0, w - 1):
       if np.array_equal(img[i, j],[255,255,255]):
          print(img[i, j])
          print(i, j)

cv2.imshow(&#39;spot&#39;, img)

cv2.waitKey(1000)
</code></pre>
<p>OpenCV에서 이미지를 불러오는 방식이 height, width 좌표의 픽셀의 BGR 값들로 구성된 array로 읽어들인다. img.shape 를 통해 해당 값을 알 수 있다.
그렇기 때문에 특정 색깔을 찾고자 할때 해당 픽셀의 좌표와 원하는 BRG 값을 대조하면 원하는 색깔의 위치를 찾을 수 있다.
위 코드에서는 검정색을 찾을 수 있다.</p>
<p>그리고 <code>IMREAD_GRAYSCALE</code> 로 불러온 파일은 무채색이기 때문에 색상의 밝기로만 픽셀을 구분 할 수 있다. 그래서 픽셀에 저장되는 값이 0~255 사이의 값이다.</p>
<h3 id="roiregion-of-interest-설정하기">ROI(Region of Interest) 설정하기</h3>
<p>이미지를 처리할때 관심범위를 지정하여 작업을 할 수있다.</p>
<pre><code>cv2.imshow(window_title,image[a:b, c:d])</code></pre><p>잘라내는 범위는 당연히 원본이미지 값 보다 크면 안된다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/ca784f98-0ae2-4371-8b3a-f505e927fe86/image.png" alt=""></p>
<h1 id="3-hsv-색상-모델">3. HSV 색상 모델</h1>
<p>OpenCV에서는 BGR 색상 모델을 사용하기도 하지만, 때때로 HSV 라는 색상 표현 방식을 사용하기도 한다.
H(Hue, 색상) , S(Saturation, 채도), V(Value, 명도) 로 표현하며 , 객체를 분할해 내기 용이한 장점이 있다.
아래의 코드를 통해 BGR을 HSV로 변환 할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/50014396-4bad-440b-8695-5bfc74d5d70c/image.png" alt=""></p>
<pre><code>cv2.cvtColor(image, cv2.COLOR_BGR2HSV) </code></pre><p>Hue 범위 : 0 ~ 179 (색상스펙트럼 각도)
Saturation 범위 : 0 ~ 255 
Value 범위 : 0~255</p>
<h2 id="hsv-모델을-통한-차선-구분">HSV 모델을 통한 차선 구분</h2>
<p>도로의 색상은 주로 어두운색(검정색)이고, 차선은 밝은 색이기 때문에, 명도를 활용하면 구분해내기 쉽다.
아래의 코드와 입력하면 어떤 이미지에서 흰색으로 인식할 범위를 설정하여 inRange 함수를 통해 흰색 픽셀의 범위를 알아 낼 수 있다. </p>
<p>예시코드</p>
<pre><code class="language-python">import cv2
import numpy as np

img = cv2.imread(&#39;cars.png&#39;)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

lower_white = np.array([0,0,150])
upper_white = np.array([179,255,255])

mask = cv2.inRange(hsv, lower_white, upper_white)

cv2.imshow(&#39;line&#39;, mask)

cv2.waitKey(10000)
</code></pre>
<h4 id="normal-image">normal image</h4>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/6cde9c0f-7e39-4dd3-88fb-8fd1aca6e9ff/image.png" alt=""></p>
<h4 id="value-range-50255">value range 50~255</h4>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/82e80bfb-749e-4875-9884-beebb6b9b8c6/image.png" alt=""></p>
<h4 id="value-range-100255">value range 100~255</h4>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/aead98a5-c670-4afe-aa6c-072c18318c68/image.png" alt=""></p>
<h4 id="value-range-150255">value range 150~255</h4>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/3ffb60e3-f9bc-471d-bcad-1e48acfdd3ed/image.png" alt=""></p>
<p>위와 같이 image를 HSV로 변한 한 뒤 범위내의 색상을 흰색 영역으로 추출하여 이미지를 변환 한 것을 확인 할 수 있다.
value range 를 바꿔가면서 실행화면을 확인해보았을때 150~255 범위에서 차선을 잘 인식하는 것을 확인 할 수 있었다.
외부환경 및 상황을 고려하여 명도 값을 조절하여 차선을 인식하게 끔 바꾸는 것이 좋을 것 같다.</p>
<p>번외로 차선 외에 이미지의 특정 색상을 검출해내는 것도 시도해보았다.</p>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/7234b2d7-710e-4ea5-afab-5864b131d3e9/image.png" alt=""></p>
<p>응용하면 위와같이 파란부분의 영역만 골라서 확인 할 수도 있다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/ae403935-c430-4a00-8342-fcf00af4bc26/image.png" alt=""></p>
<pre><code>cv2.bitwise_and(img, img, mask=blue_mask)</code></pre><p>위 코드를 통해 원본이미지의 파란부분만 강조 표시 할수도 있다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/e2bf5edb-7f82-464a-9109-ec03f8c33b31/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/4e544b2b-2799-4c13-b747-dd10f4b0bd9e/image.png" alt=""></p>
<p>다른 이미지 예시.</p>
<h1 id="4-동영상-파일-다루기">4. 동영상 파일 다루기</h1>
<p>openCV에서 이미지 뿐만아니라 동영상 및 실시간 화면을 처리해야하는 경우가 많다.
아래에서 동영상 파일을 핸들링 하는방법에 대해서 알아보자.</p>
<p>video.py</p>
<pre><code class="language-python">import cv2

vid = cv2.VideoCapture(&#39;src/ex_codes/opencv_ex/small.avi&#39;)

while True:
    ret, frame = vid.read()
    if not ret:
        #once
        break
        #if want roof
        #vid.set(cv2.CAP_PROP_POS_FRAMES,0)
        #continue

    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    cv2.imshow(&#39;video&#39;, frame)

    if cv2.waitKey(1) &gt; 0:
        break

vid.release()

# cv2.waitKey(20000)
cv2.destroyAllWindows()
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OpenCV] OpenCV 알아보기]]></title>
            <link>https://velog.io/@dazi2_2/OpenCV-OpenCV-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@dazi2_2/OpenCV-OpenCV-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Tue, 05 Mar 2024 11:59:06 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dazi2_2/post/548ba384-e556-48ae-ad4d-d23e0985d524/image.png" alt=""></p>
<h1 id="😎1-개요">😎1. 개요</h1>
<p>OpenCV란 컴퓨터 비전 (Computer Vision)을 위해 만들어진 오픈소스로 이미지 및 비디오 처리를 위한 라이브러리이다.
컴퓨터 비전 및 머신러닝, 의료분야, 로봇 및 자율주행 차량, 인공지능, 딥러닝 등과 같이 다양한 방면에서 사용되고 있다.
예를들면 공장에서 카메라를 활용하여 어떠한 물품인지 식별하여 분류를 하기도 하고, 자율주행 자동차 방면에서는 차선을 인식하거나, 주변의 장애물이 어떤 물체인지, 앞선 차량을 인식하는 등과 같이 사용할 수 있다.</p>
<blockquote>
<p>공식 사이트 :  <a href="https://opencv.org/">https://opencv.org/</a>
OpenCV 문서 : <a href="https://docs.opencv.org/">https://docs.opencv.org/</a></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/1440109d-177e-45ed-b237-65650118896f/image.png" alt="">
위 이미지는 활용예시로 물체를 구분하는 기능을 가진다.</p>
<h1 id="💻2-특징">💻2. 특징</h1>
<h2 id="21-오픈소스">2.1 오픈소스</h2>
<p>OpenCV의 특징으로는 우선 오픈소스로 누구나 사용할 수 있다.(수익 창출 가능)
하지만, OpenCV를 사용했다는 것을 반드시 명시해야한다.</p>
<h2 id="22-다양한-플렛폼-및-언어-지원">2.2 다양한 플렛폼 및 언어 지원</h2>
<p>Window , macOS, Linux 과 같은 다양한 플렛폼에서 사용하여, 파이썬 , C++로 주로 사용하며, 그 밖의 언어 환경에서도 사용 할 수 있다.
내부적으로는 C/C++로 실행되기 때문에 수행속도가 빠른 장점을 가지고 있다.</p>
<h1 id="3-🚌기능">3. 🚌기능</h1>
<h2 id="31-이미지-및-비디오-처리">3.1 이미지 및 비디오 처리</h2>
<p>OpenCV는 이미지 및 비디오 데이터에 대한 다양한 처리 기능을 제공한다. 영상의 읽기, 쓰기, 변환, 필터링, 색상 공간 변환 등의 작업을 쉽게 수행할 수 있다.</p>
<h2 id="32-컴퓨터-비전-및-알고리즘-제공">3.2 컴퓨터 비전 및 알고리즘 제공</h2>
<p>OpenCV는 다양한 컴퓨터 비전 알고리즘을 제공한다. 특징점 검출, 객체 검출, 얼굴 인식, 이미지 매칭 등 다양한 작업을 수행할 수 있는 함수와 클래스를 포함하고 있다.</p>
<h1 id="4마치며">4.마치며</h1>
<p>이번에는 OpenCV에 대해서 가볍게 알아보는 시간을 가졌다.
아래의 영상을 보면 아두이노 및 OpenCV를 활용하여 손가락의 움직임을 인식하여 LED를 조작하는 것을 확인 할 수 있었다.
다음에는 활용하는 방법에 대해서 자세히 알아보도록 하자!
<a href="https://www.youtube.com/watch?v=hKbtfto9trw&amp;t=215s&amp;pp=ygUGb3BlbkNW">https://www.youtube.com/watch?v=hKbtfto9trw&amp;t=215s&amp;pp=ygUGb3BlbkNW</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Filter 코드 작성하고 그래프로 확인하기.]]></title>
            <link>https://velog.io/@dazi2_2/Filter-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1%ED%95%98%EA%B3%A0-%EA%B7%B8%EB%9E%98%ED%94%84%EB%A1%9C-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dazi2_2/Filter-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1%ED%95%98%EA%B3%A0-%EA%B7%B8%EB%9E%98%ED%94%84%EB%A1%9C-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 01 Feb 2024 13:00:47 GMT</pubDate>
            <description><![CDATA[<h1 id="👀0개요">👀0.개요</h1>
<p><del>## 💡0.1 데이터 만들기
두가지 데이터를 사용하였는데,
첫번째 데이터는 y = (x-10)(x-20)
두번째 데이터는 y = (x-10)(x-20)(x-30)
데이터를 x범위 0</del>50 으로 세팅하여 csv파일을 각각 만들었다.
아래의 코드를 통해 쉽게 csv파일을 얻을 수 있었다.~~</p>
<p>알고보니 CSV 파일은 주어져 있었다.. 그냥 데이터를 좀더 다뤄본걸로 생각하자 ..</p>
<p><del>🐍makedata.py</del></p>
<pre><code class="language-python">#!/usr/bin/env python3

import pandas as pd
import numpy as np

a = 1
b = -60
c = 1100
d = -6000

x_values = np.arange(0, 50, 1)

y_values = a * x_values**3 + b * x_values**2 + c* x_values + d

df = pd.DataFrame({&#39;time&#39;: x_values, &#39;y_measure&#39;: y_values})

df.to_csv(&#39;3rd_degree_equation_data_2.csv&#39;, index=False)
</code></pre>
<h1 id="📁1average-filter">📁1.Average Filter</h1>
<p>예측 결과값을 이전 결과의 평균값을 반영하여 추정값을 계산한다.</p>
<h2 id="🐍11-파이썬-코드">🐍1.1 파이썬 코드</h2>
<p>🐍average_filter.py</p>
<pre><code class="language-python">#!/usr/bin/env python3

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

#input arraylike
class AverageFilter:
    def __init__(self, y_initial_measure):
        self.df = pd.DataFrame()
        self.df[&#39;initial&#39;] = y_initial_measure

    def estimate(self):
        self.y_estimate = []
        initArray = self.df[&#39;initial&#39;].values
        print(len(initArray))
        for i in range(len(initArray)):
            if (i==0) :
                self.y_estimate.append(initArray[i])
            else:
                value = round(self.y_estimate[i-1]*(i)/(i+1) + initArray[i]/(i+1),4)
                self.y_estimate.append(value)

        self.df[&#39;estimate&#39;] =np.array(self.y_estimate)

    def getAverageValue(self):
        return self.df[&#39;estimate&#39;].values

if __name__ == &quot;__main__&quot;:
    #read csv file as date frame
    #signal = pd.read_csv(&quot;filters/data/example_Filter_1.csv&quot;)
    signal = pd.read_csv(&quot;filters/data/example_Filter_2.csv&quot;)

    #set values
    timeValues = signal[&#39;time&#39;].values
    measureValues = signal[&#39;y_measure&#39;].values

    #estimate value
    y_estimate = AverageFilter(measureValues)
    y_estimate.estimate()

    plt.figure()

    # use date for floating
    plt.plot(timeValues, measureValues,&#39;k.&#39;, label = &quot;Measure&quot;)
    plt.plot(timeValues, y_estimate.getAverageValue(),&#39;r-&#39;,label = &quot;Estimate&quot;)
    #set label
    plt.xlabel(&#39;time (s)&#39;)
    plt.ylabel(&#39;signal&#39;)

    #settings
    plt.legend(loc=&quot;best&quot;)
    #plt.axis(&quot;equal&quot;)
    plt.grid(True)
    plt.show()
</code></pre>
<h2 id="📊12-차트-화면">📊1.2 차트 화면</h2>
<p>📊first_data chart</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/d93e8237-18d4-4c21-b099-30f741f45f87/image.png" alt=""></p>
<p>📊second_data chart</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/750f5a35-739a-4c46-a036-ad22819af18f/image.png" alt=""></p>
<p>실행결과를 보면 첫번째 데이터의 경우 추정치가 0에 거의 수렴하는 모습이 보였고, 
두번째 데이터와 같이 어떤 양상이 있는 데이터에서는 뒤늦게 Data를 따라가는 모습을 보였다.
번외로 아래와 같이 sin(x)파를 한번 확인해보았다.</p>
<p>📊sin_data chart</p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/4ef4fec3-bb3d-47be-a8da-7d8f7dce69fa/image.png" alt=""></p>
</blockquote>
<p>그림과 같이 음수값에서도 추정값은 양수 값을 추정하는 모습을 보인다.
갈수록 추정값이 0에 수렴하는 걸로 보아 sin 파동이 무한정 반복하면 추정값은 한없이 0에 가까워 진다고 추정이된다.</p>
<h2 id="🤔13-고찰">🤔1.3 고찰</h2>
<p>Average filter의 확인결과 속도가 너무 느리고 최신데이터를 반영하는 힘이 약해서 굳이 사용하지는 않을 것 같다.
사용을 한다면 일정한 데이터를 수신 받는 도중 큰 노이즈가 가끔 발생하는 경우에 사용할 수 있을것 같다.</p>
<h1 id="📁2moving-average-filter">📁2.Moving Average Filter</h1>
<p>평균값의 구간을 정해서 추정치를 정하는 필터.</p>
<h2 id="🐍21-파이썬-코드">🐍2.1 파이썬 코드</h2>
<p>🐍moving_average_filter.py</p>
<pre><code class="language-python">#!/usr/bin/env python3

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

class MovingAverageFilter:
    def __init__(self, y_initial_measure, num_average=2.0):
        self.df = pd.DataFrame()
        self.df[&#39;initial&#39;] = y_initial_measure
        self.mv = num_average

    def estimate(self):
        self.y_estimate = []
        initArray = self.df[&#39;initial&#39;].values
        n = self.mv
        sum = 0
        for i in range(len(initArray)):
            if (i == 0 ) :
                value = initArray[i]
                sum+= value
            elif(i &lt; n):
                sum += initArray[i]
                value = sum/(i+1)
            else:
                value = self.y_estimate[i-1] + (initArray[i]- initArray[i-n])/n

            self.y_estimate.append(value)

        self.df[&#39;estimate&#39;] =np.array(self.y_estimate)

    def getMovingAverageValue(self):
        return self.df[&#39;estimate&#39;].values

if __name__ == &quot;__main__&quot;:
    #signal = pd.read_csv(&quot;filters/data/example_Filter_1.csv&quot;)
    #signal = pd.read_csv(&quot;filters/data/example_Filter_2.csv&quot;)
    signal = pd.read_csv(&quot;filters/data/sin_function_data.csv&quot;)

    #set values
    timeValues = signal[&#39;time&#39;].values
    measureValues = signal[&#39;y_measure&#39;].values

    #estimate value
    y_estimate = MovingAverageFilter(measureValues,5)
    y_estimate.estimate()

    plt.figure()
    plt.plot(timeValues, measureValues,&#39;k.&#39;,label = &quot;Measure&quot;)
    plt.plot(timeValues, y_estimate.getMovingAverageValue(),&#39;r-&#39;,label = &quot;Estimate&quot;)

    plt.xlabel(&#39;time (s)&#39;)
    plt.ylabel(&#39;signal&#39;)

    plt.legend(loc=&quot;best&quot;)
    #plt.axis(&quot;equal&quot;)
    plt.grid(True)
    plt.show()
</code></pre>
<h2 id="📊22-차트-화면">📊2.2 차트 화면</h2>
<h3 id="221-mv--5-일-때">2.2.1 MV = 5 일 때</h3>
<p>📊first_data chart</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/6bb1e614-40c3-4d0e-92da-25dda98a34d1/image.png" alt=""></p>
<p>📊second_data chart</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/5cb7fd33-c2f6-40c3-b594-97fb0478c4dc/image.png" alt=""></p>
<p>📊sin_data chart</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/e0e2e5ab-1463-41de-8de1-ff69dec1c057/image.png" alt=""></p>
<h3 id="221-mv--10-일-때">2.2.1 MV = 10 일 때</h3>
<p>📊first_data chart</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/a0c2920c-03af-4744-b0da-c86e636b17d9/image.png" alt=""></p>
<p>📊second_data chart</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/3ab77fd0-3993-4f8c-9ff7-55fdfb02c0ef/image.png" alt=""></p>
<p>📊sin_data chart</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/e9090738-dbe9-48a1-b535-b00a9e424928/image.png" alt=""></p>
<h2 id="🤔23-고찰">🤔2.3 고찰</h2>
<p>Average Filter 보다 새로운 데이터를 받아들이는 수치가 높은 것으로 보인다.
최신의 데이터를 훨씬 더 빠르게 반영하는 것을 확인 할 수 있었다.
그리고 mv 의 수가 높을수록 데이터를 반영하는 속도가 느리고, 진동폭이 좁아진다.
sin그래프와 같이 진동하는 그래프에서 mv가 높을수록 0에 수렴하는 것을 확인하였다.</p>
<h1 id="📁3low-pass-filter">📁3.Low pass filter</h1>
<p>저주파 통과 필터로 최근의 값에 가중치를 높게 줘서 최신데이터의 영향을 키우는 필터이다.</p>
<h2 id="🐍31-파이썬-코드">🐍3.1 파이썬 코드</h2>
<p>🐍LowPassFilter.py</p>
<pre><code class="language-python">#!/usr/bin/env python3

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

class LowPassFilter:
    def __init__(self, y_initial_measure, alpha=0.9):
        self.df = pd.DataFrame()
        self.alpha = alpha
        self.df[&#39;initial&#39;] = y_initial_measure

    def estimate(self):
        self.y_estimate = []
        initArray = self.df[&#39;initial&#39;].values
        print(len(initArray))
        for i in range(len(initArray)):
            if (i==0) :
                self.y_estimate.append(initArray[i])
            else:
                value = self.y_estimate[i-1]*self.alpha + initArray[i]*(1-self.alpha)
                self.y_estimate.append(value)

        self.df[&#39;estimate&#39;] =np.array(self.y_estimate)

    def getLowPassValue(self):
        return self.df[&#39;estimate&#39;].values


if __name__ == &quot;__main__&quot;:
    #read csv file as date frame
    #signal = pd.read_csv(&quot;filters/data/example_Filter_1.csv&quot;)
    signal = pd.read_csv(&quot;filters/data/example_Filter_2.csv&quot;)
    #signal = pd.read_csv(&quot;filters/data/sin_function_data.csv&quot;)

    #set values
    timeValues = signal[&#39;time&#39;].values
    measureValues = signal[&#39;y_measure&#39;].values

    #estimate value
    y_estimate = LowPassFilter(measureValues)
    y_estimate.estimate()


    plt.figure()
    plt.plot(timeValues, measureValues,&#39;k.&#39;, label = &quot;Measure&quot;)
    plt.plot(timeValues, y_estimate.getLowPassValue(),&#39;r-&#39;,label = &quot;Estimate&quot;)
    plt.xlabel(&#39;time (s)&#39;)
    plt.ylabel(&#39;signal&#39;)
    plt.legend(loc=&quot;best&quot;)
    #plt.axis(&quot;equal&quot;)
    plt.grid(True)
    plt.show()</code></pre>
<h2 id="📊32-차트-화면">📊3.2 차트 화면</h2>
<h3 id="321-alpha-02">3.2.1 alpha =0.2</h3>
<p>📊first_data chart</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/52146aa6-d948-41b7-ad4e-7f7b020cbcc1/image.png" alt=""></p>
<p>📊second_data chart</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/985b52c9-5e8e-4930-8fa8-bb9c7b9582dd/image.png" alt=""></p>
<p>📊sin_data chart</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/e0bdaaed-fcf5-4983-ad60-819eb29d2bfb/image.png" alt=""></p>
<h3 id="321-alpha-09">3.2.1 alpha =0.9</h3>
<p>📊first_data chart</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/c75f44e3-c8c9-4a70-b985-98a497738035/image.png" alt=""></p>
<p>📊second_data chart</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/9f35183a-41e3-4d3d-be3c-6e40e5aaaa9c/image.png" alt=""></p>
<p>📊sin_data chart</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/764d1c1c-a946-433d-94ca-369a4db0b343/image.png" alt=""></p>
<h2 id="🤔33-고찰">🤔3.3 고찰</h2>
<p>1,2차 데이터의 alpha =0.9 인 경우 MovingAverage 와 흡사한 모습을 보였다.
alpha 값이 낮을 수록 최신데이터를 많이 반영하기 때문에 최신데이터와 거의 흡사한 것을 볼 수 있었다.
sin과 같이 진동하는 그래프에서 명확한 차이를 볼수 있다.</p>
<h1 id="4-kalman-filter">4. Kalman Filter</h1>
<h1 id="5-comparing-filter">5. Comparing Filter</h1>
<h1 id="6-tuning-kalman-filter">6. Tuning Kalman Filter</h1>
<h1 id="7-kalman-filterstate-space-equation1">7. Kalman Filter(State Space Equation,1)</h1>
<h1 id="8-kalman-filterstate-space-equation2">8. Kalman Filter(State Space Equation,2)</h1>
<p>To be Continue</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[PID Controller 알아보기]]></title>
            <link>https://velog.io/@dazi2_2/PID-Controller-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@dazi2_2/PID-Controller-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Thu, 01 Feb 2024 12:59:57 GMT</pubDate>
            <description><![CDATA[<h1 id="👀0개요">👀0.개요</h1>
<p>이 세상에 존재하는 로봇을 컨트롤 하기 위해서는 컨트롤러가 필요하다.
로봇이 어떤 좌표에 도달하고자 할 때, 어떻게 제어해야 원하는 좌표를 찾아 갈 수 있을까?
이번시간에는 여러가지 컨트롤러들을 통해 로봇을 컨트롤 하는 값을 제어하여 원하는 좌표로 도달할 수 있게끔 하는 방법에 대해서 
알아볼 예정이다.</p>
<h1 id="💡1p-controller">💡1.P Controller</h1>
<p>우선 P Controller 에 대해서 간략하게 설명하면,
만약 로봇의 현재 위치와 목적지의 위치의 크기에 따라 제어하는 값을 변경하는 기법이다.
아래에서 파이썬 코드를 작성하고 그래프를 확인하여 해당 컨트롤러의 동작 및 단점에 대해 알아보자.</p>
<h2 id="🐍11-파이썬코드">🐍1.1 파이썬코드</h2>
<p>🐍P_controller.py</p>
<pre><code class="language-python">#!/usr/bin/env python3

from vehicle_model import VehicleModel
import numpy as np
import matplotlib.pyplot as plt

class P_Controller(object):
    def __init__(self,P_Gain= 0.3):
        self.target_y = target_y
        self.P_Gain = P_Gain
    def ControllerInput(self, reference, measure):
        self.target_y = reference
        self.y_measure = measure
        self.error = target_y-measure
        self.u = self.P_Gain*(self.error)

if __name__ == &quot;__main__&quot;:
    target_y = 0.0
    measure_y =[]

    time = []
    step_time = 0.1
    simulation_time = 30

    #vehicle
    plant = VehicleModel(step_time, 0.0, 0.99, 0.05) # R, force_ratio, force_bias

    controller = P_Controller()

    # 시뮬레이션
    for i in range(int(simulation_time/step_time)):
        #시간기록
        time.append(step_time*i)
        #y 좌표 기록
        measure_y.append(plant.y_measure[0][0])
        #u 값 측정
        controller.ControllerInput(target_y, plant.y_measure[0][0])
        plant.ControlInput(controller.u)

    #plt line
    plt.figure()
    plt.plot([0, time[-1]], [target_y, target_y], &#39;k-&#39;, label=&quot;reference&quot;)
    plt.plot(time, measure_y,&#39;r-&#39;,label = &quot;Vehicle Position&quot;)
    plt.xlabel(&#39;time (s)&#39;)
    plt.ylabel(&#39;signal&#39;)
    plt.legend(loc=&quot;best&quot;)
    plt.axis(&quot;equal&quot;)
    plt.grid(True)
    plt.show()
</code></pre>
<h2 id="📊12-실행화면">📊1.2 실행화면</h2>
<h3 id="121-gain--03">1.2.1 Gain = 0.3</h3>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/cfdfd0b1-5622-4191-a40c-be149dc4a711/image.png" alt=""></p>
<h3 id="122-gain--05">1.2.2 Gain = 0.5</h3>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/69e0b451-4060-4052-995b-974d0af1c762/image.png" alt=""></p>
<h3 id="123-gain--10">1.2.3 Gain = 1.0</h3>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/8701ab86-f460-48dd-8fc5-f3654e9c18bd/image.png" alt=""></p>
<h2 id="🚀13-controller-평가">🚀1.3 Controller 평가</h2>
<p>위 실행화면과 같이 로봇은 reference에 도달하기 힘든 것 같다.
그 이유는 컨트롤러가 제어 값을 변경하긴 하지만 레퍼런스에 도달하기 직전에 가장 속도가 빠르기 때문이다.(에러의 적분 값에 비례하는 속도)
이러한 현상을 overshot 이 발생한다고 한다.
reference 에 도달하기 위해서는 overshot을 줄일 필요가 있을 것 같다.
그리고 Gain 값이 높을 수록 reference에 도달 하는 시간은 빠르지만, 에러가 점점 커지는 것을 볼 수 있었다.</p>
<h1 id="💡2-pd-controller">💡2. PD Controller</h1>
<p>앞서 알아본 컨트롤러에 D가 추가된 컨트롤러이다.
제어값에 미분값을 추가하여 좀더 reference에 도달할 수 있게 도와준다.
아래코드와 실행화면을 살펴보자.</p>
<h2 id="🐍21-파이썬코드">🐍2.1 파이썬코드</h2>
<p>🐍PD_controller.py</p>
<pre><code class="language-python">#!/usr/bin/env python3

from vehicle_model import VehicleModel
import numpy as np
import matplotlib.pyplot as plt

class PD_Controller(object):
    def __init__(self, reference, measure, step_time, P_Gain=0.6, D_Gain=1.2):
        self.target_y = reference
        self.y_measure = measure
        self.step_time = step_time
        self.error = reference-measure

        self.P_Gain = P_Gain
        self.D_Gain = D_Gain
    def ControllerInput(self, reference, measure):
        self.target_y = reference
        self.y_measure = measure
        #errorGap
        self.errorGap = (reference-measure) - self.error
        #update error
        self.error = reference-measure

        self.p_controller = self.P_Gain * self.error
        self.d_controller = self.D_Gain * self.errorGap
        self.u = self.p_controller + self.d_controller


if __name__ == &quot;__main__&quot;:
    target_y = 0.0
    measure_y =[]
    time = []
    step_time = 0.1
    simulation_time = 30
    plant = VehicleModel(step_time, 0.0, 0.99, 0.1)
    controller = PD_Controller(target_y, plant.y_measure[0][0], step_time)

    for i in range(int(simulation_time/step_time)):
        time.append(step_time*i)
        measure_y.append(plant.y_measure[0][0])
        controller.ControllerInput(target_y, plant.y_measure[0][0])
        plant.ControlInput(controller.u)

    plt.figure()
    plt.plot([0, time[-1]], [target_y, target_y], &#39;k-&#39;, label=&quot;reference&quot;)
    plt.plot(time, measure_y,&#39;r-&#39;,label = &quot;Vehicle Position&quot;)
    plt.xlabel(&#39;time (s)&#39;)
    plt.ylabel(&#39;signal&#39;)
    plt.legend(loc=&quot;best&quot;)
    plt.axis(&quot;equal&quot;)
    plt.grid(True)
    plt.show()</code></pre>
<h2 id="📊22-실행화면">📊2.2 실행화면</h2>
<h3 id="221-d_gain--12">2.2.1 D_Gain = 1.2</h3>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/d1eec503-f3d5-47d8-bb2e-e3a7f4f34278/image.png" alt=""></p>
<h3 id="222-gain--100">2.2.2 Gain = 10.0</h3>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/231657d9-06e9-49d5-a26f-03a3e548a4b9/image.png" alt=""></p>
<h2 id="🚀23-controller-평가">🚀2.3 Controller 평가</h2>
<p>D컨트롤러를 추가하니 D_gain 값이 낮을때는 P Controller 와 비슷한 결과를 보였지만, D_gain 값을 10으로 설정하니 reference에 훨씬 가까워 지는 것을 확인 할 수 있었다.
하지만 reference와 일치되는 모습은 확인 할 수 없었는데,
여기에 I control을 추가하면 어떤 모습을 보일까?</p>
<h1 id="💡3pid-controller">💡3.PID Controller</h1>
<p>I(적분) 이 추가된 컨트롤러.
에러의 값들을 적분하여 값을 제어한다.
코드에서는 error_sum 값을 추가하여 I_Gain 값에 곱하였다.
코드를 작성해보고, 실행화면을 보자.</p>
<h2 id="🐍31-파이썬코드">🐍3.1 파이썬코드</h2>
<p>🐍PID_controller.py</p>
<pre><code>#!/usr/bin/env python3

from vehicle_model import VehicleModel
import numpy as np
import matplotlib.pyplot as plt

class PID_Controller(object):
    def __init__(self, reference, measure, step_time, P_Gain=10.0, D_Gain=50.0, I_Gain=0.02):
        self.target_y = reference
        self.y_measure = measure
        self.step_time = step_time
        self.error = reference-measure
        self.errorSum = 0
        self.P_Gain = P_Gain
        self.D_Gain = D_Gain
        self.I_Gain = I_Gain

    def ControllerInput(self, reference, measure):
        self.target_y = reference
        self.y_measure = measure

        #errorGap for D
        self.errorGap = (reference-measure) - self.error

        #update error
        self.error = reference-measure

        #integral for I
        self.errorSum+= self.error
        #print(self.errorSum)
        self.p_controller = self.P_Gain * self.error
        self.d_controller = self.D_Gain * self.errorGap
        self.i_controller = self.I_Gain * self.errorSum

        self.u = self.p_controller + self.d_controller +self.i_controller

if __name__ == &quot;__main__&quot;:
    target_y = 0.0
    measure_y =[]
    time = []
    step_time = 0.1
    simulation_time = 30
    plant = VehicleModel(step_time, 0.0, 0.99, 0.05)
    controller = PID_Controller(target_y, plant.y_measure[0][0], step_time)

    for i in range(int(simulation_time/step_time)):
        time.append(step_time*i)
        measure_y.append(plant.y_measure[0][0])
        controller.ControllerInput(target_y, plant.y_measure[0][0])
        plant.ControlInput(controller.u)

    plt.figure()
    plt.plot([0, time[-1]], [target_y, target_y], &#39;k-&#39;, label=&quot;reference&quot;)
    plt.plot(time, measure_y,&#39;r-&#39;,label = &quot;Vehicle Position&quot;)
    plt.xlabel(&#39;time (s)&#39;)
    plt.ylabel(&#39;signal&#39;)
    plt.legend(loc=&quot;best&quot;)
    #plt.axis(&quot;equal&quot;)
    plt.grid(True)
    plt.show()
</code></pre><h2 id="📊32-실행화면">📊3.2 실행화면</h2>
<h3 id="321-p_gain05-d_gain10-i_gain002">3.2.1 P_Gain=0.5, D_Gain=1.0, I_Gain=0.02</h3>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/3f8e112d-e8f0-4300-825f-29d4cf581c51/image.png" alt=""></p>
<p>초기 Gain 설정으로는 reference 값에 다가가기 힘들었다.</p>
<h3 id="322-p_gain100-d_gain500-i_gain002">3.2.2 P_Gain=10.0, D_Gain=50.0, I_Gain=0.02</h3>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/9233c1fc-bc2b-4247-a66d-47905aadee23/image.png" alt=""></p>
<h2 id="💡33-controller-평가">💡3.3 Controller 평가</h2>
<p>초기 설정으로 reference에 근접하기 쉽지가 않아 많은 튜닝을 거쳐 두번째 사진과 같이 거의 근접하는 것을 볼 수 있었다.
초기 화면과 같이 Gain 값을 튜닝하지 않으면 쉽게 맞출 수 없다는 것을 알 수 있었다.</p>
<h1 id="🤔4-pid-controller-돌아보기">🤔4. PID Controller 돌아보기</h1>
<p>세 가지 컨트롤러를 알아보았다. 각 컨트롤러의 특징 및 장,단점을 정리해보자.</p>
<blockquote>
<p><code>P Controller</code> : 에러의 크기에 비례(P)한 컨트롤 값을 전달한다. 제어를 시작 하였을때 , 즉 에러가 가장 클때 많은 영향을 끼칠 수 있다. 미래를 보고 제어를 한다고 하기도 한다.
#장점 : 빠르게 원하는 위치로 이동할 수 있다.
#단점 : 단일로 사용 시 reference 근처에서 진동하는 모습을 보인다.</p>
</blockquote>
<blockquote>
<p><code>D Controller</code> : 에러의 변화량,즉 에러를 미분한 값에 따른 제어를 제공한다. 현재를 제어한다고 한다.
#장점 : P Controller만을 사용했을때의 Overshot을 막을 수 있다.
#단점 : 에러의 변화량에 반응하기 때문에 정확한 목적지에 도달할 수 없는 점.</p>
</blockquote>
<blockquote>
<p><code>I Controller</code> : 에러의 총합,즉 에러의 적분에 따른 제어를 제공한다. 과거를 보고 제어한다고 한다.
#장점 : PD Controller만을 사용했을때의 reference에 도달 할 수 없는 문제를 해결 할 수 있다.
#단점 : 아래의 사진과 같이 단일로 사용하였을 때 오류가 걷잡을 수 없게 커지는 점이 있다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/c164df8b-c888-4332-a971-0445e7ac2ff2/image.png" alt=""></p>
</blockquote>
<h1 id="💡5pid-controller-튜닝">💡5.PID Controller 튜닝</h1>
<p>PID 각각의 gain 값을 적절히 조절하였다.</p>
<h2 id="🐍51-파이썬-코드">🐍5.1 파이썬 코드</h2>
<p>🐍Tuning_PID_Controller.py</p>
<pre><code class="language-python">#!/usr/bin/env python3

from vehicle_model import VehicleModel
import numpy as np
import matplotlib.pyplot as plt

class PID_Controller(object):
    def __init__(self, reference, measure, step_time, P_Gain=10.0, D_Gain=50.0, I_Gain=0.02):
        self.target_y = reference
        self.y_measure = measure
        self.step_time = step_time
        self.error = reference-measure
        self.errorSum = 0
        self.P_Gain = P_Gain
        self.D_Gain = D_Gain
        self.I_Gain = I_Gain

    def ControllerInput(self, reference, measure):
        self.target_y = reference
        self.y_measure = measure

        #errorGap for D
        self.errorGap = (reference-measure) - self.error

        #update error
        self.error = reference-measure

        #integral for I
        self.errorSum+= self.error
        #print(self.errorSum)
        self.p_controller = self.P_Gain * self.error
        self.d_controller = self.D_Gain * self.errorGap
        self.i_controller = self.I_Gain * self.errorSum

        self.u = self.p_controller + self.d_controller +self.i_controller

if __name__ == &quot;__main__&quot;:
    target_y = 0.0
    measure_y =[]
    time = []
    step_time = 0.1
    simulation_time = 30
    plant = VehicleModel(step_time, 0.0, 0.99, 0.05)
    controller = PID_Controller(target_y, plant.y_measure[0][0], step_time)

    for i in range(int(simulation_time/step_time)):
        time.append(step_time*i)
        measure_y.append(plant.y_measure[0][0])
        controller.ControllerInput(target_y, plant.y_measure[0][0])
        plant.ControlInput(controller.u)

    plt.figure()
    plt.plot([0, time[-1]], [target_y, target_y], &#39;k-&#39;, label=&quot;reference&quot;)
    plt.plot(time, measure_y,&#39;r-&#39;,label = &quot;Vehicle Position&quot;)
    plt.xlabel(&#39;time (s)&#39;)
    plt.ylabel(&#39;signal&#39;)
    plt.legend(loc=&quot;best&quot;)
    #plt.axis(&quot;equal&quot;)
    plt.grid(True)
    plt.show()
</code></pre>
<h2 id="52-실행화면">5.2 실행화면</h2>
<blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/555246e1-f6c2-4618-96d3-2df22534b307/image.png" alt=""></p>
</blockquote>
<h1 id="6pid-controller-with-state-estimator--low-pass-filter">6.PID Controller with State Estimator : Low Pass Filter</h1>
<h2 id="🐍61-파이썬-코드">🐍6.1 파이썬 코드</h2>
<p>🐍PID_Controller_StateEstimator_LPF.py</p>
<pre><code>#!/usr/bin/env python3

from vehicle_model import VehicleModel
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

class PID_Controller(object):
    def __init__(self, reference, measure, step_time, P_Gain=10.0, D_Gain=50.0, I_Gain=0.02):
        self.target_y = reference
        self.y_measure = measure
        self.step_time = step_time
        self.error = reference-measure
        self.errorSum = 0
        self.P_Gain = P_Gain
        self.D_Gain = D_Gain
        self.I_Gain = I_Gain

    def ControllerInput(self, reference, measure):
        self.target_y = reference
        self.y_measure = measure

        #errorGap for D
        self.errorGap = (reference-measure) - self.error

        #update error
        self.error = reference-measure

        #integral for I
        self.errorSum+= self.error
        #print(self.errorSum)
        self.p_controller = self.P_Gain * self.error
        self.d_controller = self.D_Gain * self.errorGap
        self.i_controller = self.I_Gain * self.errorSum

        self.u = self.p_controller + self.d_controller +self.i_controller

class LowPassFilter:
    def __init__(self, y_initial_measure, alpha=0.1):
        self.y_initial_measure = y_initial_measure
        self.y_estimate = y_initial_measure
        self.i = 0.0
        self.alpha = alpha

    def estimate(self, y_measure):
        if self.i == 0.0:
            value = self.y_initial_measure
            self.i+=1
        else:
            value = self.y_initial_measure * self.alpha + y_measure*(1.0-self.alpha)
        self.y_estimate = value
        return value


if __name__ == &quot;__main__&quot;:
    target_y = 0.0
    measure_y =[]
    estimated_y = []

    time = []
    step_time = 0.1
    simulation_time = 30
    plant = VehicleModel(step_time, 0.25, 0.99, 0.05)
    estimator = LowPassFilter(plant.y_measure[0][0])
    controller = PID_Controller(target_y, plant.y_measure[0][0], step_time)

    for i in range(int(simulation_time/step_time)):
        #time
        time.append(step_time*i)

        #measure_y
        measure_y.append(plant.y_measure[0][0])

        # append
        estimated_y.append(estimator.estimate(plant.y_measure[0][0]))

        #estimator.estimate(plant.y_measure[0][0])

        controller.ControllerInput(target_y, estimator.y_estimate)
        plant.ControlInput(controller.u)

    plt.figure()
    plt.plot([0, time[-1]], [target_y, target_y], &#39;k-&#39;, label=&quot;reference&quot;)
    plt.plot(time, measure_y,&#39;r-&#39;,label = &quot;Vehicle Position(Measure)&quot;)
    plt.plot(time, estimated_y,&#39;c-&#39;,label = &quot;Vehicle Position(Estimator)&quot;)
    plt.xlabel(&#39;time (s)&#39;)
    plt.ylabel(&#39;signal&#39;)
    plt.legend(loc=&quot;best&quot;)
    #plt.axis(&quot;equal&quot;)
    plt.grid(True)
    plt.show()
</code></pre><h2 id="62-실행화면">6.2 실행화면</h2>
<h3 id="621-alpha--02">6.2.1 alpha = 0.2</h3>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/33ba3eae-050d-4677-be0b-0e4c19c11d36/image.png" alt=""></p>
<h3 id="622-alpha--07">6.2.2 alpha = 0.7</h3>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/0becb58b-31a1-4f83-8076-2c61547b84f5/image.png" alt=""></p>
<h1 id="7pid-controller-with-state-estimator--kalman-filter">7.PID Controller with State Estimator : Kalman Filter</h1>
<p>To be continue</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ROS]Path Planning ]]></title>
            <link>https://velog.io/@dazi2_2/ROSPath-Planning</link>
            <guid>https://velog.io/@dazi2_2/ROSPath-Planning</guid>
            <pubDate>Sun, 21 Jan 2024 05:53:20 GMT</pubDate>
            <description><![CDATA[<h1 id="0-개요">0. 개요</h1>
<h1 id="1-rviz-에서-path-planning-시각화하기">1. Rviz 에서 Path Planning 시각화하기</h1>
<p>우선 rviz에 여러가지 토픽을 추가하자.
<img src="https://velog.velcdn.com/images/dazi2_2/post/46149c6a-2ba8-4de9-ad39-7232f4c1cea9/image.png" alt="">
추가한 토픽에 대해서 설명하자면,
기존에 사용하였던 <code>Map</code>, <code>LaserScan</code>, <code>RobotModel</code> , <code>PoseArray</code> 을 제외하고
<code>Global Cost Map</code>, <code>Local Cost Map</code>, <code>Global Plan</code>, <code>Local Plan</code> 를 추가하였다.
<code>Global footprint</code> 및 <code>Local footprint</code> 는 로봇의 위치를 나타낸다.
각 토픽의 역할을 rviz 화면을 통해 알아보자.</p>
<p>아래의 사진은 Local Cost Map 을 활성화 한 사진이다.
로봇의 주변의 환경을 인식하여 path planning에 효율적인 경로를 만드는데 사용된다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/06b898b1-1510-44af-81cf-c83dc27471a7/image.png" alt="local cost map">
아래의 사진은 Global Cost Map으로 map data를 통해 만들어진 전체의 Cost Map이다.
마찬가지로 path planning에 효율적인 경로를 만드는 정보를 제공한다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/8d9982f3-4e9c-44a4-9f60-3049b2510118/image.png" alt="global cost map"></p>
<p>Plan 요소의 경우 로봇을 움직여야지 확인 할 수 있는데, rviz 의 2D NavGaol 버튼을 통해 
로봇의 목적지를 설정해주면 아래의 화면과 같이 로봇의 진행방향에 선이 나타나는 것을 확인 할 수 있다.
첫번째 사진과 같이 빨간 선이 Global Plan으로 전체 경로를 나타내고, 
두번째 사진과 같이 초록색 짧은 선은 Local Plan으로 단기적인 경로를 나타낸다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/34dfd5dd-9b4d-4a9b-80d9-d5c4646b757e/image.png" alt="">
<img src="https://velog.velcdn.com/images/dazi2_2/post/1d2ec31c-8e23-4f81-bee9-1171a2e79d2e/image.png" alt=""></p>
<h1 id="2-오픈소스-path-planning-패키지">2. 오픈소스 Path Planning 패키지</h1>
<p>Move_base 노드에는 다양한 토픽들이 존재하고, 해당 토픽을 발행하여
로봇을 컨트롤 할 수 있다.
파이썬 코드를 작성하여 토픽을 발행하여 로봇을 컨트롤 해보자.</p>
<h2 id="21-실행-코드">2.1 실행 코드</h2>
<p>send_goal_client.py</p>
<pre><code>#! /usr/bin/env python

import rospy
import time
import actionlib
from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal, MoveBaseResult, MoveBaseFeedback

goal_list = [[1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0],
             [1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0],
             [-1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0]]

def feedback_callback(msg):
    &quot;&quot;&quot;
    definition of the feedback callback. This will be called when feedback
    is received from the action server
    it just prints a message indicating a new message has been received
    &quot;&quot;&quot;
    rospy.loginfo(&quot;[Feedback] Going to Goal Pose...&quot;)

# initializes the action client node
rospy.init_node(&#39;move_base_action_client&#39;)
# create the connection to the action server
client = actionlib.SimpleActionClient(&#39;move_base&#39;, MoveBaseAction)
# waits until the action server is up and running
client.wait_for_server()

while not rospy.is_shutdown():
    i = 1
    for goal in goal_list:
        # creates a goal to send to the action server
        goal_msg = MoveBaseGoal()
        goal_msg.target_pose.header.frame_id = &#39;map&#39;
        goal_msg.target_pose.pose.position.x = goal[0]
        goal_msg.target_pose.pose.position.y = goal[1]
        goal_msg.target_pose.pose.position.z = goal[2]
        goal_msg.target_pose.pose.orientation.x = goal[3]
        goal_msg.target_pose.pose.orientation.y = goal[4]
        goal_msg.target_pose.pose.orientation.z = goal[5]
        goal_msg.target_pose.pose.orientation.w = goal[6]

        rospy.loginfo(&quot;Goal Target &quot; + str(i))

        # sends the goal to the action server, specifying which feedback function
        # to call when feedback received
        client.send_goal(goal=goal_msg, feedback_cb=feedback_callback)
        client.wait_for_result()

        rospy.loginfo(&quot;[Result] State: %d&quot;%(client.get_state()))
        i += 1</code></pre><p>세 가지 목적지를 부여하고, 순서대로 목적지에 도달하면 다음목적지를 목적지로 설정하고, 이 과정을 반복하는 코드이다.</p>
<h2 id="22-실행영상">2.2 실행영상</h2>
<blockquote>
<p><a href="https://www.youtube.com/watch?v=QasvoEjDdjM">https://www.youtube.com/watch?v=QasvoEjDdjM</a></p>
</blockquote>
<p>용량이 커서 유튜부 영상으로 업로드하였다.</p>
<h1 id="3-global-planner--global-costmap">3. Global planner &amp; Global Costmap</h1>
<h1 id="4-local-planner--local-costmap">4. Local Planner &amp; Local Costmap</h1>
<h1 id="5-commom-costmap-parameters">5. Commom Costmap Parameters</h1>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ROS] Robot Localization]]></title>
            <link>https://velog.io/@dazi2_2/ROS-Robot-Localization</link>
            <guid>https://velog.io/@dazi2_2/ROS-Robot-Localization</guid>
            <pubDate>Sat, 20 Jan 2024 14:13:55 GMT</pubDate>
            <description><![CDATA[<h1 id="🐣0개요">🐣0.개요</h1>
<p>지난번에 로봇이 자율주행 하기 위해서 필요한 Mapping 이라는 요소를 알아보았다.
이번에는 Localization에 대해서 알아보고 어떤식으로 동작하는지 알아보자.
아래의 사진은 이번에 사용할 로봇 모델 husky.
<img src="https://velog.velcdn.com/images/dazi2_2/post/bbe8f68c-504e-4932-94f3-0f31936ab89f/image.png" alt=""></p>
<h1 id="🗺️1rviz에서-localization-사용하기">🗺️1.Rviz에서 Localization 사용하기</h1>
<p>rviz 에서 depth 카메라 및 AMCL을 사용해서 localization 을 어떤식으로 하는지,
AMCL 노드의 경우 여러가지 파라미터를 수정 할 수 있는데, 각종 파라미터를 변경해보고 결과를 확인해보자.</p>
<h1 id="📹2-depth-카메라">📹2. depth 카메라</h1>
<p>기존에 사용하던 cafe 맵에 husky 로봇을 소환하였다.
rviz에서 depth 카메라를 확인 할 수 있다.
로봇 앞에 원통의 장애물을 두었을때 아래의 사진과 같이 기본 사진(좌측 하단)과, depth를 파악한 사진(좌측 상단)을 확인 할 수 있다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/3aad512b-886e-4443-8e23-aecc5496700b/image.png" alt=""></p>
<h1 id="⚙️3-amcl-adaptive-monte-carlo-localization">⚙️3. AMCL (Adaptive Monte Carlo Localization)</h1>
<p>AMCL은 적응형 몬테카를로 로컬라이제이션으로 센서의 값과 map data를 바탕으로 로봇의 위치를 추정하는 기법이다.
아래의 실행화면을 보면 로봇근처에 수많은 화살표를 볼 수 있다. 
데이터를 취합해서 로봇이 위치할 수 있는 확률이 있는 곳에 표현하게 된다.
하나의 화살표를 파티클이라고도 한다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/672096ab-34d5-400b-ae3e-20853c9d2578/image.png" alt="">
<img src="https://velog.velcdn.com/images/dazi2_2/post/8fb6041f-c92c-498c-ad07-fa7ddb2aa46d/image.png" alt="">
map의 데이터가 정확하고 로봇을 움직일 수록 화살표가 한점으로 모이게 된다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/579802ca-aabd-4002-8ca8-12fd65602b5d/image.gif" alt=""></p>
<h2 id="31-amcl-파라미터-변경해보기">3.1 AMCL 파라미터 변경해보기</h2>
<p><code>.yaml</code> 파일을 통해 <code>amcl</code>의 파라미터를 변경할 수 있다.
아래와 같이 런치 파일 및 <code>.yaml</code> 코드를 작성하였다.</p>
<p>🚀my_amcl.launch</p>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;launch&gt;
    &lt;arg name=&quot;scan_topic&quot; default=&quot;scan&quot; /&gt;
    &lt;arg name=&quot;map_file&quot; default=&quot;$(find husky_navigation)/maps/my_map.yaml&quot; /&gt;

    &lt;node name=&quot;map_server&quot; pkg=&quot;map_server&quot; type=&quot;map_server&quot; args=&quot;$(arg map_file)&quot; /&gt;

    &lt;!-- AMCL Node --&gt;
    &lt;node pkg=&quot;amcl&quot; type=&quot;amcl&quot; name=&quot;amcl&quot;&gt;
        &lt;rosparam file=&quot;$(find husky_navigation)/config/amcl_params.yaml&quot; command=&quot;load&quot; /&gt;
        &lt;remap from=&quot;scan&quot; to=&quot;$(arg scan_topic)&quot; /&gt;
    &lt;/node&gt;

    &lt;!-- Visulization --&gt;
    &lt;node name=&quot;rviz&quot; pkg=&quot;rviz&quot; type=&quot;rviz&quot; args=&quot;-d $(find husky_navigation)/rviz/amcl.rviz&quot;/&gt;
&lt;/launch&gt;</code></pre>
<p>🗒amcl_params.yaml</p>
<pre><code>use_map_topic: true
odom_model_type: diff
odom_frame_id: odom

gui_publish_rate: 10.0
min_particles: 500
max_particles: 2000
kld_err: 0.05
update_min_d: 0.25
update_min_a: 0.2
resample_interval: 1
transform_tolerance: 1.0

laser_max_beams: 60
laser_max_range: 1.0
laser_z_hit: 0.5
laser_z_short: 0.05
laser_z_max: 0.05
laser_z_rand: 0.5</code></pre><h3 id="311-min_particle-및-max_particles를-각-1-5로-설정">3.1.1 min_particle 및 max_particles를 각 1 ,5로 설정</h3>
<p>기존의 설정 값보다 매우 적은 파티클을 확인 할 수 있다.
이렇게 되면 로봇의 위치파악이 훨씬 더 어려워 진다고 할 수 있다.
그러면 화살표가 많으면 좋을수 있지 않을까?
하지만 연산수가 늘어나기 때문에 로봇의 스펙에 맞춰서 결정하는것이 좋다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/1d8753a3-1517-473a-8d42-6c34e4fb2f11/image.png" alt=""></p>
<h3 id="312-laser_max_range-를-1로-변경하기">3.1.2 laser_max_range 를 1로 변경하기</h3>
<p>초기화면은 동일하나 로봇을 키보드를 통해 조작하였을 경우 파티클이 뭉치지 않고,
파티클이 좀더 분산돼어 정확한 위치를 확인 할 수 없었다.
레이저 센서를 사용하여 localization 에 얼마나 중요한 지 알 수 있었다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/f4b2ec75-74f4-4ce7-bf97-382e318e7d7f/image.png" alt="이동 전"></p>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/0e50c3d0-a798-4c87-a4e7-2244fb248a21/image.png" alt="이동 후"></p>
<h2 id="32-global_localization">3.2 global_localization</h2>
<p>amcl 노드에서는 여러가지 서비스를 제공 사용할 수 있는데, 그중에는 <code>global_localization</code> 과 <code>static_map</code> 이 있다.
<code>global_localization</code>은 모든 파티클이 Map의 빈공간에 무작위로 분산되는 기능이다.
무작위로 분산시키는 기능은 로봇의 위치를 제공해주지 않을 때, 센서로 주변환경의 데이터를 취합하여 amcl의 위치 추정 정보를 제공하게 된다. 
<code>static_map</code> 은 Laser 기반 위치 추정에 사용되는 Map을 가져오는 기능을 한다. 필요한 map data를 얻을 때 사용한다.</p>
<h3 id="321-global_localization-호출코드">3.2.1 global_localization 호출코드</h3>
<p>🐍init_particles_caller.py</p>
<pre><code>#! /usr/bin/env python

import rospy
from std_srvs.srv import Empty, EmptyRequest
import sys

rospy.init_node(&quot;init_particles_caller&quot;)
rospy.wait_for_service(&#39;/global_localization&#39;)

disperse_particles_service = rospy.ServiceProxy(&#39;/global_localization&#39;, Empty)
msg = EmptyRequest()
result = disperse_particles_service(msg)

rospy.loginfo(result)</code></pre><h3 id="322-global_localization-실행화면">3.2.2 global_localization 실행화면</h3>
<p>월드 및 로봇을 소환하고 아래의 명령어를 통해 파이썬코드를 실행하면 아래와 같이 파티클이 퍼지는 것을 확인 할 수 있다.
로봇을 계속움직일 수록 로봇의 실제 위치와 가까운 곳에 파티클이 모이는 것을 확인 할 수 있었다.</p>
<pre><code>rosrun husky_launch init_particles_caller.py</code></pre><p><img src="https://velog.velcdn.com/images/dazi2_2/post/bd740750-ac24-404e-9271-ef23273d542c/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ROS] SLAM 알아보기]]></title>
            <link>https://velog.io/@dazi2_2/ROS-SLAM-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@dazi2_2/ROS-SLAM-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Sat, 20 Jan 2024 01:50:08 GMT</pubDate>
            <description><![CDATA[<h1 id="🐢0개요">🐢0.개요</h1>
<p>자율주행에서 mapping은 아주 중요한 요소이다.
로봇이 SLAM을 이용하여 Mapping을 어떻게 하여 Map 파일을 만드는지 아래의 과정을 통해 알아보자</p>
<blockquote>
</blockquote>
<p>목차</p>
<ol>
<li>Rviz에서 Mapping 과정 시각화 하기</li>
<li>Map 파일 다루기</li>
<li>Transforms</li>
<li>SLAM 오픈소스 패키지</li>
</ol>
<h1 id="⛰️1rviz에서-mapping-과정-시각화">⛰️1.Rviz에서 Mapping 과정 시각화</h1>
<p>데모 gmapping 파일을 통해 mapping 과정을 시각적으로 알아보자.
카페 맵에 꼬부기 소환 및 gmapping_demo.launch 를 실행하였다.</p>
<h2 id="11-터미널-명령어">1.1 터미널 명령어</h2>
<pre><code class="language-ruby">roslaunch turtlebot_navigation_gazebo main.launch 
roslaunch turtlebot_navigation_gazebo gmapping_demo.launch</code></pre>
<p>각각 다른 터미널에서 실행새주고 Rviz를 따로 실행시켜주자</p>
<h2 id="12-실행화면">1.2 실행화면</h2>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/18fdde0f-300d-4ccd-bacd-79fca04a1a9c/image.png" alt="초기사진">
초기 mapping 화면은 위와 같다.
Add를 통해 로봇 및 레이저 스캔을 추가해주었다.</p>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/f61fc5a2-f055-4bba-b8ed-969dcaefcd43/image.png" alt=""></p>
<p>키보드를 통해 꼬부기 로봇을 이동시켜 mapping 범위를 늘릴 수 있었다.</p>
<h1 id="🗺️2map-다루기">🗺️2.Map 다루기</h1>
<p>gmaping 을 통해 저장된 map 은 <code>map.yaml</code> 과 <code>map.pgm</code>으로 저장 할 수 있다.
저장 터미널 명령어는 아래와 같다.</p>
<pre><code class="language-ruby">roslaunch rosrun map_server map_saver -f &quot;저장할 map이름&quot;</code></pre>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/366964b1-069e-4f97-a6ce-60520d56cb4d/image.png" alt="">
위와 같이 저장된 모습 확인 할 수 있다.</p>
<pre><code>rosrun map_server map_server ~경로/파일.yaml</code></pre><p><img src="https://velog.velcdn.com/images/dazi2_2/post/9f289671-82cc-4cd3-b7b5-b6e781255346/image.png" alt="">
저장된 map은 rviz에서 로드 할 수 있다.</p>
<h2 id="실습">실습</h2>
<h3 id="211-launch-파일-작성하기">2.1.1 launch 파일 작성하기</h3>
<p>map_server 노드를 roslanch 를 통해 실행 할 수 있는 파일을 만들어 보자.
🚀map_server.launch</p>
<pre><code class="language-xml">&lt;launch&gt;
    &lt;arg name=&quot;map_file&quot; default=&quot;/home/dazi/sim_ws/src/programmers_turtlebot/turtlebot_navigation_gazebo/maps/playground.yaml&quot; /&gt;

    &lt;node name=&quot;map_server&quot; pkg=&quot;map_server&quot; type=&quot;map_server&quot; args=&quot;$(arg map_file)&quot; /&gt;
&lt;/launch&gt;</code></pre>
<h3 id="212-실행화면">2.1.2 실행화면</h3>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/b196b90b-90e6-44f0-bf3a-454f36fadb25/image.png" alt=""></p>
<h3 id="221-static_map-서비스를-처리-할-수-있는-서비스-클라이언트-노드-작성하기">2.2.1 /static_map 서비스를 처리 할 수 있는 서비스 클라이언트 노드 작성하기</h3>
<p>🐍call_map_service.py</p>
<pre><code class="language-python">#! /usr/bin/env python

import rospy
from nav_msgs.srv import GetMap, GetMapRequest
import sys

rospy.init_node(&#39;service_client&#39;) # Initialise a ROS node with the name service_client
rospy.wait_for_service(&#39;static_map&#39;) # Wait for the service /static_map to be running
rospy.loginfo(&quot;init complete&quot;)

get_map_service = rospy.ServiceProxy(&#39;static_map&#39;, GetMap) # Create the connection to the service
get_map = GetMapRequest() # Create an object of type GetMapRequest

result = get_map_service(get_map) # Call the service
print(result) # Print the result given by the service called</code></pre>
<h3 id="212-실행화면-1">2.1.2 실행화면</h3>
<p>아래의 터미널과 같은 실행 결과를 확인 할 수 있었다. 
<img src="https://velog.velcdn.com/images/dazi2_2/post/5a7f545c-1a1f-4436-9e29-ef8abfccb4f2/image.png" alt=""></p>
<h1 id="🤖3transforms">🤖3.Transforms</h1>
<p>mapping을 잘 하기 위해서는 제대로 된 map을 만들어야 한다.
그러기 위해서는 좋은 로봇을 사용해야한다.
로봇에서 인식하는 <code>odom</code> 과 <code>laser</code> 데이터의 퀄리티가 높아야 좋은 mapping을 할 수 있다.</p>
<h2 id="31-tf란">3.1 TF란?</h2>
<p>한 프레임에 표현된 데이터를 다른 프레임으로 변환하는 방법으로 <code>Position</code>, <code>Orientation</code> 정보를 포함한다.
특히 로봇의 <code>base_link</code> 와 <code>laser_link</code> 관계는 mapping에 반드시 필요하다.</p>
<p>$rqt 를 통해 알아본 <code>꼬부기</code>봇의 관계를 확인 할 수 있다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/a3c267d5-ddba-4cff-8fed-ff77458d7234/image.png" alt=""></p>
<p>매우 많지만 실행화면의 일부만을 가져왔다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/39a91c11-9752-4a09-8a21-edc73ec6c876/image.png" alt=""></p>
<p>rviz에서 시각적으로도 확인 할 수 있다.</p>
<h2 id="32-tf-publishing">3.2 TF publishing</h2>
<p>만약 어떤 로봇에 다른 TF를 추가하고 싶으면 아래와 같은 <code>launch</code>파일을 작성 하면 된다.
🚀pub_static_tf.launch</p>
<pre><code>&lt;launch&gt;
    &lt;node pkg=&quot;tf&quot; type=&quot;static_transform_publisher&quot; name=&quot;static_tf_node&quot; 
          args=&quot;0 0 2 0 0 0 base_link button 30&quot;&gt;
    &lt;/node&gt;
&lt;/launch&gt;</code></pre><p><img src="https://velog.velcdn.com/images/dazi2_2/post/3726be9f-c9e7-4fd5-b308-2aec5435bfc9/image.png" alt="">
<img src="https://velog.velcdn.com/images/dazi2_2/post/1183926b-6999-493e-9076-e96a94b75d38/image.png" alt="">
rviz 및 rqt 에서 button 이 추가 된 것을 확인 할 수 있다.</p>
<h1 id="📦4slam-오픈소스-패키지">📦4.SLAM 오픈소스 패키지</h1>
<p>SLAM 의 오픈소스인 <code>gammping</code> 에는 성능을 조절하기 위한 여러가지 파라미터가 존재한다.
아래의 ros 위키에서 파라미터를 확인 할 수 있다.</p>
<blockquote>
<p><a href="https://wiki.ros.org/gmapping">https://wiki.ros.org/gmapping</a></p>
</blockquote>
<p>실습을 통해 어떤식으로 파라미터 값에 영향을 줄 수 있는지 ,
각 파라미터가 어떤역할을 하는지 알아보자.</p>
<h2 id="41-실습코드">4.1 실습코드</h2>
<p><code>.yaml</code> 코드에서 파라미터를 설정하고, launch 파일을 통해 해당 설정을 불러 올 수 있다.</p>
<p>🗒gmapping_params.yaml</p>
<pre><code>base_frame: base_footprint
odom_frame: odom
map_update_interval: 5.0
maxUrange: 6.0
maxRange: 8.0

minimumScore: 200

linearUpdate: 0.5
angularUpdate: 0.436
temporalUpdate: -1.0
resampleThreshold: 0.5
particles: 80
xmin: -1.0
ymin: -1.0
xmax: 1.0
ymax: 1.0

delta: 0.05
llsamplerange: 0.01
llsamplestep: 0.01
lasamplerange: 0.005
lasamplestep: 0.005</code></pre><p>🚀my_gmapping.launch</p>
<pre><code>&lt;launch&gt;
    &lt;arg name=&quot;scan_topic&quot; default=&quot;kobuki/laser/scan&quot; /&gt;

    &lt;node pkg=&quot;gmapping&quot; type=&quot;slam_gmapping&quot; name=&quot;slam_gmapping&quot; output=&quot;screen&quot;&gt;
        &lt;rosparam file=&quot;$(find turtlebot_navigation_gazebo)/param/gmapping_params.yaml&quot; command=&quot;load&quot; /&gt;

        &lt;remap from=&quot;scan&quot; to=&quot;$(arg scan_topic)&quot; /&gt;
    &lt;/node&gt;
&lt;/launch&gt;</code></pre><h2 id="42-실행화면">4.2 실행화면</h2>
<p>위 코드와 같이 설정하였을 때 아래와 같이 평소에 보던 실행화면을 확인 할 수 있었다. 
아래에서 설정을 바꾼 실행화면을 확인해보자.
<img src="https://velog.velcdn.com/images/dazi2_2/post/db544357-5566-45c1-af65-339758de9a93/image.png" alt=""></p>
<h3 id="421-maxurange-2로-변경">4.2.1 maxUrange 2로 변경</h3>
<p>로봇의 mapping 범위가 매우 좁아 진 것을 확인 할 수 있다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/2a966486-d1dc-4041-9fac-c0ae1da33282/image.png" alt=""></p>
<h3 id="422-xmin-ymin---100-xmax-ymax--100-으로-설정-후-화면-확인">4.2.2 xmin, ymin = -100 xmax, ymax = 100 으로 설정 후 화면 확인</h3>
<p>map의 크기가 매우 커진 것을 확인 할 수 있다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/dc3b7bfa-5ae0-4ee0-94c5-bb538290ae8e/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ROS] Mid project]]></title>
            <link>https://velog.io/@dazi2_2/ROS-Mid-project</link>
            <guid>https://velog.io/@dazi2_2/ROS-Mid-project</guid>
            <pubDate>Thu, 18 Jan 2024 15:39:32 GMT</pubDate>
            <description><![CDATA[<h1 id="🤔0개요">🤔0.개요</h1>
<p>지금까지 ROS의 시뮬레이션 기초에 대해서 강의를 들으면서 배운내용들을 확인하기 위한 과제를 수행해보자.
아래의 목차의 순서대로 진행하면서 배운내용들을 다시 돌아보자!</p>
<h3 id="🗒목차">🗒목차</h3>
<blockquote>
</blockquote>
<p>1.🚖모델 및 월드생성
2.🔌플러그인 생성
3.🤖gazebo 차량 수정
4.🎮회피 알고리즘 설계</p>
<h1 id="🚖1모델-및-월드-생성">🚖1.모델 및 월드 생성</h1>
<h2 id="🗿11-모델-생성">🗿1.1 모델 생성</h2>
<p>🗿model.sdf</p>
<pre><code class="language-html">&lt;?xml version=&#39;1.0&#39;?&gt;
&lt;sdf version=&quot;1.6&quot;&gt;
    &lt;model name=&quot;custom_mid_obj&quot;&gt;
        &lt;pose&gt;0 0 0.5 0 0 0&lt;/pose&gt;
        &lt;static&gt;false&lt;/static&gt;
        &lt;link name=&quot;link&quot;&gt;
            &lt;inertial&gt;
                &lt;mass&gt;1.0&lt;/mass&gt;
                &lt;inertia&gt;
                    &lt;ixx&gt;0.083&lt;/ixx&gt;
                    &lt;ixy&gt;0.0&lt;/ixy&gt;
                    &lt;ixz&gt;0.0&lt;/ixz&gt;
                    &lt;iyy&gt;0.083&lt;/iyy&gt;
                    &lt;iyz&gt;0.0&lt;/iyz&gt;
                    &lt;izz&gt;0.083&lt;/izz&gt;
                &lt;/inertia&gt;
            &lt;/inertial&gt;

            &lt;!-- can --&gt;
            &lt;collision name=&quot;can_1&quot;&gt;
                &lt;pose&gt;0 2 0 0 0 0&lt;/pose&gt;
                &lt;geometry&gt;
                    &lt;cylinder radius=&quot;0.05&quot; length=&quot;1&quot;/&gt;
                &lt;/geometry&gt;
            &lt;/collision&gt;
            &lt;visual name=&quot;can_visual_1&quot;&gt;
                &lt;pose&gt;0 2 0 0 0 0&lt;/pose&gt;
                &lt;geometry&gt;
                    &lt;cylinder&gt;
                        &lt;radius&gt;0.25&lt;/radius&gt;
                        &lt;length&gt;1&lt;/length&gt;
                    &lt;/cylinder&gt;
                &lt;/geometry&gt;
            &lt;/visual&gt;

            &lt;collision name=&quot;can_2&quot;&gt;
                &lt;pose&gt;-1.732 -1 0 0 0 0 &lt;/pose&gt;
                &lt;geometry&gt;
                    &lt;cylinder&gt;
                        &lt;radius&gt;0.25&lt;/radius&gt;
                        &lt;length&gt;1&lt;/length&gt;
                    &lt;/cylinder&gt;
                &lt;/geometry&gt;
            &lt;/collision&gt;
            &lt;visual name=&quot;can_visual_2&quot;&gt;
                &lt;pose&gt;-1.732 -1 0 0 0 0 &lt;/pose&gt;
                &lt;geometry&gt;
                    &lt;cylinder&gt;
                        &lt;radius&gt;0.25&lt;/radius&gt;
                        &lt;length&gt;1&lt;/length&gt;
                    &lt;/cylinder&gt;
                &lt;/geometry&gt;
            &lt;/visual&gt;

            &lt;collision name=&quot;can_3&quot;&gt;
                &lt;pose&gt;1.732 -1 0 0 0 0 &lt;/pose&gt;
                &lt;geometry&gt;
                    &lt;cylinder&gt;
                        &lt;radius&gt;0.25&lt;/radius&gt;
                        &lt;length&gt;1&lt;/length&gt;
                    &lt;/cylinder&gt;
                &lt;/geometry&gt;
            &lt;/collision&gt;
            &lt;visual name=&quot;can_visual_3&quot;&gt;
                &lt;pose&gt;1.732 -1 0 0 0 0 &lt;/pose&gt;
                &lt;geometry&gt;
                    &lt;cylinder&gt;
                        &lt;radius&gt;0.25&lt;/radius&gt;
                        &lt;length&gt;1&lt;/length&gt;
                    &lt;/cylinder&gt;
                &lt;/geometry&gt;
            &lt;/visual&gt;
            &lt;!-- box --&gt;
            &lt;collision name=&quot;box_1&quot;&gt;
                &lt;pose&gt;-1 0.366 0 0 0 1.047198 &lt;/pose&gt;
                &lt;geometry&gt;
                    &lt;box&gt;
                        &lt;size&gt;3.464 0.2 0.8&lt;/size&gt;
                    &lt;/box&gt;
                &lt;/geometry&gt;
            &lt;/collision&gt;
            &lt;visual name=&quot;box_visual_1&quot;&gt;
                &lt;pose&gt;-1 0.366 0 0 0 1.047198 &lt;/pose&gt;
                &lt;geometry&gt;
                    &lt;box&gt;
                        &lt;size&gt;3.464 0.2 0.8&lt;/size&gt;
                    &lt;/box&gt;
                &lt;/geometry&gt;
            &lt;/visual&gt;

            &lt;collision name=&quot;box_2&quot;&gt;
                &lt;pose&gt;1 0.366 0 0 0 -1.047198 &lt;/pose&gt;
                &lt;geometry&gt;
                    &lt;box&gt;
                        &lt;size&gt;3.464 0.2 0.8&lt;/size&gt;
                    &lt;/box&gt;
                &lt;/geometry&gt;
            &lt;/collision&gt;
            &lt;visual name=&quot;box_visual_2&quot;&gt;
                &lt;pose&gt;1 0.366 0 0 0 -1.047198 &lt;/pose&gt;
                &lt;geometry&gt;
                    &lt;box&gt;
                        &lt;size&gt;3.464 0.2 0.8&lt;/size&gt;
                    &lt;/box&gt;
                &lt;/geometry&gt;
            &lt;/visual&gt;

            &lt;collision name=&quot;box_3&quot;&gt;
                &lt;pose&gt;0 -1 0 0 0 0 &lt;/pose&gt;
                &lt;geometry&gt;
                    &lt;box&gt;
                        &lt;size&gt;3.464 0.2 0.8&lt;/size&gt;
                    &lt;/box&gt;
                &lt;/geometry&gt;
            &lt;/collision&gt;
            &lt;visual name=&quot;box_visual_3&quot;&gt;
                &lt;pose&gt;0 -1 0 0 0 0 &lt;/pose&gt;
                &lt;geometry&gt;
                    &lt;box&gt;
                        &lt;size&gt;3.464 0.2 0.8&lt;/size&gt;
                    &lt;/box&gt;
                &lt;/geometry&gt;
            &lt;/visual&gt;
        &lt;/link&gt;
    &lt;/model&gt;
&lt;/sdf&gt;
</code></pre>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/f839e935-58ef-4308-b47b-946ce08dba63/image.png" alt="">
위와 같은 모델을 구성하는 sdf 파일이다.
기본모델인 <code>box</code>, <code>cylinder</code>를 통해 구성하였다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/41afb158-d5d0-4ffb-975a-0b6d47c5eb10/image.png" alt="">
모델을 생성하는 중 위와 같이 <code>cylinder</code> 의 형태를 인식 못하는 문제가 있었는데 
아래와 같이 해결하였다.</p>
<p>수정 전
🗿model.sdf</p>
<pre><code class="language-html">...
&lt;cylinder radius=&quot;0.25&quot; length=&quot;1&quot;/&gt;
...</code></pre>
<p>수정 후</p>
<pre><code class="language-html">...
&lt;cylinder&gt;
        &lt;radius&gt;0.25&lt;/radius&gt;
          &lt;length&gt;1&lt;/length&gt;
&lt;/cylinder&gt;
...</code></pre>
<p>가제보에서 수정 전 코드의 tag를 인식못해 발생하는 문제 같다.</p>
<p>⚙️model.config</p>
<pre><code class="language-html">&lt;?xml version=&quot;1.0&quot;?&gt;

&lt;model&gt;
    &lt;name&gt;Box Model&lt;/name&gt;
    &lt;version&gt;1.0&lt;/version&gt;
    &lt;sdf version=&quot;1.6&quot;&gt;model.sdf&lt;/sdf&gt;

    &lt;author&gt;
        &lt;name&gt;Nodazi&lt;/name&gt;
        &lt;email&gt;dk3146@naver.com&lt;/email&gt;
    &lt;/author&gt;

    &lt;description&gt;
        A simple box model
    &lt;/description&gt;
&lt;/model&gt;</code></pre>
<p>모델의 설명을 <code>config</code>파일에 작성하였다.
가제보에서 해당 모델을 인식할 수 있게 <code>.gazebo/models</code>파일에 넣어두자!</p>
<h2 id="🗺️12-월드-생성">🗺️1.2 월드 생성</h2>
<p>20*20의 벽 안에 위에서 제작한 모델 4개를 10*10 안쪽에 생성하는 코드를 작성하였다.
🗺️mid_project_world.world</p>
<pre><code class="language-html">&lt;?xml version=&quot;1.0&quot; ?&gt;
&lt;sdf version=&quot;1.6&quot;&gt;
    &lt;world name=&quot;default&quot;&gt;

        &lt;!-- A global light source --&gt;
        &lt;include&gt;
            &lt;uri&gt;model://sun&lt;/uri&gt;
        &lt;/include&gt;

        &lt;!-- A ground plane --&gt;
        &lt;include&gt;
            &lt;uri&gt;model://ground_plane&lt;/uri&gt;
        &lt;/include&gt;

        &lt;!--static obj--&gt;
        &lt;population name=&quot;custom_mid_obj_population&quot;&gt;
            &lt;model name=&quot;custom_mid_obj&quot;&gt;
                &lt;include&gt;
                &lt;static&gt;true&lt;/static&gt;
                &lt;uri&gt;model://custom_mid_obj&lt;/uri&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;0 0 0 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;10 10 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;4&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;uniform&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;

        &lt;!-- walls --&gt;
        &lt;!-- +X grey_wall --&gt;
        &lt;population name=&quot;grey_wall_px&quot;&gt;
            &lt;model name=&quot;grey_wall&quot;&gt;
                &lt;include&gt;
                    &lt;static&gt;true&lt;/static&gt;
                    &lt;uri&gt;model://grey_wall&lt;/uri&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;3.333333 10.32 0.01 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;20 20 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;3&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;linear-x&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;

        &lt;!-- -X grey_wall --&gt;
        &lt;population name=&quot;grey_wall_mx&quot;&gt;
            &lt;model name=&quot;grey_wall&quot;&gt;
                &lt;include&gt;
                    &lt;static&gt;true&lt;/static&gt;
                    &lt;uri&gt;model://grey_wall&lt;/uri&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;3.333333 -10.32 0.01 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;20 20 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;3&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;linear-x&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;

        &lt;!-- +Y grey_wall --&gt;
        &lt;population name=&quot;grey_wall_py&quot;&gt;
            &lt;model name=&quot;grey_wall&quot;&gt;
                &lt;include&gt;
                    &lt;static&gt;true&lt;/static&gt;
                    &lt;uri&gt;model://grey_wall&lt;/uri&gt;
                    &lt;pose&gt;0 0 0 0 0 1.57&lt;/pose&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;10.32 3.333333 0.01 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;20 20 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;3&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;linear-y&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;

        &lt;!-- -Y grey_wall_py --&gt;
        &lt;population name=&quot;grey_wall_my&quot;&gt;
            &lt;model name=&quot;grey_wall&quot;&gt;
                &lt;include&gt;
                    &lt;static&gt;true&lt;/static&gt;
                    &lt;uri&gt;model://grey_wall&lt;/uri&gt;
                    &lt;pose&gt;0 0 0 0 0 1.57&lt;/pose&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;-10.32 3.333333 0.01 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;20 20 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;3&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;linear-y&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;       
    &lt;/world&gt;
&lt;/sdf&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/55dadbf0-c5e3-44cc-8b83-f7473f2153b9/image.png" alt="world 구동">
깔끔하게 생성된 모습</p>
<h1 id="🔌2플러그인-생성">🔌2.플러그인 생성</h1>
<p>위에서 벽안에 둘러쌓인 모델 4개를 균일하게 배치하였다.
플러그인을 생성하여 나머지 공간에 움직이는 모델 4개를 배치해보자.</p>
<h2 id="🔌21-플러그인-작성">🔌2.1 플러그인 작성</h2>
<p>🔌moving_plugin.cc</p>
<pre><code class="language-cpp">#include &lt;functional&gt;
#include &lt;gazebo/common/common.hh&gt;
#include &lt;gazebo/gazebo.hh&gt;
#include &lt;gazebo/physics/physics.hh&gt;
#include &lt;ignition/math/Vector3.hh&gt;

namespace gazebo {
class MovingPlugin : public ModelPlugin {
public:
  void Load(physics::ModelPtr _parent, sdf::ElementPtr _sdf) {
    // Store the pointer to the model
    this-&gt;model = _parent;
    this-&gt;iterations = 10 * 1000;
    if (_sdf-&gt;HasElement(&quot;iterations&quot;)) {
      this-&gt;iterations = _sdf-&gt;Get&lt;int&gt;(&quot;iterations&quot;);
    }
    this-&gt;linear_vel = 0.1;
    if (_sdf-&gt;HasElement(&quot;linear_vel&quot;)) {
      this-&gt;linear_vel = _sdf-&gt;Get&lt;double&gt;(&quot;linear_vel&quot;);
    }

    this-&gt;direction_x = 1.0;
    if (_sdf-&gt;HasElement(&quot;direction_x&quot;)) {
      this-&gt;direction_x = _sdf-&gt;Get&lt;double&gt;(&quot;direction_x&quot;);
    }

    this-&gt;direction_y = 0.0;
    if (_sdf-&gt;HasElement(&quot;direction_y&quot;)) {
      this-&gt;direction_y = _sdf-&gt;Get&lt;double&gt;(&quot;direction_y&quot;);
    }

    // this-&gt;direction = true;
    // if (_sdf-&gt;HasElement(&quot;direction&quot;)) {
    //   this-&gt;direction = _sdf-&gt;Get&lt;bool&gt;(&quot;direction&quot;);
    // }

    // Listen to the update event. This event is broadcast every simulation
    // iteration.
    this-&gt;updateConnection = event::Events::ConnectWorldUpdateBegin(
        std::bind(&amp;MovingPlugin::OnUpdate, this));
  }

  // Called by the world update start event
public:
  void OnUpdate() {
    // Apply a small linear velocity to the model.
    if ((counter / iterations) % 2 == 0) {
      this-&gt;model-&gt;SetLinearVel(
          ignition::math::Vector3d(this-&gt;linear_vel * this-&gt;direction_x,
                                   this-&gt;linear_vel * this-&gt;direction_y, 0));
    } else if ((counter / iterations) % 2 == 1) {
      this-&gt;model-&gt;SetLinearVel(
          ignition::math::Vector3d(-this-&gt;linear_vel * this-&gt;direction_x,
                                   -this-&gt;linear_vel * this-&gt;direction_y, 0));
    }
    counter++;
  }
  // Pointer to the model
private:
  physics::ModelPtr model;

private:
  double linear_vel;
  int iterations;
  int counter;
  double direction_x;
  double direction_y;
  // Pointer to the update event connection
private:
  event::ConnectionPtr updateConnection;
};

// Register this plugin with the simulator
GZ_REGISTER_MODEL_PLUGIN(MovingPlugin)
} // namespace gazebo</code></pre>
<p>코드를 간략히 설명하면 아래의 파라미터 값을 받아서 물체를 움직인다.
<code>linear_vel</code> : 물체가 움직이는 속도.
<code>iterations</code> : 물체가 방향 전환을 하기 까지의 시간.
<code>counter</code> : 물체의 방향 전환을 인식하기 위한 값.
<code>direction_x</code> : x방향으로 이동하는 값의 가중치.
<code>direction_y</code> : y방향으로 이동하는 값의 가중치.</p>
<p>x축 및 y축의 움직임을 유연하게 구현 하기위해 <code>direction_x</code>와 같은 변수를 추가하였다.
CMakeList.txt</p>
<pre><code class="language-ruby">...
add_library(moving_plugin SHARED src/moving_plugin.cc)
target_link_libraries(moving_plugin ${GAZEBO_LIBRARIES})
...</code></pre>
<p>플러그인을 인식하기 위해 위의 코드를 반드시 <code>CMakeLists.txt</code>파일에 넣어주자.
코드를 작성하고 <code>catkin_make</code>를 통해 빌드를 하면 플러그인이 생성된 것을 확인 할 수 있다.(위치 sim_ws/devel/lib)
<img src="https://velog.velcdn.com/images/dazi2_2/post/4261f81c-7777-48a1-bb67-00349a1d7ecc/image.png" alt="생성된 플러그인"></p>
<h2 id="🗺️22-월드-파일-수정">🗺️2.2 월드 파일 수정</h2>
<p>🗺️mid_project_world.world</p>
<pre><code class="language-html">...
        &lt;model name=&quot;moving_custom_mid_obj_1&quot;&gt;
            &lt;pose&gt;-7.5 7.5 0.5 0 0 0&lt;/pose&gt;
            &lt;static&gt;false&lt;/static&gt;
            &lt;include&gt;
                &lt;uri&gt;model://custom_mid_obj&lt;/uri&gt;
            &lt;/include&gt;

            &lt;plugin name=&quot;libmoving_plugin&quot; filename=&quot;libmoving_plugin.so&quot;&gt;
                &lt;linear_vel&gt;5.0&lt;/linear_vel&gt;
                &lt;iterations&gt;3000&lt;/iterations&gt;
                &lt;direction_x&gt;1.0&lt;/direction_x&gt;
                &lt;direction_y&gt;0.0&lt;/direction_y&gt;
            &lt;/plugin&gt;
        &lt;/model&gt;

        &lt;model name=&quot;moving_custom_mid_obj_2&quot;&gt;
            &lt;pose&gt;7.5 7.5 0.5 0 0 0&lt;/pose&gt;
            &lt;static&gt;false&lt;/static&gt;
            &lt;include&gt;
                &lt;uri&gt;model://custom_mid_obj&lt;/uri&gt;
            &lt;/include&gt;

            &lt;plugin name=&quot;libmoving_plugin&quot; filename=&quot;libmoving_plugin.so&quot;&gt;
                &lt;linear_vel&gt;5.0&lt;/linear_vel&gt;
                &lt;iterations&gt;3000&lt;/iterations&gt;
                &lt;direction_x&gt;0.0&lt;/direction_x&gt;
                &lt;direction_y&gt;-1.0&lt;/direction_y&gt;
            &lt;/plugin&gt;
        &lt;/model&gt;

        &lt;model name=&quot;moving_custom_mid_obj_3&quot;&gt;
            &lt;pose&gt;7.5 -7.5 0.5 0 0 0&lt;/pose&gt;
            &lt;static&gt;false&lt;/static&gt;
            &lt;include&gt;
                &lt;uri&gt;model://custom_mid_obj&lt;/uri&gt;
            &lt;/include&gt;

            &lt;plugin name=&quot;libmoving_plugin&quot; filename=&quot;libmoving_plugin.so&quot;&gt;
                &lt;linear_vel&gt;5.0&lt;/linear_vel&gt;
                &lt;iterations&gt;3000&lt;/iterations&gt;
                &lt;direction_x&gt;-1.0&lt;/direction_x&gt;
                &lt;direction_y&gt;0.0&lt;/direction_y&gt;
            &lt;/plugin&gt;
        &lt;/model&gt;

        &lt;model name=&quot;moving_custom_mid_obj_4&quot;&gt;
            &lt;pose&gt;-7.5 -7.5 0.5 0 0 0&lt;/pose&gt;
            &lt;static&gt;false&lt;/static&gt;
            &lt;include&gt;
                &lt;uri&gt;model://custom_mid_obj&lt;/uri&gt;
            &lt;/include&gt;

            &lt;plugin name=&quot;libmoving_plugin&quot; filename=&quot;libmoving_plugin.so&quot;&gt;
                &lt;linear_vel&gt;5.0&lt;/linear_vel&gt;
                &lt;iterations&gt;3000&lt;/iterations&gt;
                &lt;direction_x&gt;0.0&lt;/direction_x&gt;
                &lt;direction_y&gt;1.0&lt;/direction_y&gt;
            &lt;/plugin&gt;
        &lt;/model&gt;
...</code></pre>
<p>지난번에 생성한 모델에 플러그인을 설치하여, 움직임을 구현하였다.</p>
<h2 id="🖥️23-실행화면">🖥️2.3 실행화면</h2>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/bf273dfe-c3f3-4fc6-b9c8-37c47f3d5af1/image.gif" alt="">
위와 같이 잘 움직이는 것을 확인 할 수 있었다. 
나중에는 모델의 현제 위치를 받아서 방향을 바꾸는 방법도 구상하면 좋을 것 같다.</p>
<h1 id="🤖3-gazebo-차량-수정">🤖3. gazebo 차량 수정</h1>
<p>기존에 만든 로봇은 2개의 바퀴와 1개의 caster wheel로 구성돼있었다.
바퀴를 2개 더 추가하여 4개의 바퀴달린 차량을 만들고, <code>skid_steer_drive_controller</code> 플러그인을 추가해
사륜차량을 제어해보자.
로봇에 바퀴를 추가한 코드는 생략하겠다.</p>
<h2 id="31-차량-추가-코드">3.1 차량 추가 코드</h2>
<p>🤖robot.gazebo</p>
<pre><code class="language-html">    &lt;!-- skid steer driver --&gt;
    &lt;gazebo&gt;
    &lt;plugin name=&quot;skid_steer_drive_controller&quot; filename=&quot;libgazebo_ros_skid_steer_drive.so&quot;&gt;
        &lt;updateRate&gt;100.0&lt;/updateRate&gt;
        &lt;robotNamespace&gt;/&lt;/robotNamespace&gt;
        &lt;leftFrontJoint&gt;joint_chassis_left_front_wheel&lt;/leftFrontJoint&gt;
        &lt;rightFrontJoint&gt;joint_chassis_right_front_wheel&lt;/rightFrontJoint&gt;
        &lt;leftRearJoint&gt;joint_chassis_left_back_wheel&lt;/leftRearJoint&gt;
        &lt;rightRearJoint&gt;joint_chassis_right_back_wheel&lt;/rightRearJoint&gt;
        &lt;wheelSeparation&gt;1.16&lt;/wheelSeparation&gt;
        &lt;wheelDiameter&gt;0.8&lt;/wheelDiameter&gt;
        &lt;robotBaseFrame&gt;link_chassis&lt;/robotBaseFrame&gt;
        &lt;torque&gt;20&lt;/torque&gt;
        &lt;topicName&gt;cmd_vel&lt;/topicName&gt;
        &lt;broadcastTF&gt;true&lt;/broadcastTF&gt;
    &lt;/plugin&gt;
    &lt;/gazebo&gt;</code></pre>
<p> 기존의 이륜차량에는 <code>libgazebo_ros_diff_drive</code> 플러그인을 사용했었는데 실습중 그대로 작성하였을때는, 
 차량이 뭔가 제어가 잘 안되는 느낌이 가득 들었었다.
 알고보니 <code>skid_steer_drive_controller</code> 이라는 스키드 방식의 조향 플러그인을 사용해야 한다.</p>
<blockquote>
<p>참고 : <a href="https://classic.gazebosim.org/tutorials?tut=ros_gzplugins#JointPoseTrajectory">https://classic.gazebosim.org/tutorials?tut=ros_gzplugins#JointPoseTrajectory</a></p>
</blockquote>
<p> ＊참고로 스키드 조향은 여러가지 바퀴의 회전을 달리하여 조향을 만들어 내는 시스템이다.
 해당 플러그인을 넣어주고, 바퀴를 추가한뒤 차체 크기를 줄였다.</p>
<h2 id="32-차량-동작-영상">3.2 차량 동작 영상</h2>
<p> <img src="https://velog.velcdn.com/images/dazi2_2/post/652c6374-89ec-4a75-96f0-caad6658e9d1/image.gif" alt="4륜차량 구동영상">
전에 생성한 world에 spawn하여 키보드를 통해 조작을 해보았다.
움직이는 장애물의 크기가 크고 빠르게 움직여서 마구마구 두둘겨 맞는 우리의 로봇을 확인 할 수 있었다.
장애물 크기 및 속도를 개선해야겠다.
그리고 뭔가 조향이 마음대로 잘 안되는데 이에 대해서는 로봇의 설계를 좀 바꿔야 하는걸까..라는 생각을 하게 되었다.</p>
<p>차후 개선해볼 예정!</p>
<h1 id="🎮4-장애물-회피-알고리즘-설계">🎮4. 장애물 회피 알고리즘 설계</h1>
<p>우선 장애물 크기를 줄이고 레이저를 통해 상황만을 인식하는 sample 코드를 입력해서 동작 시켜보았다. 
<img src="https://velog.velcdn.com/images/dazi2_2/post/86d1ebb1-544b-4251-9049-71410f5c01d9/image.gif" alt="">
<img src="https://velog.velcdn.com/images/dazi2_2/post/0ace8f59-51a1-45e6-9410-d1982faeacd7/image.png" alt="">
상황에 대한 브리핑이 위와 같이 터미널에서 나타나는데 코드를 개선하여 움직임을 줘봐야겠다.
..to be continue</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ROS] World 만들기 ]]></title>
            <link>https://velog.io/@dazi2_2/ROS-World-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@dazi2_2/ROS-World-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Wed, 17 Jan 2024 12:42:10 GMT</pubDate>
            <description><![CDATA[<h1 id="🗺️과제--world-만들기">🗺️과제 : World 만들기</h1>
<p><code>population</code>을 이용하여 World를 제작해 보았다.</p>
<p><code>population</code>의 다양한 패턴을 사용하여 물체를 배치해보았다.
<code>random</code> , <code>grid</code> , <code>linear</code> 과 같은 패턴이 존재하는데,
각각의 <code>tag</code>의 사용법을 좀더 익힐 수 있었다.</p>
<p>만든 world에 <code>robot</code>을 소환시켜 <code>camera</code> 토픽에 어떤식으로 보이는지 확인하였다.</p>
<h2 id="🚗실행화면">🚗실행화면</h2>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/805c9db3-4d69-4352-a31d-860d1a3334b4/image.png" alt=""></p>
<p>아래의 링크에서 카메라 토픽의 화면을 확인 할 수 있다.</p>
<blockquote>
<p>실습 영상 -&gt; <a href="https://www.youtube.com/watch?v=zjq92hpUuFU&amp;ab_channel=Nodazi">https://www.youtube.com/watch?v=zjq92hpUuFU&amp;ab_channel=Nodazi</a></p>
</blockquote>
<h2 id="🌍world-코드">🌍world 코드</h2>
<p>🌍test_world.world</p>
<pre><code>&lt;?xml version=&quot;1.0&quot; ?&gt;
&lt;sdf version=&quot;1.5&quot;&gt;
    &lt;world name=&quot;default&quot;&gt;

        &lt;!-- A global light source --&gt;
        &lt;include&gt;
            &lt;uri&gt;model://sun&lt;/uri&gt;
        &lt;/include&gt;

        &lt;!-- A ground plane --&gt;
        &lt;include&gt;
            &lt;uri&gt;model://ground_plane&lt;/uri&gt;
        &lt;/include&gt;

        &lt;!-- ground --&gt;
        &lt;population name=&quot;ground&quot;&gt;
            &lt;model name=&quot;ground&quot;&gt;
                &lt;include&gt;
                &lt;static&gt;true&lt;/static&gt;
                &lt;uri&gt;model://asphalt_plane&lt;/uri&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;0 0 0.01 0 0 0&lt;/pose&gt;
            &lt;distribution&gt;
                &lt;type&gt;grid&lt;/type&gt;
                &lt;rows&gt;2&lt;/rows&gt;
                &lt;cols&gt;2&lt;/cols&gt;
                &lt;step&gt;20 20 0.0&lt;/step&gt;
            &lt;/distribution&gt;

        &lt;/population&gt;

        &lt;!-- brick_box_3x1x3 walls --&gt;
        &lt;!-- +X brick_box_3x1x3 --&gt;
        &lt;population name=&quot;brick_box_3x1x3&quot;&gt;
            &lt;model name=&quot;brick_box_3x1x3_1&quot;&gt;
                &lt;include&gt;
                    &lt;static&gt;true&lt;/static&gt;
                    &lt;uri&gt;model://brick_box_3x1x3&lt;/uri&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;1.5 12.5 0.3 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;30 30 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;10&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;linear-x&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;

        &lt;!-- -X brick_box_3x1x3 --&gt;
        &lt;population name=&quot;brick_box_3x1x3&quot;&gt;
            &lt;model name=&quot;brick_box_3x1x3_2&quot;&gt;
                &lt;include&gt;
                    &lt;static&gt;true&lt;/static&gt;
                    &lt;uri&gt;model://brick_box_3x1x3&lt;/uri&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;1.5 -12.5 0.3 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;30 30 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;10&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;linear-x&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;

        &lt;!-- +Y brick_box_3x1x3 --&gt;
        &lt;population name=&quot;brick_box_3x1x3&quot;&gt;
            &lt;model name=&quot;brick_box_3x1x3_3&quot;&gt;
                &lt;include&gt;
                    &lt;static&gt;true&lt;/static&gt;
                    &lt;uri&gt;model://brick_box_3x1x3&lt;/uri&gt;
                    &lt;pose&gt;0 0 0 0 0 1.57&lt;/pose&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;14.5 1.5 0.3 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;24 24 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;8&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;linear-y&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;

        &lt;!-- +Y brick_box_3x1x3 --&gt;
        &lt;population name=&quot;brick_box_3x1x3&quot;&gt;
            &lt;model name=&quot;brick_box_3x1x3_4&quot;&gt;
                &lt;include&gt;
                    &lt;static&gt;true&lt;/static&gt;
                    &lt;uri&gt;model://brick_box_3x1x3&lt;/uri&gt;
                    &lt;pose&gt;0 0 0 0 0 1.57&lt;/pose&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;-14.5 1.5 0.3 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;24 24 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;8&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;linear-y&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;

        &lt;!-- random models population --&gt;
        &lt;population name=&quot;construction_barrel_population&quot;&gt;
            &lt;model name=&quot;construction_barrel&quot;&gt;
                &lt;include&gt;
                &lt;static&gt;false&lt;/static&gt;
                &lt;uri&gt;model://construction_barrel&lt;/uri&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;8.5 5 0.2 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;10 5 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;15&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;uniform&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;

        &lt;!-- coke_can_random --&gt;
        &lt;population name=&quot;coke_can_random&quot;&gt;
            &lt;model name=&quot;coke_can&quot;&gt;
                &lt;include&gt;
                &lt;static&gt;false&lt;/static&gt;
                &lt;uri&gt;model://coke_can&lt;/uri&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;-8.5 -5 0.2 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;5 5 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;100&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;random&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;

        &lt;!-- dumpster --&gt;
        &lt;population name=&quot;dumpster&quot;&gt;
            &lt;model name=&quot;dumpster&quot;&gt;
                &lt;include&gt;
                    &lt;static&gt;false&lt;/static&gt;
                    &lt;uri&gt;model://dumpster&lt;/uri&gt;
                    &lt;pose&gt;0 0 0 0 0 1.57&lt;/pose&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;-11.8 -4 0.1 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;10 12 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;4&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;linear-y&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;

        &lt;!-- double_pendulum_with_base --&gt;
        &lt;population name=&quot;double_pendulum_with_base_random&quot;&gt;
            &lt;model name=&quot;double_pendulum_with_base&quot;&gt;
                &lt;include&gt;
                &lt;static&gt;false&lt;/static&gt;
                &lt;uri&gt;model://double_pendulum_with_base&lt;/uri&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;8.5 -5 0.2 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;10 10 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;10&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;random&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;
    &lt;/world&gt;
&lt;/sdf&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[ROS] 시뮬레이터 심화편]]></title>
            <link>https://velog.io/@dazi2_2/ROS-%EC%8B%9C%EB%AE%AC%EB%A0%88%EC%9D%B4%ED%84%B0-%EC%8B%AC%ED%99%94%ED%8E%B8</link>
            <guid>https://velog.io/@dazi2_2/ROS-%EC%8B%9C%EB%AE%AC%EB%A0%88%EC%9D%B4%ED%84%B0-%EC%8B%AC%ED%99%94%ED%8E%B8</guid>
            <pubDate>Tue, 16 Jan 2024 09:21:17 GMT</pubDate>
            <description><![CDATA[<h1 id="🐢0개요">🐢0.개요</h1>
<p>앞에서 시뮬레이션 환경의 기본편을 알아보았다.
이번시간에는 자율주행을 위한 네비게이션 요소에 대해서 알아보고 실용적인 실습도 진행해본다.
예제코드를 통해 자율주행에 무엇이 필요한지 간단하게 알아보고, 자세한 활용방법에 대한 것은 차후 알아보도록 하겠다.</p>
<h1 id="🤔1자율주행에-필요한-요소">🤔1.자율주행에 필요한 요소</h1>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/67f0a325-b9e3-47c9-8e71-8d3a008cf474/image.png" alt="">
위 모델링 파일이 이번시간에서 사용될 시뮬레이션 Map이다. 
중간에 서빙로봇 <code>kobugi</code>가 위치한 것을 확인 할 수 있다.</p>
<h2 id="🗺️11-mapping">🗺️1.1 Mapping</h2>
<p>가장 먼저해야 할 일으로 자율주행 하고자 하는 맵에 대한 정보가 필요하다.
이는 로봇의 카메라 정보로 하거나 기존에 가지고 있는 정보를 통해 설정 할 수 있다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/6e5ada0b-aafc-4907-b914-d5c4c3301945/image.png" alt="">
위 화면은 <code>gmapping_demo</code>를 실행한 모습이다. 
<code>rviz</code>상에서 로봇이 인식한 맵을 확인 할 수 있고, 로봇을 움직여서 맵을 만들 수 있다.
로봇 조작은 아래의 명령어로 키보드로 조작할 수 있다.</p>
<pre><code class="language-bash">roslaunch turtlebot_teleop keyboard_teleop.launch</code></pre>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/f51d37d8-b388-46f0-9d7a-bbeb87802707/image.png" alt=""></p>
<p>로봇을 조작하여 위와 같이 mapping을 할 수 있었다.</p>
<h2 id="🛣️12-localization">🛣️1.2 Localization</h2>
<p>mapping이 끝났다고 해서 자율주행을 할 수 있을까? 정답은 그렇지 않다.
로봇의 위치나 어디를 향하는 지를 알아야 제대로 된 자율주행을 만들 수 있다.
<code>amcl</code>이라는 알고리즘을 통해 로봇의 위치를 알 수 있다. 아래의 실행화면을 보자.
<img src="https://velog.velcdn.com/images/dazi2_2/post/d0bdc8b3-b80c-438f-9434-0c13c6c9b5f5/image.gif" alt="">
화면을 보면 초록색 화살표가 보이는데 초기에는 퍼져서 분포되어 있다가 로봇을 조작하자 
로봇의 위치와 거의 흡사한 위치를 추정하는 것을 알 수 있다.
<code>amcl</code>알고리즘에 대해서는 차후 알아보자.</p>
<h2 id="🤖13-path-planning">🤖1.3 Path Planning</h2>
<p>앞서 두가지 요소를 알아봤는데, 여기에 경로계획이라는 시스템이 있어야 자율주행이 가능하다.
그 중에는 장애물을 피하고, 목적지에 도달하는 알고리즘이 필요하다. 
로봇의 목적지를 아래의 rviz 실행화면의 상단에 있는 <code>2D Nav Goal</code>을 통해 설정이 가능하다.
목적지를 설정해두고 장애물을 추가하였을 때 어떤식으로 로봇이 동작하는 지 확인해보자.
<img src="https://velog.velcdn.com/images/dazi2_2/post/81bfdf84-5614-47b5-b135-d5fd10936dee/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/c239248e-5437-4e32-a788-1913b7008674/image.gif" alt="">
로봇이 지나가는 경로에 여러가지 장애물을 추가해보았다.
장애물이 나타나는 순간 자연스럽게 경로를 꺾어 피해서 가는 모습을 확인 할 수 있었다.</p>
<p>아래의 실행화면은 장애물을 로봇이 어떻게 인식하여 회피하는 지 확인해보자.
<img src="https://velog.velcdn.com/images/dazi2_2/post/3588bbae-4e76-4181-973f-2c4cb0587b56/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/fc5b9f6f-96ae-403d-9385-0b63cd8834bf/image.gif" alt=""></p>
<p>로봇이 장애물을 확인하여 회피경로를 통해 이동하는 것을 확인할 수 있다.
실행화면을 자세히보면 초록색 경로와 빨간색 경로가 나타나는데,
초록색 경로는 전체경로, 빨간색 경로 장애물 회피경로이다.
로봇의 아래쪽에 깔린 여러가지 색상을 확인 할 수 있는데, 
로봇이 앞으로 가야하는 경로의 우선순위(빨간색 우선)를 나타낸다고 볼 수 있다.</p>
<h1 id="＊navigation-stack-알아보기">＊Navigation Stack 알아보기</h1>
<p>아래의 <code>rqt_graph</code> 실행 화면으로 각 노드가 주고받는 토픽에 대해서 알 수 있다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/9e4687fd-2b72-4119-b91e-dc108de172b5/image.png" alt=""></p>
<p>cmd_vel -&gt; 네이베이션 스텍에 출력을 받아서 모터의 속도로 변환하는 역할
LaserScan -&gt; 스캐너의 스캔값을 네이게이션에 전달하는 역할
odom -&gt; 네이게이션 스텍에 오도메트리 값 전달
tf -&gt; 네이게이션 스텍에 map을 기준으로 로봇의 위치나 센서의 tf 정보를 전달하는 역할.
move_base -&gt; 모든 정보를 모아서 로봇을 목적지까지 도달 할 수 있는 명령을 전달.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ROS] Gazebo Plugins]]></title>
            <link>https://velog.io/@dazi2_2/ROS-Gazebo-Plugins</link>
            <guid>https://velog.io/@dazi2_2/ROS-Gazebo-Plugins</guid>
            <pubDate>Mon, 15 Jan 2024 17:19:13 GMT</pubDate>
            <description><![CDATA[<h1 id="🐢0개요">🐢0.개요</h1>
<p>Gazebo Plugin이란? Gazebo 시뮬레이션 환경에서 다양한 사물의 움직임을 구현하거나, 기능을 구현하는데 사용된다.
로봇의 센서,엑추에이터, 컨트롤러를 구현하는데 활용할 수 있다. Python이나 C++로 작성 될 수 있으며,
이번시간에서 C++로 Plugin을 직접 작성해보면서 Plugin의 역할을 알아보자.</p>
<h1 id="🔌1plugin-작성-과정">🔌1.Plugin 작성 과정</h1>
<p>이번 실습에는 Gazebo의 사물을 정사각형 모양을 그리면서 움직이는 모형을 만들어 볼 예정이다.
아래의 순서대로 진행해보자.</p>
<h2 id="11-cmakelisttxt-작성하기">1.1 CMakeList.txt 작성하기</h2>
<p>프로젝트의 의존성을 추가하기 위한 CMakeList를 작성한다.
  <code>gazebo_plugins</code>,<code>gazebo_ros</code>,<code>roscpp</code> package를 추가해주고 아래와 같이 코드를 작성한다.
⛰️ CMakeList.txt</p>
<pre><code class="language-ruby">cmake_minimum_required(VERSION 3.0.2)
project(writing_plugins)

find_package(gazebo REQUIRED)

find_package(catkin REQUIRED COMPONENTS
  gazebo_plugins
  gazebo_ros
  roscpp
)

catkin_package()

include_directories(
    # include
  ${catkin_INCLUDE_DIRS}
)

include_directories(${GAZEBO_INCLUDE_DIRS})
link_directories(${GAZEBO_LIBRARY_DIRS})
list(APPEND CMAKE_CXX_FLAGS &quot;${GAZEBO_CXX_FLAGS}&quot;)

add_library(a_model_plugin SHARED src/a_model_plugin.cc)
target_link_libraries(my_gazebo_plugin ${GAZEBO_LIBRARIES})</code></pre>
<p><code>add_library</code> 라인에 본인이 작성한 플러그인 파일 (<code>a_model_plugin.cc</code>)을 넣어준다.
아래에서 <code>.cc</code>파일을 확인해보자</p>
<h2 id="12-cc-파일-작성">1.2 .CC 파일 작성</h2>
<p><code>.cc</code>파일에서는 Gazebo의 모델을 컨트롤할 코드를 작성해준다.
<code>Load</code> 함수는 플러그인이 로드 될 때 실행되는 함수이다. 클래스의 변수를 초기화 하여 Gazebo에 알려주는 역할을 한다.
여기에서는 물체의 속도 및 움직이는 시간을 입력하였다.
입력되는 변수가 없다면 초기에 선언한 값을 사용 할 수 있다.</p>
<p><code>OnUpdate</code> 함수는 Gazebo 시뮬레이션에서 각 타임스탭에서 호출되는 함수이다.
아래와 같이 정사각형으로 움직이게 하기 위해 <code>+x</code> -&gt; <code>+y</code> -&gt; <code>-x</code> -&gt; <code>-y</code> 순으로 동작하게 작성하였다.
🗒a_model_plugin.cc</p>
<pre><code class="language-cpp">#include &lt;functional&gt;
#include &lt;gazebo/common/common.hh&gt;
#include &lt;gazebo/gazebo.hh&gt;
#include &lt;gazebo/physics/physics.hh&gt;
#include &lt;ignition/math/Vector3.hh&gt;

namespace gazebo {
class AModelPlugin : public ModelPlugin {
public:
  void Load(physics::ModelPtr _parent, sdf::ElementPtr _sdf) {
    // Store the pointer to the model
    this-&gt;model = _parent;
    this-&gt;iterations = 10 * 1000;
    if (_sdf-&gt;HasElement(&quot;iterations&quot;)) {
      this-&gt;iterations = _sdf-&gt;Get&lt;int&gt;(&quot;iterations&quot;);
    }
    this-&gt;linear_vel = 0.1;
    if (_sdf-&gt;HasElement(&quot;linear_vel&quot;)) {
      this-&gt;linear_vel = _sdf-&gt;Get&lt;double&gt;(&quot;linear_vel&quot;);
    }

    // Listen to the update event. This event is broadcast every simulation
    // iteration.
    this-&gt;updateConnection = event::Events::ConnectWorldUpdateBegin(
        std::bind(&amp;AModelPlugin::OnUpdate, this));
  }

  // Called by the world update start event
public:
  void OnUpdate() {
    // Apply a small linear velocity to the model.
    if ((counter / iterations) % 4 == 0) {
      this-&gt;model-&gt;SetLinearVel(
          ignition::math::Vector3d(this-&gt;linear_vel, 0, 0));
    } else if ((counter / iterations) % 4 == 1) {
      this-&gt;model-&gt;SetLinearVel(
          ignition::math::Vector3d(0, this-&gt;linear_vel, 0));
    } else if ((counter / iterations) % 4 == 2) {
      this-&gt;model-&gt;SetLinearVel(
          ignition::math::Vector3d(-this-&gt;linear_vel, 0, 0));
    } else if ((counter / iterations) % 4 == 3) {
      this-&gt;model-&gt;SetLinearVel(
          ignition::math::Vector3d(0, -this-&gt;linear_vel, 0));
    }

    counter++;
  }
  // Pointer to the model
private:
  physics::ModelPtr model;

private:
  double linear_vel;
  int iterations;
  int counter;
  // Pointer to the update event connection
private:
  event::ConnectionPtr updateConnection;
};

// Register this plugin with the simulator
GZ_REGISTER_MODEL_PLUGIN(AModelPlugin)
} // namespace gazebo</code></pre>
<h2 id="13-world">1.3 .world</h2>
<p>world 에서 물체의 속성과 적용할 플러그인을 선언하였다.
<code>.cc</code>의 변수를 plugin tag 내에 삽입하여 해당 값을 사용 할 수 있게 한다.
🌍model.world</p>
<pre><code class="language-xml">...
        &lt;model name=&quot;a_barrier&quot;&gt;
            &lt;pose&gt;2 2 0.5 0 0 0&lt;/pose&gt;
            &lt;static&gt;false&lt;/static&gt;
            &lt;include&gt;
                &lt;uri&gt;model://person_walking&lt;/uri&gt;
            &lt;/include&gt;

            &lt;!-- import plugin --&gt;
            &lt;plugin name=&quot;liba_model_plugin&quot; filename=&quot;liba_model_plugin.so&quot;&gt;
                &lt;linear_vel&gt;0.2&lt;/linear_vel&gt;
                &lt;iterations&gt;2500&lt;/iterations&gt;
            &lt;/plugin&gt;
        &lt;/model&gt;
...</code></pre>
<h2 id="14-plugin-경로-설정">1.4 Plugin 경로 설정</h2>
<p>Gazebo에 사용할 Plugin의 경로를 알려주기 위해 터미널에 아래와 같이 입력한다.</p>
<pre><code class="language-bash">export GAZEBO_PLUGIN_PATH=${GAZEBO_PLUGIN_PATH}:~/sim_ws/devel/lib</code></pre>
<h1 id="🚗2실행화면">🚗2.실행화면</h1>
<p><code>launch</code>파일을 작성하여 실행화면을 확인해보자.
<img src="https://velog.velcdn.com/images/dazi2_2/post/db6f40d7-3d67-435c-af61-d67bf5fb837c/image.gif" alt="">
조금 속도가 느리지만 정사각형 형태를 그리면서 잘 동작하는 것을 확인 할 수 있다.</p>
<p>속도가 많이 느려서 아래와 같이 조정하였다.
<code>linear_vel</code> = 5.0 값 <code>iterations</code> = 1000</p>
<p>🌍model.world</p>
<pre><code class="language-xml">...
&lt;linear_vel&gt;5.0&lt;/linear_vel&gt;
&lt;iterations&gt;2500&lt;/iterations&gt;
...</code></pre>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/473416b1-680b-445b-854b-17ff920a1d68/image.gif" alt="빨리도는 콘"></p>
<p>아래와 같이 코드를 수정하여 물체를 사람으로 변경하여 움직이게 할 수 있다.</p>
<p>🌍model.world</p>
<pre><code class="language-xml">...
        &lt;model name=&quot;human&quot;&gt;
            &lt;pose&gt;2 2 0.5 0 0 0&lt;/pose&gt;
            &lt;static&gt;false&lt;/static&gt;
            &lt;include&gt;
                &lt;uri&gt;model://person_walking&lt;/uri&gt;
            &lt;/include&gt;

            &lt;!-- import plugin --&gt;
            &lt;plugin name=&quot;liba_model_plugin&quot; filename=&quot;liba_model_plugin.so&quot;&gt;
                &lt;linear_vel&gt;5.0&lt;/linear_vel&gt;
                &lt;iterations&gt;1000&lt;/iterations&gt;
            &lt;/plugin&gt;
        &lt;/model&gt;
...</code></pre>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/e0cd13b3-a21c-41e1-9dfe-7e45627d6417/image.gif" alt="사람"></p>
<p><code>box</code>객체를 밀어내는 사람의 모습을 확인 할 수 있었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ROS] 잡학사전]]></title>
            <link>https://velog.io/@dazi2_2/ROS-%EC%9E%A1%ED%95%99%EC%82%AC%EC%A0%84</link>
            <guid>https://velog.io/@dazi2_2/ROS-%EC%9E%A1%ED%95%99%EC%82%AC%EC%A0%84</guid>
            <pubDate>Mon, 15 Jan 2024 15:32:23 GMT</pubDate>
            <description><![CDATA[<h1 id="0개요">0.개요</h1>
<p>ROS 개발을 하면서 자주 깜빡하거나 알아두면 좋은 내용들을 순서 상관없이 정리하는 글이다.</p>
<h3 id="1gazebo-plugin-생성시-아래와-같이-cmake파일을-추가해야한다">1.Gazebo Plugin 생성시 아래와 같이 CMake파일을 추가해야한다.</h3>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/055d7fe6-2200-472c-8d3b-ce082a7dd57f/image.png" alt=""></p>
<h3 id="2bashrc-파일에서-단축-터미널-명령어를-alias를-통해-정의-할-수-있다">2.<code>.bashrc</code> 파일에서 단축 터미널 명령어를 alias를 통해 정의 할 수 있다.</h3>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/2a5ddfcd-0a9c-461b-8302-84a08c202ed6/image.png" alt="">
자주쓰는 명령어가 있다면 추가해서 활용해보자!</p>
<h3 id="3-py-파일에-권한-부여하기">3. .py 파일에 권한 부여하기.</h3>
<p>rosrun에 쓸 <code>.py</code>파일은 아래의 명령어로 반드시 권한을 부여해줘야한다.</p>
<pre><code class="language-terminal">chmod +x *.py</code></pre>
<h3 id="4-새로운-워크-스페이스-추가시-해야할-것">4. 새로운 워크 스페이스 추가시 해야할 것</h3>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/5f51674d-7d32-45c9-81a7-4d11500ef73d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/29470671-d45b-4a57-8283-5f53fa80b60a/image.png" alt=""></p>
<h3 id="5-rqt-quaternions-값">5. rqt quaternions 값</h3>
<blockquote>
<p><a href="https://eater.net/quaternions/video/intro">https://eater.net/quaternions/video/intro</a></p>
</blockquote>
<h2 id="용어집">용어집</h2>
<ol>
<li><code>odomety</code> : 거래주행계, 로봇이 기준점으로 부터 얼마나 이동했는지를 나타내는 지표</li>
<li><code>TF</code> : Transform 으로 한 프레임에 표현된 데이터를 다른 프레임으로 변환하는 방법 </li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ROS] 카메라 플러그인 적용하기]]></title>
            <link>https://velog.io/@dazi2_2/ROS-%EC%B9%B4%EB%A9%94%EB%9D%BC-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dazi2_2/ROS-%EC%B9%B4%EB%A9%94%EB%9D%BC-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 13 Jan 2024 04:12:54 GMT</pubDate>
            <description><![CDATA[<h1 id="🐣0개요">🐣0.개요</h1>
<p>지난 시간에 Gazebo에서 사용할 수 있는 <code>Tag</code> 및 <code>Plugin</code>, 적용방법에 대해서 학습하였다. 
그리고 <code>world</code> 파일 <code>tag</code> <code>population</code>에 대해서 학습하면서 규칙을 생성하여 월드를 만드는 방법에 대해서 학습하였다.
이번 시간에는 카메라 모듈을 적용한 로봇 모델을 지난 시간에 만든 <code>world</code>에 배치하여 <code>Camera Plugin</code>의 동작 화면을 확인하는게 목표이다. 
아래의 링크에서 <code>Camera</code> 플러그인 적용법을 참조 할 수 있었다. </p>
<p>[Gazebo plugins in ROS] - <a href="https://classic.gazebosim.org/tutorials?tut=ros_gzplugins#Camera">https://classic.gazebosim.org/tutorials?tut=ros_gzplugins#Camera</a></p>
<h1 id="🗒1코드-작성">🗒1.코드 작성</h1>
<h2 id="📒11-xacro-파일-작성">📒1.1 .xacro 파일 작성</h2>
<p>카메라의 형태를 정의해준다. 
카메라의 위치는 이전에 만든 Ladar 위에 위치시키기 위해 <code>parent link</code>를 해당 링크로 정의하였다.
<code>joint</code>의 위치를 <code>parent link</code>의 z축으로 0.1m 위치한 곳으로 선언하였다.
<code>xacro:property</code>를 통해 정육면체 모양의 카메라의 길이를 선언하였다.
🤖robot.xacro</p>
<pre><code>...
    &lt;xacro:property name=&quot;camera_link&quot; value=&quot;0.05&quot; /&gt;
...
    &lt;!-- camera_joint --&gt;
    &lt;joint name=&quot;camera_joint&quot; type=&quot;fixed&quot;&gt;
        &lt;axis xyz=&quot;0 1 0&quot; /&gt;
        &lt;origin rpy=&quot;0 0 0&quot; xyz=&quot;0 0 0.1&quot;/&gt;
        &lt;parent link=&quot;link_laser_scan&quot;/&gt;
        &lt;child link=&quot;camera_link&quot;/&gt;
    &lt;/joint&gt;

    &lt;!-- Camera --&gt;
    &lt;link name=&quot;camera_link&quot;&gt;
        &lt;collision&gt;
            &lt;origin xyz=&quot;0 0 0&quot; rpy=&quot;0 0 0&quot;/&gt;
            &lt;geometry&gt;
                &lt;box size=&quot;${camera_link} ${camera_link} ${camera_link}&quot;/&gt;
            &lt;/geometry&gt;
        &lt;/collision&gt;

        &lt;visual&gt;
            &lt;origin xyz=&quot;0 0 0&quot; rpy=&quot;0 0 0&quot;/&gt;
                &lt;geometry&gt;
                    &lt;box size=&quot;${camera_link} ${camera_link} ${camera_link}&quot;/&gt;
                &lt;/geometry&gt;
        &lt;/visual&gt;

        &lt;inertial&gt;
            &lt;mass value=&quot;1e-5&quot; /&gt;
            &lt;origin xyz=&quot;0 0 0&quot; rpy=&quot;0 0 0&quot;/&gt;
            &lt;inertia ixx=&quot;1e-6&quot; ixy=&quot;0&quot; ixz=&quot;0&quot; iyy=&quot;1e-6&quot; iyz=&quot;0&quot; izz=&quot;1e-6&quot; /&gt;
        &lt;/inertial&gt;
    &lt;/link&gt;
...</code></pre><p><img src="https://velog.velcdn.com/images/dazi2_2/post/88f98fb5-4c49-4fc9-adef-998695914ce8/image.png" alt=""></p>
<p>카메라의 색상은 <code>robot.gazebo</code>에서 적용하였다. 
🏛️robot.gazebo</p>
<pre><code>...
    &lt;gazebo reference=&quot;camera_link&quot;&gt;
    &lt;material&gt;Gazebo/Red&lt;/material&gt;
    &lt;/gazebo&gt;
...</code></pre><h2 id="🔌12-gazebo-파일-camera-plugin-적용">🔌1.2 .gazebo 파일 Camera plugin 적용</h2>
<p><code>.gazebo</code> 파일에 <code>Camera</code> <code>plugin</code>을 사용하기 위한 코드를 아래와 같이 추가하였다.</p>
<p>🏛️robot.gazebo</p>
<pre><code>...
&lt;gazebo reference=&quot;camera_link&quot;&gt;
        &lt;sensor type=&quot;camera&quot; name=&quot;camera1&quot;&gt;
        &lt;update_rate&gt;30.0&lt;/update_rate&gt;
        &lt;camera name=&quot;head&quot;&gt;
            &lt;horizontal_fov&gt;1.3962634&lt;/horizontal_fov&gt;
            &lt;image&gt;
                &lt;width&gt;800&lt;/width&gt;
                &lt;height&gt;800&lt;/height&gt;
                &lt;format&gt;R8G8B8&lt;/format&gt;
            &lt;/image&gt;
            &lt;clip&gt;
                &lt;near&gt;0.02&lt;/near&gt;
                &lt;far&gt;300&lt;/far&gt;
            &lt;/clip&gt;
            &lt;noise&gt;
                &lt;type&gt;gaussian&lt;/type&gt;
                &lt;!-- Noise is sampled independently per pixel on each frame.
                    That pixel&#39;s noise value is added to each of its color
                    channels, which at that point lie in the range [0,1]. --&gt;
                &lt;mean&gt;0.0&lt;/mean&gt;
                &lt;stddev&gt;0.007&lt;/stddev&gt;
            &lt;/noise&gt;
        &lt;/camera&gt;
        &lt;plugin name=&quot;camera_controller&quot; filename=&quot;libgazebo_ros_camera.so&quot;&gt;
            &lt;alwaysOn&gt;true&lt;/alwaysOn&gt;
            &lt;updateRate&gt;0.0&lt;/updateRate&gt;
            &lt;cameraName&gt;rrbot/camera1&lt;/cameraName&gt;
            &lt;imageTopicName&gt;image_raw&lt;/imageTopicName&gt;
            &lt;cameraInfoTopicName&gt;camera_info&lt;/cameraInfoTopicName&gt;
            &lt;frameName&gt;camera_link&lt;/frameName&gt;
            &lt;hackBaseline&gt;0.07&lt;/hackBaseline&gt;
            &lt;distortionK1&gt;0.0&lt;/distortionK1&gt;
            &lt;distortionK2&gt;0.0&lt;/distortionK2&gt;
            &lt;distortionK3&gt;0.0&lt;/distortionK3&gt;
            &lt;distortionT1&gt;0.0&lt;/distortionT1&gt;
            &lt;distortionT2&gt;0.0&lt;/distortionT2&gt;
        &lt;/plugin&gt;
        &lt;/sensor&gt;
    &lt;/gazebo&gt;
...</code></pre><p><code>.gazebo</code>에서 선언한 <code>camera_link</code>에 <code>camera</code> 플러그인을 적용하는 코드이다.
각각의 <code>Tag</code>의 역할에 대해서 알아보자.
<code>sensor</code> : 센서를 정의한다. 위코드에서는 <code>camera</code> 센서의 속성 값 및 플러그인이 포함되어 있는 <code>Tag</code>이다.
<code>updata_rate</code> : 카메라 센서의 프레임을 선언 (FPS: Frame per Second).
<code>horizontal_fov</code> : 카메라의 시야각을 나타낸다. 위 코드의 선언값은 <code>1.3962634</code> 약 80도의 시야각을 나타낸다.
<code>image</code> : 카메라의 해상도 및 형식을 지정한다. (800 * 800) , R8G8B8 은 RGB의 각각의 값들이 8비트로 표현된다는 뜻이다.
<code>clip</code> : 카메라의 클리핑을 설정한다. 카메라의 최대 거리 및 최소 거리를 지정 할 수 있다.
<code>noise</code> : 사진의 노이즈를 추가한다. 노이즈를 추가하는 이유는 현실의 카메라는 노이즈가 존재하기 때문에 시뮬레이션 환경에서 불확실성을 추가하여 현실과 비슷한 시뮬레이션을 진행 할 수 있게 한다. <code>gaussian</code> 노이즈를 추가하였고 표준편차 및 평균 값을 지정할 수 있다. </p>
<p>❓<code>gaussian</code> noise 란 - 가우스 분포를 나타내는 잡음으로 이미지 처리를 할 때 주로 나타나는 노이즈. 차후 자세히 다뤄보겠다.</p>
<p><code>plugin</code> : 플러그인 내용으로 <code>libgazebo_ros_camera.so</code> 플러그인을 사용한다.
내부의 내용은 카메라의 이름, 주기, 발행토픽이름, 왜곡 등을 설정 할 수 있다.</p>
<h1 id="✈️2실행화면">✈️2.실행화면</h1>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/8370b750-f894-4435-af2d-fad83bd2ded1/image.gif" alt=""></p>
<p>전에 작업한 world 에 모형 로봇을 소환하여 카메라 시점을 확인 할 수 있었다.
로봇이 움직이면서 카메라의 시점이 바뀌는 데,
카메라 시야각에 보이는 물체들을 잡아내는 것을 확인 할 수 있다.</p>
<p>실행과정은 아래와 같다.
1.world 실행</p>
<pre><code>$roslaunch robot_description empty_world.launch</code></pre><p>2.spawn robot</p>
<pre><code>$roslaunch robot_description spawn.launch</code></pre><p>3.동작 명령</p>
<pre><code>$rosrun move_with_laser.py</code></pre><p>4.rviz 에서 카메라 토픽 기반으로 만들어진 이미지 확인</p>
<pre><code>$roslaunch robot_description rviz.launch</code></pre><p><img src="https://velog.velcdn.com/images/dazi2_2/post/9214eee3-2916-48a5-a095-448776d4f0ff/image.png" alt=""></p>
<p>rqt_graph 를 통해 토픽의 흐름을 확인 할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ROS] Gazebo population 알아보기]]></title>
            <link>https://velog.io/@dazi2_2/ROS-Gazebo-population-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@dazi2_2/ROS-Gazebo-population-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Fri, 12 Jan 2024 15:33:31 GMT</pubDate>
            <description><![CDATA[<h1 id="👀0개요">👀0.개요</h1>
<p>world를 생성할때 <code>population</code> tag를 사용하면 오브젝트를 하나씩 배치하지 않고 일정한 규칙을 지정하여 생성하거나 랜덤으로 배치 할 수 있는 기능을 가진다. 실습을 통해 <code>population</code>의 사용법을 알아보자.</p>
<h1 id="🚙1실습-진행">🚙1.실습 진행</h1>
<p><code>population</code> tag를 이용하여 <code>world</code> 파일을 작성 한 뒤 gazebo에서 어떤 식으로 나타나는지 확인하고 각각의 코드가 어떤식으로 동작하는지 알아보겠다.</p>
<h2 id="🗒11-코드">🗒1.1 코드</h2>
<p>🌍population.world</p>
<pre><code>&lt;?xml version=&quot;1.0&quot; ?&gt;
&lt;sdf version=&quot;1.5&quot;&gt;
    &lt;world name=&quot;default&quot;&gt;

        &lt;!-- A global light source --&gt;
        &lt;include&gt;
            &lt;uri&gt;model://sun&lt;/uri&gt;
        &lt;/include&gt;

        &lt;!-- A ground plane --&gt;
        &lt;include&gt;
            &lt;uri&gt;model://ground_plane&lt;/uri&gt;
        &lt;/include&gt;

        &lt;!-- +X barriers --&gt;
        &lt;population name=&quot;barriers_population&quot;&gt;
            &lt;model name=&quot;jersey_barrier_1&quot;&gt;
                &lt;include&gt;
                    &lt;static&gt;true&lt;/static&gt;
                    &lt;uri&gt;model://jersey_barrier&lt;/uri&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;0 19 0.3 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;40 40 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;10&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;linear-x&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;

        &lt;!-- -X barriers --&gt;
        &lt;population name=&quot;barriers_population&quot;&gt;
            &lt;model name=&quot;jersey_barrier_2&quot;&gt;
                &lt;include&gt;
                    &lt;static&gt;true&lt;/static&gt;
                    &lt;uri&gt;model://jersey_barrier&lt;/uri&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;0 -21.5 0.3 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;40 40 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;10&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;linear-x&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;

        &lt;!-- +Y barriers --&gt;
        &lt;population name=&quot;barriers_population&quot;&gt;
            &lt;model name=&quot;jersey_barrier_3&quot;&gt;
                &lt;include&gt;
                    &lt;static&gt;true&lt;/static&gt;
                    &lt;uri&gt;model://jersey_barrier&lt;/uri&gt;
                    &lt;pose&gt;0 0 0 0 0 1.57&lt;/pose&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;19 0 0.3 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;40 40 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;10&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;linear-y&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;

        &lt;!-- -Y barriers --&gt;
        &lt;population name=&quot;barriers_population&quot;&gt;
            &lt;model name=&quot;jersey_barrier_4&quot;&gt;
                &lt;include&gt;
                    &lt;static&gt;true&lt;/static&gt;
                    &lt;uri&gt;model://jersey_barrier&lt;/uri&gt;
                    &lt;pose&gt;0 0 0 0 0 1.57&lt;/pose&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;-21.5 0 0.3 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;40 40 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;10&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;linear-y&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;


        &lt;!-- random models population --&gt;
        &lt;population name=&quot;construction_barrel_population&quot;&gt;
            &lt;model name=&quot;construction_barrel&quot;&gt;
                &lt;include&gt;
                &lt;static&gt;false&lt;/static&gt;
                &lt;uri&gt;model://construction_barrel&lt;/uri&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;0 0 0.2 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;30 30 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;100&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;random&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;

    &lt;/world&gt;
&lt;/sdf&gt;</code></pre><h2 id="💻12-gazebo-화면">💻1.2 Gazebo 화면</h2>
<h3 id="1️⃣121-첫번째-실행">1️⃣1.2.1 첫번째 실행</h3>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/09a126c4-92d6-491d-994a-121ff60a4bfc/image.png" alt=""></p>
<h3 id="2️⃣122-두번재-실행">2️⃣1.2.2 두번재 실행</h3>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/44c9079c-63bf-4fc2-89f1-f0713cdc63f1/image.png" alt=""></p>
<h1 id="🤔2-코드-분석">🤔2. 코드 분석</h1>
<p>실행화면을 보면 사각형 형태로 둘러싼 바리케이드와 내부에 꼬깔이 여러개 배치되어 있는 것을 확인 할 수 있다.
아래에 <code>population</code>에서 사용가능한 <code>tag</code>를 설명하였고, 바리케이트 및 꼬깔이 어떤식으로 배치되었는지 살펴보자.
<strong>🗒테그 목록</strong></p>
<blockquote>
</blockquote>
<p>1.<code>population</code> - <code>population</code> 객체의 이름을 지정할 수 있다.
2. <code>model</code> - 객체의 모델을 정의하는데 사용된다.
3. <code>include</code> - 모델에 필요한 소스를 불러오거나 ,속성을 정의 하는데 사용된다.
4. <code>pose</code> - 시뮬레이션에서 오브젝트의 초기위치 또는 방향을 설정한다.
5. <code>box</code> - 시뮬레이션에서 객체가 위치할 영역을 설정한다.
6. <code>model_count</code> - 시뮬레이션에서 만들 모델의 수를 정의한다.
7. <code>distribution</code> - 개체를 배포할 방법을 지정한다 (ex : random , uniform, grid, linear-x,linear-y,linear-z)</p>
<p>*<em>🚧바리케이스 생성코드 *</em></p>
<pre><code>        &lt;population name=&quot;barriers_population&quot;&gt;
            &lt;model name=&quot;jersey_barrier_1&quot;&gt;
                &lt;include&gt;
                    &lt;static&gt;true&lt;/static&gt;
                    &lt;uri&gt;model://jersey_barrier&lt;/uri&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;0 19 0.3 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;40 40 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;10&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;linear-x&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;</code></pre><p>우선 <code>population</code>의 name은 <code>barriers_population</code>이고 , 
<code>model</code> name 은 <code>jersey_barrier_1</code>이다.
<code>static</code> 이 <code>true</code>이므로 고정된 성질을 가진다.
<code>uri</code>에 해당되는 모델 <code>jersey_barrier</code>을 불러온다.
<code>pose</code>모델의 초기 위치는 <code>xyz</code> = 0 19 0.3 <code>pry</code> = 0 0 0 으로 회전 값은 따로 부여하지 않았다.
<code>box</code> 테그는 객체가 위치할 영역으로 40*40 으로 설정하였다.
<code>model_count</code> 값이 10이므로 10개의 바리케이트가 설치될 것이다.
<code>distribution</code> == linear-x 이므로 x축으로 일렬로 배열한다.</p>
<p>해당 내용으로 x축 및 y축으로 총 4개의 <code>population</code>의 <code>pose</code>, <code>distribution</code> 값을 바꾸면서 구성할 수 있었고 , y축 바리케이트는 <code>include</code> 의 <code>pose</code> 중 <code>pry</code> = 0 0 1.57 을 부여해 모델이 Y축으로 향하게 배치할 수 있었다.</p>
<p>위 실행화면을 보면 첫번째 실행화면과 두번째 실행화면의 바리케이트 배치는 같으나 꼬깔의 배치가 다른 것을 확인 할 수 있다.
그이유는 아래의 코드를 보면서 확인해보자.</p>
<p>*<em>🚧꼬깔 생성코드 *</em></p>
<pre><code>        &lt;!-- random models population --&gt;
        &lt;population name=&quot;construction_barrel_population&quot;&gt;
            &lt;model name=&quot;construction_barrel&quot;&gt;
                &lt;include&gt;
                &lt;static&gt;false&lt;/static&gt;
                &lt;uri&gt;model://construction_barrel&lt;/uri&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;0 0 0.2 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;30 30 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;100&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;random&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;</code></pre><p>차이점은 <code>distribution</code> 이부분의 값이 <code>random</code>으로 설정되어있기 때문이다. 
<code>random</code> 값을 부여하면 지정된 범위 내에 랜덤으로 해당 객체를 생성하게 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ROS] Gazebo model 제작하기]]></title>
            <link>https://velog.io/@dazi2_2/ROS-Gazebo-model-%EC%A0%9C%EC%9E%91%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dazi2_2/ROS-Gazebo-model-%EC%A0%9C%EC%9E%91%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 11 Jan 2024 12:18:21 GMT</pubDate>
            <description><![CDATA[<h1 id="🚙1model-구성-살펴보기">🚙1.Model 구성 살펴보기.</h1>
<p>Gazebo에서 사용할 3D모델을 직접 구성하여 여러가지 속성의 동작에 대해서 알아보자.
박스형태의 간단한 모델을 제작해보자.</p>
<h2 id="🚖11-sdf-파일">🚖1.1 SDF 파일</h2>
<p>🚖model.sdf</p>
<pre><code>&lt;?xml version=&#39;1.0&#39;?&gt;
&lt;sdf version=&quot;1.4&quot;&gt;
    &lt;model name=&quot;box_model&quot;&gt;
        &lt;pose&gt;0 0 0.5 0 0 0&lt;/pose&gt;
        &lt;static&gt;false&lt;/static&gt;
        &lt;link name=&quot;link&quot;&gt;
            &lt;inertial&gt;
                &lt;mass&gt;1.0&lt;/mass&gt;
                &lt;inertia&gt;
                    &lt;ixx&gt;0.083&lt;/ixx&gt;
                    &lt;ixy&gt;0.0&lt;/ixy&gt;
                    &lt;ixz&gt;0.0&lt;/ixz&gt;
                    &lt;iyy&gt;0.083&lt;/iyy&gt;
                    &lt;iyz&gt;0.0&lt;/iyz&gt;
                    &lt;izz&gt;0.083&lt;/izz&gt;
                &lt;/inertia&gt;
            &lt;/inertial&gt;
            &lt;collision name=&quot;collision&quot;&gt;
                &lt;geometry&gt;
                    &lt;box&gt;
                        &lt;size&gt;1 1 1&lt;/size&gt;
                    &lt;/box&gt;
                &lt;/geometry&gt;
            &lt;/collision&gt;
            &lt;visual name=&quot;visual1&quot;&gt;
                &lt;geometry&gt;
                    &lt;box&gt;
                        &lt;size&gt;1 1 1&lt;/size&gt;
                    &lt;/box&gt;
                &lt;/geometry&gt;
            &lt;/visual&gt;
            &lt;visual name=&quot;visual2&quot;&gt;
                &lt;pose&gt;0 0 1 0 0 0&lt;/pose&gt;
                &lt;geometry&gt;
                    &lt;cylinder radius=&quot;0.25&quot; length=&quot;1.5&quot;/&gt;
                &lt;/geometry&gt;
            &lt;/visual&gt;
        &lt;/link&gt;
    &lt;/model&gt;
&lt;/sdf&gt;</code></pre><p>sdf파일에는 urdf와 비슷하게 물체의 속성 (무게, 크기, 재질 등)을 결정하는 코드를 작성 할 수 있다.
위 코드 같은 경우 아래의 사진과 같은 모델을 gazebo에서 확인 할 수 있다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/aa4eec07-3b3f-404c-b289-0b0d1bf19422/image.png" alt=""></p>
<p>sdf 파일은 <code>collision</code>속성과 <code>visual</code>속성이 존재한다.
<code>collision</code>은 말그대로 충돌 범위를 설정 할 수 있고, 
<code>visual</code>은 시각적인 정보만 담고 있다. 그렇기 때문에 어떤 객체에 <code>visual</code>만 설정하고 <code>collision</code>속성을 같게하여
보이는 것과 충돌하게 할 수도 있고 <code>visual</code>만 설정하여 보이지만 충돌하지 않는 객체를 생성 할 수 있다.
이러한 점을 위에서 만든 모델로 확인해보자.
코드의 <code>collision</code>이라는 이름을 가지는 충돌범위와 <code>visual</code>를 동일하게 정의하였고,
<code>visual2</code>의 경우 <code>visual</code> 만 정의하였다.</p>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/d559fd0d-9e14-4d1d-8790-8d98e22efcc3/image.gif" alt=""></p>
<p>그렇기 때문에 해당 model 위에 자동차 모형을 떨어뜨리면 실린더 모양의 구조물을 통과하는 모습을 확인 할 수 있다.</p>
<h2 id="⚙️12-config-파일">⚙️1.2 Config 파일</h2>
<p>⚙️model.config</p>
<pre><code>&lt;?xml version=&quot;1.0&quot;?&gt;

&lt;model&gt;
    &lt;name&gt;Box Model&lt;/name&gt;
    &lt;version&gt;1.0&lt;/version&gt;
    &lt;sdf version=&quot;1.6&quot;&gt;model.sdf&lt;/sdf&gt;

    &lt;author&gt;
        &lt;name&gt;Nodazi&lt;/name&gt;
        &lt;email&gt;dk3146@naver.com&lt;/email&gt;
    &lt;/author&gt;

    &lt;description&gt;
        A simple box model
    &lt;/description&gt;
&lt;/model&gt;</code></pre><p>Config 파일은 해당 모델의 <code>name</code>, <code>version</code> ,<code>author</code>등 의 요소를 기입할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ROS] ROS 연동하기]]></title>
            <link>https://velog.io/@dazi2_2/ROS-ROS-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dazi2_2/ROS-ROS-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 10 Jan 2024 11:58:22 GMT</pubDate>
            <description><![CDATA[<h1 id="🐣1gazebo의-ros-노드">🐣1.Gazebo의 Ros 노드</h1>
<p>Gazebo에서 여러가지동작을 하는 명령어를 터미널을 이용 할 수 있다.
아래의 코드에 추가로 덧붙혀서 사용할 수 있으며 각각의 기능에 대해서 알아보자.</p>
<pre><code>$rosservice call gazebo/ </code></pre><pre><code>pause_phsics</code></pre><p>gazebo의 모든 객체들이 멈추게 된다.</p>
<pre><code>unpause_phsics</code></pre><p>일시정지가 해제된다.</p>
<pre><code>reset_simulator</code></pre><p>모든 오브젝트의 위치를 초기 설정으로 되돌린다.</p>
<pre><code>reset_world</code></pre><p>모든 오브젝트의 위치를 초기 설정으로 되돌린다.(시뮬레이션 시간 포함)</p>
<h1 id="👀2gazebo-tag">👀2.Gazebo Tag</h1>
<p><code>urdf</code> 파일에서 gazebo 속성을 추가하여 gazebo 시뮬레이션에 적용될 속성을 추가 할 수 있다. </p>
<h2 id="21-tag로-색상-부여하기">2.1 Tag로 색상 부여하기</h2>
<p>지난 시간에 <code>urdf</code> 파일의 내용을 수정하여 로봇의 구성 및 생상을 변경하였었다. 하지만 Rviz상에서 잘 적용되던 색상같은경우 Gazebo 환경에서 로봇을 소환하였을 때 색이 없는 것을 확인 할 수 있었다. </p>
<p><strong>아래의 사진과 같이gazebo 상에서 색상이 없는 모습</strong>
<img src="https://velog.velcdn.com/images/dazi2_2/post/247f10cd-921b-4d78-984f-2abcf2d3593c/image.png" alt=""></p>
<p>이를 해결하기 위해서는 <code>urdf</code>파일에 gazebo tag를 입력하여 원하는 색상 및 해당 객체의 여러가지 값들을 변경 할 수 있다.
코드를 입력하여 물체의 색상을 넣어보고, 마찰계수를 넣는 코드를 작성해보자.</p>
<p>*<em>🤖robot.urdf *</em></p>
<pre><code class="language-xml">...
    &lt;gazebo reference=&quot;link_chassis&quot;&gt;
        &lt;material&gt; Gazebo/Purple &lt;/material&gt;
    &lt;/gazebo&gt;
    &lt;gazebo reference=&quot;link_caster_wheel&quot;&gt;
        &lt;material&gt; Gazebo/Grey &lt;/material&gt;
    &lt;/gazebo&gt;
    &lt;gazebo reference=&quot;link_left_wheel&quot;&gt;
        &lt;material&gt; Gazebo/Black &lt;/material&gt;
    &lt;/gazebo&gt;
    &lt;gazebo reference=&quot;link_right_wheel&quot;&gt;
        &lt;material&gt; Gazebo/Black &lt;/material&gt;
    &lt;/gazebo&gt;
...</code></pre>
<p><strong>색상 테그를 추가한 화면</strong>
<img src="https://velog.velcdn.com/images/dazi2_2/post/f90e87e4-4797-4b7d-9e20-908fed0aa307/image.png" alt=""></p>
<p><strong>gazebo material 확인</strong></p>
<pre><code>$vim /usr/share/gazebo-9/media/materials/scripts/gazebo.material</code></pre><p>위 명령어를 통해 위 코드의 색상에 사용된 <code>Gazebo/Black</code> 과 같이 gazebo tag에서 사용할 수 있는 material 을 확인 할 수 있다.</p>
<h2 id="22-tag로-물체-마찰계수-부여하기">2.2 Tag로 물체 마찰계수 부여하기</h2>
<p>추가로 로봇 모델에 <code>tag</code>를 추가하여 마찰 설정을 해보자.
*<em>🤖robot.urdf *</em></p>
<pre><code class="language-xml">...
    &lt;gazebo reference=&quot;link_chassis&quot;&gt;
        &lt;material&gt; Gazebo/Purple &lt;/material&gt;
    &lt;/gazebo&gt;
    &lt;gazebo reference=&quot;link_caster_wheel&quot;&gt;
        &lt;material&gt; Gazebo/Grey &lt;/material&gt;
        &lt;mu1&gt;0&lt;/mu1&gt;
        &lt;mu2&gt;0&lt;/mu2&gt;
    &lt;/gazebo&gt;
    &lt;gazebo reference=&quot;link_left_wheel&quot;&gt;
        &lt;material&gt; Gazebo/Black &lt;/material&gt;
        &lt;mu1&gt;1&lt;/mu1&gt;
        &lt;mu2&gt;1&lt;/mu2&gt;
    &lt;/gazebo&gt;
    &lt;gazebo reference=&quot;link_right_wheel&quot;&gt;
        &lt;material&gt; Gazebo/Black &lt;/material&gt;
        &lt;mu1&gt;1&lt;/mu1&gt;
        &lt;mu2&gt;1&lt;/mu2&gt;
    &lt;/gazebo&gt;
...</code></pre>
<p><strong>Force를 가하여 움직이는 모습</strong>
<img src="https://velog.velcdn.com/images/dazi2_2/post/bbfcf9be-0b84-4f8f-b1a6-f21bf15d11cd/image.gif" alt=""></p>
<h1 id="🔌3-ros-plugin">🔌3. ROS Plugin</h1>
<p>플러그인을 사용하면 Gazebo에서 유용한 기능을 추가할 수 있다. </p>
<h2 id="31-robot-differential-driver">3.1 Robot Differential Driver</h2>
<h3 id="311-코드-작성">3.1.1 코드 작성</h3>
<p>🐍move_simple.py</p>
<pre><code>#! /usr/bin/env python

import rospy
from geometry_msgs.msg import Twist
from nav_msgs.msg import Odometry

def odom_callback(msg):
        rospy.loginfo(&#39;X: %s / Y: %s&#39; % (msg.pose.pose.position.x,msg.pose.pose.position.y))

def main():
    odom_sub = rospy.Subscriber(&#39;odom&#39;,Odometry,odom_callback)
    twist_pub = rospy.Publisher(&#39;cmd_vel&#39;, Twist,queue_size=10)

    rospy.init_node(&#39;move_simple_node&#39;, anonymous=True)
    rate = rospy.Rate(10)

    while not rospy.is_shutdown():
        vel_msg = Twist()
        vel_msg.linear.x = 0.3
        vel_msg.linear.z = 0.3

        twist_pub.publish(vel_msg)
        rate.sleep()

if __name__==&#39;__main__&#39;:
    try:
        main()
    except rospy.ROSInterruptException:
        pass</code></pre><h3 id="312-실행화면">3.1.2 실행화면</h3>
<p>위 파이썬 코드를 실행시키면 아래와 같이 로봇이 움직이게 된다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/c68a53b4-ce13-411e-8d28-051ba3561e43/image.gif" alt=""></p>
<p>X,Y 좌표정보가 갱신되는 것도 확인 할 수 있다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/0f0dfde3-11a1-4269-95a9-8998ae456cec/image.png" alt=""></p>
<h2 id="32-robot-laser-sensor">3.2 Robot Laser Sensor</h2>
<h3 id="321-코드-작성">3.2.1 코드 작성</h3>
<p>*<em>🤖robot.urdf *</em></p>
<pre><code>...
&lt;!-- Gazebo tags - Laser scan --&gt;
    &lt;gazebo reference=&quot;joint_laser_scan_chassis&quot;&gt;
        &lt;preserveFixedJoint&gt;true&lt;/preserveFixedJoint&gt;
    &lt;/gazebo&gt;
    &lt;gazebo reference=&quot;link_laser_scan&quot;&gt;
        &lt;material&gt;Gazebo/DarkGrey&lt;/material&gt;
    &lt;/gazebo&gt;

    &lt;!-- Laser scan --&gt;
    &lt;joint name=&quot;joint_laser_scan_chassis&quot; type=&quot;fixed&quot;&gt;
        &lt;origin rpy=&quot;0 0 0&quot; xyz=&quot;0.8 0 0.3&quot; /&gt;
        &lt;child link=&quot;link_laser_scan&quot; /&gt;
        &lt;parent link=&quot;link_chassis&quot; /&gt;
        &lt;joint_properties damping=&quot;1.0&quot; friction=&quot;1.0&quot; /&gt;
    &lt;/joint&gt;
    &lt;link name=&quot;link_laser_scan&quot;&gt;
        &lt;inertial&gt;
            &lt;origin xyz=&quot;0 0 0&quot; rpy=&quot;0 0 0&quot; /&gt;
            &lt;mass value=&quot;0.5&quot; /&gt;
            &lt;inertia ixx=&quot;0.000252666666667&quot; ixy=&quot;0&quot; ixz=&quot;0&quot; iyy=&quot;0.000252666666667&quot; iyz=&quot;0&quot; izz=&quot;0.0005&quot;/&gt;
        &lt;/inertial&gt;
        &lt;visual&gt;
            &lt;origin xyz=&quot;0 0 0&quot; rpy=&quot;0 0 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;cylinder radius=&quot;0.15&quot; length=&quot;0.20&quot;/&gt;
            &lt;/geometry&gt;
            &lt;material name=&quot;Red&quot;&gt;
                &lt;color rgba=&quot;0.7 0.1 0.1 1&quot; /&gt;
            &lt;/material&gt;
        &lt;/visual&gt;
        &lt;collision&gt;
            &lt;origin xyz=&quot;0 0 0&quot; rpy=&quot;0 0 0&quot;/&gt;
            &lt;geometry&gt;
                &lt;cylinder radius=&quot;0.15&quot; length=&quot;0.20&quot;/&gt;
            &lt;/geometry&gt;
        &lt;/collision&gt;
    &lt;/link&gt;

    &lt;gazebo reference=&quot;link_laser_scan&quot;&gt;
        &lt;sensor type=&quot;ray&quot; name=&quot;head_hokuyo_sensor&quot;&gt;
            &lt;pose&gt;0 0 0 0 0 0&lt;/pose&gt;
            &lt;visualize&gt;true&lt;/visualize&gt;
            &lt;update_rate&gt;20&lt;/update_rate&gt;
            &lt;ray&gt;
                &lt;scan&gt;
                    &lt;horizontal&gt;
                        &lt;samples&gt;720&lt;/samples&gt;
                        &lt;resolution&gt;1&lt;/resolution&gt;
                        &lt;min_angle&gt;-1.570796&lt;/min_angle&gt;
                        &lt;max_angle&gt;1.570796&lt;/max_angle&gt;
                    &lt;/horizontal&gt;
                &lt;/scan&gt;
                &lt;range&gt;
                    &lt;min&gt;0.20&lt;/min&gt;
                    &lt;max&gt;10.0&lt;/max&gt;
                    &lt;resolution&gt;0.01&lt;/resolution&gt;
                &lt;/range&gt;
                &lt;noise&gt;
                    &lt;type&gt;gaussian&lt;/type&gt;
                    &lt;mean&gt;0.0&lt;/mean&gt;
                    &lt;stddev&gt;0.01&lt;/stddev&gt;
                &lt;/noise&gt;
            &lt;/ray&gt;
            &lt;plugin name=&quot;gazebo_ros_head_hokuyo_controller&quot; filename=&quot;libgazebo_ros_laser.so&quot;&gt;
                &lt;topicName&gt;/laser/scan&lt;/topicName&gt;
                &lt;frameName&gt;sensor_laser&lt;/frameName&gt;
            &lt;/plugin&gt;
        &lt;/sensor&gt;
    &lt;/gazebo&gt;
...</code></pre><p>urdf 파일에 Laser sensor Plugin에 대한 정보를 입력하고 , Ladar 센서 형태를 로봇의 전면에 생성한다. 아래의 사진과 같이 센서의 범위를 눈으로 확인 할 수 있다. visualize 설정을 통해 레이저를 보이지 않게 할 수도 있다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/24cae50f-1b63-4176-8be7-b55ddb18811d/image.png" alt=""></p>
<p><strong>🐍move_with_laser.py</strong></p>
<pre><code>#!/usr/bin/env python

import rospy
from geometry_msgs.msg import Twist
from sensor_msgs.msg import LaserScan

def laser_callback(msg):
    rospy.loginfo(&#39;Minimum distance is: %s&#39; % min(msg.ranges))

def main():
    laser_sub = rospy.Subscriber(&#39;laser/scan&#39;, LaserScan, callback=laser_callback)
    twist_pub = rospy.Publisher(&#39;cmd_vel&#39;, Twist, queue_size=10)

    rospy.init_node(&#39;move_with_laser_node&#39;, anonymous=True)
    rate = rospy.Rate(10) # 10hz

    while not rospy.is_shutdown():
        twist_msg = Twist()
        twist_msg.linear.x = 0.3
        twist_msg.angular.z = 0.3
        twist_pub.publish(twist_msg)
        rate.sleep()

if __name__==&#39;__main__&#39;:
    try:
        main()
    except rospy.ROSInterruptException:
        pass</code></pre><p>로봇의 동작 및 Laser 센서 값을 통해 최단 거리를 계산하는 코드이다.</p>
<h3 id="322-실행화면">3.2.2 실행화면</h3>
<p>*<em>1.gazebo 실행화면 *</em>
<img src="https://velog.velcdn.com/images/dazi2_2/post/382112ca-95cf-4dc2-8ed8-c190ea92beed/image.gif" alt=""></p>
<p>*<em>2.터미널 실행화면 *</em>
<img src="https://velog.velcdn.com/images/dazi2_2/post/db635d06-fbaf-4e91-9e3c-7318aa2553dc/image.png" alt=""></p>
<p>위 사진과 같이 로봇이 회전운동하면서 아래의 터미널과 같이 Ladar에서 가장 가까운 거리를 계산하여 출력되는 것을 확인 할 수 있었다.</p>
<h2 id="33-xacro">3.3 XACRO</h2>
<p>로봇 XML 파일을 생성할 때 여러가지 요소의 값을 동시에 바꾸고 싶었던 적이 있는가? 그런 행위를 가능하게 하는 것이 XACRO이다.
XACRO란 XML + Macro의 합성어로 로봇모델을 간단하게 메크로를 사용하여 XML 파일을 생성 할 수 있는 프로그램이다. Parameter를 사용하여 더욱더 빠르고 쉽게 urdf파일을 생성 할 수 있다. 실습을 통해 XACRO 사용법을 알아보자.</p>
<h3 id="331-코드작성">3.3.1 코드작성</h3>
<p>🤖robot.xacro</p>
<pre><code>...
    &lt;xacro:include filename=&quot;$(find robot_description)/urdf/robot.gazebo&quot; /&gt;

    &lt;!-- Parameters --&gt;
    &lt;xacro:property name=&quot;chassis_mass&quot; value=&quot;10&quot; /&gt;
    &lt;xacro:property name=&quot;pi&quot; value=&quot;3.1415926535897931&quot;/&gt;

    &lt;!-- Link - chassis --&gt;
    &lt;link name=&quot;link_chassis&quot;&gt;
        &lt;inertial&gt;
            &lt;mass value=&quot;${chassis_mass}&quot; /&gt;
            &lt;origin xyz=&quot;0 0 0.3&quot; rpy=&quot;0 0 0&quot; /&gt;
            &lt;inertia ixx=&quot;1.5417&quot; ixy=&quot;0&quot; ixz=&quot;0&quot; iyy=&quot;3.467&quot; iyz=&quot;0&quot; izz=&quot;4.742&quot; /&gt;
        &lt;/inertial&gt;

        &lt;collision&gt;
            &lt;!-- &lt;geometry&gt;
                &lt;box size=&quot;2 1.3 0.4&quot; /&gt;
            &lt;/geometry&gt; --&gt;
            &lt;geometry&gt;
                &lt;mesh filename=&quot;package://robot_description/meshes/chassis.stl&quot; /&gt;
            &lt;/geometry&gt;
        &lt;/collision&gt;

        &lt;visual&gt;
            &lt;geometry&gt;
                &lt;mesh filename=&quot;package://robot_description/meshes/chassis.stl&quot; /&gt;
            &lt;/geometry&gt;
            &lt;!-- &lt;geometry&gt;
                &lt;box size=&quot;2 1.3 0.4&quot; /&gt;
            &lt;/geometry&gt; --&gt;
            &lt;material name=&quot;DarkBlue&quot;&gt;
                &lt;color rgba=&quot;0.2 0.2 0.4 1&quot; /&gt;
            &lt;/material&gt;
        &lt;/visual&gt;
    &lt;/link&gt;
    ...
</code></pre><p>Parameters 항목에서 변수에 값을 선언하여 사용할 수 있다.
<code>chassis_mass</code> 값을 10으로 선언하였고 <code>chassis_mass</code> 변수를 중간에 사용하는 모습을 확인 할 수 있다.
그리고 pi과 같은 상수도 선언하여 사용하면 편리하다.</p>
<h2 id="34-joint-control">3.4 Joint Control</h2>
<p>Joint Control은 말그대로 관절의 움직임과 제어를 처리하는데 사용되는 도구이다. 로봇 모델의 관절에 대한 토픽을 통해 명령을 보내거나 프로그래밍을 통해 관절을 제어 할 수 있다.</p>
<h3 id="341-동작화면">3.4.1 동작화면</h3>
<p>코드는 생략하고 동작화면은 아래와 같다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/ae4e7bad-a54d-4f8c-bdea-d559c64fd62f/image.png" alt="">
<img src="https://velog.velcdn.com/images/dazi2_2/post/f9048412-1537-4757-a8a4-01ce891d62e4/image.png" alt="">
Joint_control_gui 를 통해 아래와 같이 로봇의 꼬리를 제어할 수 있었다.</p>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/ac888259-996e-48f6-9278-f8a6139027f3/image.gif" alt=""></p>
<p>다음시간에는 Camera Plugin을 설치하여 로봇 시점에서 카메라의 동작을 확인해보겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ROS] Gazebo에 Robot 불러오기]]></title>
            <link>https://velog.io/@dazi2_2/ROS-Gazebo%EC%97%90-Robot-%EB%B6%88%EB%9F%AC%EC%98%A4%EA%B8%B0</link>
            <guid>https://velog.io/@dazi2_2/ROS-Gazebo%EC%97%90-Robot-%EB%B6%88%EB%9F%AC%EC%98%A4%EA%B8%B0</guid>
            <pubDate>Tue, 09 Jan 2024 12:17:14 GMT</pubDate>
            <description><![CDATA[<h1 id="🐣0개요">🐣0.개요</h1>
<p>지난 시간에 로봇을 만드는 실습을 진행하였다.
이번시간에는 Gazebo에서 로봇을 스폰하는 방법에 대해서 알아보겠다.</p>
<h1 id="🗒1launch-파일-만들기">🗒1.Launch 파일 만들기</h1>
<h2 id="🚀11-gazebo-world-생성-launch-파일">🚀1.1 Gazebo World 생성 launch 파일</h2>
<p>🚀empty_world.launch</p>
<pre><code>&lt;?xml version= &quot;1.0&quot; ?&gt;
&lt;launch&gt;
    &lt;include file= &quot;$(find gazebo_ros)/launch/empty_world.launch&quot;&gt;
    &lt;arg name = &quot;paused&quot; value = &quot;false&quot;/&gt;
    &lt;arg name = &quot;use_sim_time&quot; value = &quot;true&quot;/&gt;
    &lt;arg name = &quot;gui&quot; value = &quot;true&quot;/&gt;
    &lt;arg name = &quot;headless&quot; value = &quot;false&quot;/&gt;
    &lt;arg name = &quot;debug&quot; value = &quot;false&quot;/&gt;
    &lt;/include&gt;
&lt;/launch&gt;    </code></pre><p>월드를 생성하는 launch 파일.
월드의 속성을 정의할 수 있다.</p>
<h2 id="12-spawn-launch-파일">1.2 spawn launch 파일</h2>
<p>🚀spawn_robot.launch</p>
<pre><code>&lt;?xml version= &quot;1.0&quot; ?&gt;
&lt;launch&gt;
    &lt;param name=&quot;robot_description&quot; command = &quot;cat &#39;$(find robot_description)/urdf/robot.urdf&#39;&quot;/&gt;
    &lt;arg name = &quot;x&quot; default = &quot;0&quot;/&gt;
    &lt;arg name = &quot;y&quot; default = &quot;0&quot;/&gt;
    &lt;arg name = &quot;z&quot; default = &quot;0.5&quot;/&gt;
    &lt;node name = &quot;mybot_spawn&quot; pkg =&quot;gazebo_ros&quot; type =&quot;spawn_model&quot; output =&quot;screen&quot;
            args = &quot;-urdf -param robot_description -model my_robot -x $(arg x) -y $(arg y) -z $(arg z)&quot; /&gt;
&lt;/launch&gt;    </code></pre><p>로봇을 스폰할 위치를 정의해준다.</p>
<p>그리고 <code>empty_world</code>를 먼저 launch 하고 <code>spawn_robot</code>를 launch 하면 된다.</p>
<h1 id="🌍2실행화면">🌍2.실행화면</h1>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/44f60bda-4919-45ee-ba6b-abc64d1a6620/image.png" alt="">
위와 같이 모델링한 자동차 모형이 Gazebo에 등장 하는 것을 확인 할 수 있다.</p>
<h1 id="🤖21-같은-로봇-여러번-불러오기">🤖2.1 같은 로봇 여러번 불러오기</h1>
<p>같은 로봇을 월드에서 여러번 불러올려면 어떻게 해야할까?
위 과정으로 한번더 spawn 을 launch 하면 에러가 나타나면서 로봇이 월드에 스폰할 수 없다고 나온다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/bda80814-568f-4db6-b71c-452c9f320a6b/image.png" alt="">
이유는 중복되는 <code>model_name</code>을 가지는 객체를 소환 할 수 없기 때문이다.
위 문제를 해결하기 위해 아래와 같이 모델네임을 정의 할 수 있게 런치파일을 수정하면 된다.</p>
<p>🚀spawn_robot.launch</p>
<pre><code>&lt;?xml version= &quot;1.0&quot; ?&gt;
&lt;launch&gt;
    &lt;param name=&quot;robot_description&quot; command=&quot;cat &#39;$(find robot_description)/urdf/robot.urdf&#39;&quot; /&gt;

    &lt;arg name=&quot;model_name&quot; default=&quot;my_robot&quot;/&gt;
    &lt;arg name=&quot;x&quot; default=&quot;0&quot;/&gt;
    &lt;arg name=&quot;y&quot; default=&quot;0&quot;/&gt;
    &lt;arg name=&quot;z&quot; default=&quot;0.5&quot;/&gt;

    &lt;node name=&quot;mybot_spawn&quot; pkg=&quot;gazebo_ros&quot; type=&quot;spawn_model&quot; output=&quot;screen&quot;
          args=&quot;-urdf -param robot_description -model $(arg model_name) -x $(arg x) -y $(arg y) -z $(arg z)&quot; /&gt;
&lt;/launch&gt;</code></pre><pre><code>$roslaunch spawn.launch x:=6 model_name:=second</code></pre><p>터미널 명령어로 <code>model_name</code>을 선언하여 launch하면 이상없이 아래의 사진처럼 로봇을 스폰 할 수 있다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/2372b0d2-f53f-4e28-b83f-557e33fd181d/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ROS] Rviz 로봇 모델 만들기]]></title>
            <link>https://velog.io/@dazi2_2/ROS-Rviz-%EB%A1%9C%EB%B4%87-%EB%AA%A8%EB%8D%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@dazi2_2/ROS-Rviz-%EB%A1%9C%EB%B4%87-%EB%AA%A8%EB%8D%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Tue, 09 Jan 2024 11:46:32 GMT</pubDate>
            <description><![CDATA[<h1 id="🤔0개요">🤔0.개요</h1>
<p>Rviz 및 urdf를 활용하여 자동차 모델링을 해보자.</p>
<h1 id="🗒1예제코드">🗒1.예제코드</h1>
<p>🤖robot.urdf</p>
<pre><code>&lt;?xml version= &quot;1.0&quot; ?&gt;
&lt;robot name = &quot;robot&quot;&gt;
    &lt;!--link - chassis --&gt;
    &lt;link name = &quot;link_chassis&quot;&gt;
        &lt;interial&gt;
            &lt;mass value=&quot;10&quot;/&gt;
            &lt;origin xyz =&quot;0 0 0.3&quot; rpy = &quot;0 0 0&quot;/&gt;
            &lt;intertia ixx = &quot; 1.5417&quot; ixy = &quot;0&quot; ixz = &quot;0&quot; iyy=&quot;3.467&quot; iyx = &quot;0&quot; izz= &quot;4.742&quot; /&gt;
        &lt;/interial&gt;

        &lt;collision&gt;
            &lt;geometry&gt;
                &lt;box size = &quot;2 1.3 0.4&quot;/&gt;
            &lt;/geometry&gt;
        &lt;/collision&gt;

        &lt;visual&gt;
            &lt;geometry&gt;
                &lt;box size = &quot;2 1.3 0.4&quot;/&gt;
            &lt;/geometry&gt;
            &lt;material name =&quot;Red&quot;&gt;
                &lt;color rgba = &quot;1 0 0 1&quot;/&gt;
            &lt;/material&gt;
        &lt;/visual&gt;
    &lt;/link&gt;
    &lt;!-- Joint - chassis / left wheel --&gt;
    &lt;joint name=&quot;joint_chassis_left_wheel&quot; type=&quot;continuous&quot;&gt;
        &lt;origin rpy=&quot;0 0 0&quot; xyz=&quot;-0.5 0.65 0&quot; /&gt;
        &lt;child link=&quot;link_left_wheel&quot; /&gt;
        &lt;parent link=&quot;link_chassis&quot; /&gt;
        &lt;axis rpy=&quot;0 0 0&quot; xyz=&quot;0 1 0&quot; /&gt;
        &lt;limit effort=&quot;10000&quot; velocity=&quot;1000&quot; /&gt;
        &lt;joint_properties damping=&quot;1.0&quot; friction=&quot;1.0&quot; /&gt;
    &lt;/joint&gt;

    &lt;!-- Link - left wheel --&gt;
    &lt;link name=&quot;link_left_wheel&quot;&gt;
        &lt;inertial&gt;
            &lt;mass value=&quot;1&quot; /&gt;
            &lt;origin xyz=&quot;0 0 0&quot; rpy=&quot;0 0 0&quot; /&gt;
            &lt;inertia ixx=&quot;0.002526666666667&quot; ixy=&quot;0&quot; ixz=&quot;0&quot; iyy=&quot;0.002526666666667&quot; iyz=&quot;0&quot; izz=&quot;0.005&quot;/&gt;
        &lt;/inertial&gt;

        &lt;collision&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 0.18 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.12&quot; radius =&quot;0.4&quot;/&gt;
            &lt;/geometry&gt;
         &lt;/collision&gt;

        &lt;visual&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 0.18 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.12&quot; radius =&quot;0.4&quot;/&gt;
            &lt;/geometry&gt;
         &lt;/visual&gt;

        &lt;collision&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 0.06 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.12&quot; radius =&quot;0.08&quot;/&gt;
            &lt;/geometry&gt;
         &lt;/collision&gt;

        &lt;visual&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 0.06 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.12&quot; radius =&quot;0.08&quot;/&gt;
            &lt;/geometry&gt;
         &lt;/visual&gt;


    &lt;/link&gt;
&lt;/robot&gt;
</code></pre><p>launch 파일은 생략한다.
Rviz를 통해 위 코드를 동작시켜보면 아래의 그림과 같은 모형이 나타난다.</p>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/9dc77139-ba89-4761-918b-e7d8b3a87288/image.gif" alt="">
각각의 코드가 어떤역할을 하길래 위와 같은 동작을 수행 할 수 있는 것일까?</p>
<pre><code>&lt;!-- Joint - chassis / left wheel --&gt;
    &lt;joint name=&quot;joint_chassis_left_wheel&quot; type=&quot;continuous&quot;&gt;
        &lt;origin rpy=&quot;0 0 0&quot; xyz=&quot;-0.5 0.65 0&quot; /&gt;
        &lt;child link=&quot;link_left_wheel&quot; /&gt;
        &lt;parent link=&quot;link_chassis&quot; /&gt;
        &lt;axis rpy=&quot;0 0 0&quot; xyz=&quot;0 1 0&quot; /&gt;
        &lt;limit effort=&quot;10000&quot; velocity=&quot;1000&quot; /&gt;
        &lt;joint_properties damping=&quot;1.0&quot; friction=&quot;1.0&quot; /&gt;
    &lt;/joint&gt;</code></pre><p>위 코드는 <code>link_chassis</code>를 <code>parent</code>로 선언하여 기준으로 삼는다는 뜻이고 기준점에서 <code>origin</code>속성만큼의 값만큼 떨어진 곳에 <code>link_left_wheel</code>를 연결한다는 뜻이다.<code>origin</code>에서 <code>rpy</code>는 기울어짐 정도이고, <code>xyz</code>는 위상차이로 상대적인 좌표를 나타내는 것이다.
<code>axis</code> 는 어떤 축을 기준으로 link를 작동시킬 것인지 나타내는 것이고, 위코드에는 y축을 기준으로 움직이기 때문에 위와같이 선언하였다.</p>
<pre><code>&lt;link name=&quot;link_left_wheel&quot;&gt;
        &lt;inertial&gt;
            &lt;mass value=&quot;1&quot; /&gt;
            &lt;origin xyz=&quot;0 0 0&quot; rpy=&quot;0 0 0&quot; /&gt;
            &lt;inertia ixx=&quot;0.002526666666667&quot; ixy=&quot;0&quot; ixz=&quot;0&quot; iyy=&quot;0.002526666666667&quot; iyz=&quot;0&quot; izz=&quot;0.005&quot;/&gt;
        &lt;/inertial&gt;
        &lt;collision&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 0.18 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.12&quot; radius =&quot;0.4&quot;/&gt;
            &lt;/geometry&gt;
         &lt;/collision&gt;
        &lt;visual&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 0.18 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.12&quot; radius =&quot;0.4&quot;/&gt;
            &lt;/geometry&gt;
         &lt;/visual&gt;
        &lt;collision&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 0.06 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.12&quot; radius =&quot;0.08&quot;/&gt;
            &lt;/geometry&gt;
         &lt;/collision&gt;
        &lt;visual&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 0.06 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.12&quot; radius =&quot;0.08&quot;/&gt;
            &lt;/geometry&gt;
         &lt;/visual&gt;
    &lt;/link&gt;</code></pre><p>위 내용은 <code>link_left_wheel</code>에 대한 내용을 담고 있다. 무게와 좌표 등을 담고 있다. 충돌 정보를 담고 있는 <code>collision</code> 그리고 보여지는 요소 <code>visual</code>로 구성된다.</p>
<h1 id="💻2실습">💻2.실습</h1>
<p>위 내용을 토대로 반대쪽 바퀴를 추가하고, 아래쪽에 캐스터 휠을 추가한 로봇을 제작해보자.</p>
<p>🤖robot.urdf</p>
<pre><code>&lt;?xml version= &quot;1.0&quot; ?&gt;

&lt;robot name = &quot;robot&quot;&gt;
    &lt;!--link - chassis --&gt;
    &lt;link name = &quot;link_chassis&quot;&gt;
        &lt;interial&gt;
            &lt;mass value=&quot;10&quot;/&gt;
            &lt;origin xyz =&quot;0 0 0.3&quot; rpy = &quot;0 0 0&quot;/&gt;
            &lt;intertia ixx = &quot; 1.5417&quot; ixy = &quot;0&quot; ixz = &quot;0&quot; iyy=&quot;3.467&quot; iyx = &quot;0&quot; izz= &quot;4.742&quot; /&gt;
        &lt;/interial&gt;

        &lt;collision&gt;
            &lt;geometry&gt;
                &lt;box size = &quot;2 1.3 0.4&quot;/&gt;
            &lt;/geometry&gt;
        &lt;/collision&gt;

        &lt;visual&gt;
            &lt;geometry&gt;
                &lt;box size = &quot;2 1.3 0.4&quot;/&gt;
            &lt;/geometry&gt;
            &lt;material name =&quot;Purple&quot;&gt;
                &lt;color rgba = &quot;0.5 0 1 1&quot;/&gt;
            &lt;/material&gt;
        &lt;/visual&gt;
    &lt;/link&gt;
    &lt;!-- Joint - chassis / left wheel --&gt;
    &lt;joint name=&quot;joint_chassis_left_wheel&quot; type=&quot;continuous&quot;&gt;
        &lt;origin rpy=&quot;0 0 0&quot; xyz=&quot;-0.5 0.65 0&quot; /&gt;
        &lt;child link=&quot;link_left_wheel&quot; /&gt;
        &lt;parent link=&quot;link_chassis&quot; /&gt;
        &lt;axis rpy=&quot;0 0 0&quot; xyz=&quot;0 1 0&quot; /&gt;
        &lt;limit effort=&quot;10000&quot; velocity=&quot;1000&quot; /&gt;
        &lt;joint_properties damping=&quot;1.0&quot; friction=&quot;1.0&quot; /&gt;
    &lt;/joint&gt;

    &lt;!-- Link - left wheel --&gt;
    &lt;link name=&quot;link_left_wheel&quot;&gt;
        &lt;inertial&gt;
            &lt;mass value=&quot;1&quot; /&gt;
            &lt;origin xyz=&quot;0 0 0&quot; rpy=&quot;0 0 0&quot; /&gt;
            &lt;inertia ixx=&quot;0.002526666666667&quot; ixy=&quot;0&quot; ixz=&quot;0&quot; iyy=&quot;0.002526666666667&quot; iyz=&quot;0&quot; izz=&quot;0.005&quot;/&gt;
        &lt;/inertial&gt;

        &lt;!--hub cap--&gt;
        &lt;visual&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 0.24 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;box size = &quot;0.05 0.5 0.01&quot;/&gt;
            &lt;/geometry&gt;
            &lt;material name =&quot;White&quot;&gt;
                &lt;color rgba = &quot;1 1 1 1&quot;/&gt;
            &lt;/material&gt;
         &lt;/visual&gt;

        &lt;!--big wheel --&gt;
        &lt;collision&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 0.18 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.12&quot; radius =&quot;0.4&quot;/&gt;
            &lt;/geometry&gt;
         &lt;/collision&gt;

        &lt;visual&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 0.18 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.12&quot; radius =&quot;0.4&quot;/&gt;
            &lt;/geometry&gt;
            &lt;material name =&quot;Black&quot;&gt;
                &lt;color rgba = &quot;0 0 0 1&quot;/&gt;
            &lt;/material&gt;
         &lt;/visual&gt;

        &lt;collision&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 0.06 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.12&quot; radius =&quot;0.08&quot;/&gt;
            &lt;/geometry&gt;
         &lt;/collision&gt;

        &lt;visual&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 0.06 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.12&quot; radius =&quot;0.08&quot;/&gt;
            &lt;/geometry&gt;
            &lt;material name =&quot;Gray&quot;&gt;
                &lt;color rgba = &quot;0.5 0.5 0.5 1&quot;/&gt;
            &lt;/material&gt;
         &lt;/visual&gt;
    &lt;/link&gt;

    &lt;!-- Joint - chassis / right wheel --&gt;
    &lt;joint name=&quot;joint_chassis_right_wheel&quot; type=&quot;continuous&quot;&gt;
        &lt;origin rpy=&quot;0 0 0&quot; xyz=&quot;-0.5 -0.65 0&quot; /&gt;
        &lt;child link=&quot;link_right_wheel&quot; /&gt;
        &lt;parent link=&quot;link_chassis&quot; /&gt;
        &lt;axis rpy=&quot;0 0 0&quot; xyz=&quot;0 1 0&quot; /&gt;
        &lt;limit effort=&quot;10000&quot; velocity=&quot;1000&quot; /&gt;
        &lt;joint_properties damping=&quot;1.0&quot; friction=&quot;1.0&quot; /&gt;
    &lt;/joint&gt;

    &lt;!-- Link - right wheel --&gt;
    &lt;link name=&quot;link_right_wheel&quot;&gt;
        &lt;inertial&gt;
            &lt;mass value=&quot;1&quot; /&gt;
            &lt;origin xyz=&quot;0 0 0&quot; rpy=&quot;0 0 0&quot; /&gt;
            &lt;inertia ixx=&quot;0.002526666666667&quot; ixy=&quot;0&quot; ixz=&quot;0&quot; iyy=&quot;0.002526666666667&quot; iyz=&quot;0&quot; izz=&quot;0.005&quot;/&gt;
        &lt;/inertial&gt;

        &lt;!--hub cap--&gt;
        &lt;visual&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 -0.24 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;box size = &quot;0.05 0.5 0.01&quot;/&gt;
            &lt;/geometry&gt;
            &lt;material name =&quot;White&quot;&gt;
                &lt;color rgba = &quot;1 1 1 1&quot;/&gt;
            &lt;/material&gt;
         &lt;/visual&gt;

        &lt;collision&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 -0.18 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.12&quot; radius =&quot;0.4&quot;/&gt;
            &lt;/geometry&gt;
         &lt;/collision&gt;

        &lt;visual&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 -0.18 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.12&quot; radius =&quot;0.4&quot;/&gt;
            &lt;/geometry&gt;
            &lt;material name =&quot;Black&quot;&gt;
                &lt;color rgba = &quot;0 0 0 1&quot;/&gt;
            &lt;/material&gt;
         &lt;/visual&gt;

        &lt;collision&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 -0.06 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.12&quot; radius =&quot;0.08&quot;/&gt;
            &lt;/geometry&gt;
         &lt;/collision&gt;

        &lt;visual&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 -0.06 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.12&quot; radius =&quot;0.08&quot;/&gt;
            &lt;/geometry&gt;
            &lt;material name =&quot;Gray&quot;&gt;
                &lt;color rgba = &quot;0.5 0.5 0.5 1&quot;/&gt;
            &lt;/material&gt;
         &lt;/visual&gt;
    &lt;/link&gt;

    &lt;!-- Joint - chassis / caster wheel --&gt;
    &lt;joint name=&quot;joint_chassis_caster_wheel&quot; type=&quot;fixed&quot;&gt;
        &lt;origin rpy=&quot;0 0 0&quot; xyz=&quot;0.25 0 -0.15&quot; /&gt;
        &lt;child link=&quot;link_caster_wheel&quot; /&gt;
        &lt;parent link=&quot;link_chassis&quot; /&gt;
        &lt;axis rpy=&quot;0 0 0&quot; xyz=&quot;0 1 0&quot; /&gt;
        &lt;limit effort=&quot;10000&quot; velocity=&quot;1000&quot; /&gt;
        &lt;joint_properties damping=&quot;1.0&quot; friction=&quot;1.0&quot; /&gt;
    &lt;/joint&gt;

    &lt;!-- Link - right wheel --&gt;
    &lt;link name=&quot;link_caster_wheel&quot;&gt;
        &lt;inertial&gt;
            &lt;mass value=&quot;1&quot; /&gt;
            &lt;origin xyz=&quot;0 0 0&quot; rpy=&quot;0 0 0&quot; /&gt;
            &lt;inertia ixx=&quot;0.002526666666667&quot; ixy=&quot;0&quot; ixz=&quot;0&quot; iyy=&quot;0.002526666666667&quot; iyz=&quot;0&quot; izz=&quot;0.005&quot;/&gt;
        &lt;/inertial&gt;

        &lt;!--caster wheel--&gt;
        &lt;collision&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0.5 0 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;sphere radius =&quot;0.3&quot;/&gt;
            &lt;/geometry&gt;
         &lt;/collision&gt;

        &lt;visual&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0.5 0 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;sphere radius =&quot;0.2&quot;/&gt;
            &lt;/geometry&gt;
            &lt;material name =&quot;Black&quot;&gt;
                &lt;color rgba = &quot;0 0 0 1&quot;/&gt;
            &lt;/material&gt;
         &lt;/visual&gt;
        &lt;!--caster mini wheel --&gt;

        &lt;collision&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0.5 0 -0.1&quot; /&gt;
            &lt;geometry&gt;
                &lt;sphere radius =&quot;0.15&quot;/&gt;
            &lt;/geometry&gt;
         &lt;/collision&gt;

        &lt;visual&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0.5 0 -0.1&quot; /&gt;
            &lt;geometry&gt;
                &lt;sphere radius =&quot;0.15&quot;/&gt;
            &lt;/geometry&gt;
            &lt;material name =&quot;While&quot;&gt;
                &lt;color rgba = &quot;1 1 1 1&quot;/&gt;
            &lt;/material&gt;
         &lt;/visual&gt;
    &lt;/link&gt;
&lt;/robot&gt;
</code></pre><p><img src="https://velog.velcdn.com/images/dazi2_2/post/6c38c58c-7fdc-4b36-9dc9-ecdfbb063be6/image.png" alt=""></p>
<p>학습내용을 토대로 위 로봇을 모델링 하였다. TF축도 확인하였다.</p>
<p><img src="blob:https://velog.io/b5330850-927f-4571-ad07-39768f1e66fc" alt="업로드중.."></p>
<h1 id="🗿3meshes-설정하기">🗿3.Meshes 설정하기</h1>
<p>urdf를 통해 로봇을 디자인할때 <code>.stl</code>과 같은 파일을 이용하여 물체의 외형을 변경 할 수 있다. 앞에서 만든 자동차 모형에 <code>mesh</code>속성을 추가해보자.</p>
<pre><code>        &lt;!--big wheel --&gt;
        &lt;collision&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 0.18 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.12&quot; radius =&quot;0.4&quot;/&gt;
            &lt;/geometry&gt;
         &lt;/collision&gt;

        &lt;visual&gt;
            &lt;origin rpy=&quot;1.5707 0 0&quot; xyz = &quot;0 0.18 0&quot; /&gt;
            &lt;geometry&gt;
                &lt;mesh filename =&quot;package://robot_description/meshes/wheel.stl&quot;/&gt;
            &lt;/geometry&gt;
            &lt;material name =&quot;Black&quot;&gt;
                &lt;color rgba = &quot;0 0 0 1&quot;/&gt;
            &lt;/material&gt;
         &lt;/visual&gt;</code></pre><p>mesh 파일을 다운로드 하여 <code>src/robot_description/meshes</code> 파일에 <code>.stl</code>파일을 추가하여 위의 코드와 같이 geometry 속성에서 불러와서 사용하였다. 위 코드는 몸통에 Mesh를 적용 한 것이며 바퀴에도 따로 적용시켰다.
<img src="blob:https://velog.io/e64260ff-21be-445e-a051-3692e0dea0a2" alt="업로드중.."></p>
<p>부드러워진 자동차의 형태. Cad를 다룰줄 알면 모델링을 할때 유용 할 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ROS] URDF 알아보기]]></title>
            <link>https://velog.io/@dazi2_2/ROS-URDF-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@dazi2_2/ROS-URDF-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Mon, 08 Jan 2024 14:36:04 GMT</pubDate>
            <description><![CDATA[<h1 id="🤔0urdf란">🤔0.URDF란?</h1>
<p>URDF (Unified Robot Description Format)은 직역하면 통합 로봇 설명 형식으로 로봇의 3D 형상 및 외관, 관성 등 물리적 특성 등을 XML 언어로 정의한 로봇 모델링 언어이다. URDF로 정의된 로봇은 RVIZ 및 Gazebo에서 물리 시뮬레이션을 할 수 있다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/f7792cf8-7410-4e8c-8451-eb4c9eaee0b7/image.png" alt="Gazebo 실행화면"></p>
<h1 id="🏁1urdf-좌표계와-단위">🏁1.URDF 좌표계와 단위</h1>
<p>URDF 는 아래의 그림과 같이 <code>x</code>, <code>y</code>, <code>z</code> 축을 사용한다.
회전을 표현하기위해 오일러 각도 <code>roll</code>, <code>pitch</code>, <code>yaw</code> 를 사용한다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/5bfdc6be-aa04-43c9-89f2-5d574c34e3a2/image.png" alt=""></p>
<p>단위는 아래의 표와 같이 사용된다.</p>
<blockquote>
<ol>
<li>길이 : 미터 m</li>
<li>각도 : 라디안 radian</li>
<li>질량 : kg</li>
<li>속도 : m/s</li>
<li>각속도 : radian /s </li>
</ol>
</blockquote>
<h1 id="🐣2urdf-형상과-색상-표현">🐣2.URDF 형상과 색상 표현</h1>
<h2 id="21형상표현">2.1형상표현</h2>
<blockquote>
<p>cylinder     : 원통
box         : 상자
sphere         : 공</p>
</blockquote>
<h2 id="22색상-표현">2.2색상 표현</h2>
<p>RGB 3원색과 투과율(0~1) 을 정의하여 사용한다.</p>
<h1 id="🐤urdf-기구-표현">🐤.URDF 기구 표현</h1>
<p>기구의 속성을 부여하여 동작을 제어할 수 있다.</p>
<blockquote>
<p>Base: 고정부분(grounded)
Link: 관절에 연결되는 로봇 팔의 부분
Joint: 링크를 연결하는 부위로 보통 모터의 회전을 통해 움직임을 만듦</p>
</blockquote>
<p>Joint 의 경우 아래의 속성을 부여하여 제어 할 수 있다.</p>
<blockquote>
<p>fixed: 고정
revolution : 작동 범위 제한
continuous : 연속회전</p>
</blockquote>
<h1 id="🐢4예제를-통해-urdf-알아보기">🐢4.예제를 통해 URDF 알아보기</h1>
<p>🤖pan_tilt.urdf</p>
<pre><code class="language-urdf">&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;robot name=&quot;ex_urdf_pan_tilt&quot;&gt;

    &lt;link name=&quot;base_link&quot;&gt; ## link : base_link
        &lt;visual&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.01&quot; radius=&quot;0.2&quot;/&gt; ## 형태 (길이, 각도)
            &lt;/geometry&gt;
            &lt;origin rpy=&quot;0 0 0&quot; xyz=&quot;0 0 0&quot;/&gt; ## x,y,z 좌표 및 영점조절
            &lt;material name=&quot;yellow&quot;&gt;
                &lt;color rgba=&quot;1 1 0 1&quot;/&gt;  ## 색상값
            &lt;/material&gt;
        &lt;/visual&gt;

        &lt;collision&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.03&quot; radius=&quot;0.2&quot;/&gt;
            &lt;/geometry&gt;
            &lt;origin rpy=&quot;0 0 0&quot; xyz=&quot;0 0 0&quot;/&gt;
        &lt;/collision&gt;

        &lt;inertial&gt;
            &lt;mass value=&quot;1&quot;/&gt;
            &lt;inertia ixx=&quot;1.0&quot; ixy=&quot;0.0&quot; ixz=&quot;0.0&quot; iyy=&quot;1.0&quot; iyz=&quot;0.0&quot; izz=&quot;1.0&quot;/&gt;
        &lt;/inertial&gt;
    &lt;/link&gt;

    &lt;joint name=&quot;pan_joint&quot; type=&quot;revolute&quot;&gt; ## joint : pan_joint 
        &lt;parent link=&quot;base_link&quot;/&gt;
        &lt;child link=&quot;pan_link&quot;/&gt;
        &lt;origin xyz=&quot;0 0 0.1&quot;/&gt;
        &lt;axis xyz=&quot;0 0 1&quot; /&gt;
        &lt;limit effort=&quot;300&quot; velocity=&quot;0.1&quot; lower=&quot;-3.14&quot; upper=&quot;3.14&quot;/&gt;
        &lt;dynamics damping=&quot;50&quot; friction=&quot;1&quot;/&gt;
    &lt;/joint&gt;

    &lt;link name=&quot;pan_link&quot;&gt;  ## link : pan_link
        &lt;visual&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.4&quot; radius=&quot;0.04&quot;/&gt;
            &lt;/geometry&gt;
            &lt;origin rpy=&quot;0 0 0&quot; xyz=&quot;0 0 0.09&quot;/&gt;
            &lt;material name=&quot;red&quot;&gt;
                &lt;color rgba=&quot;0 0 1 1&quot;/&gt;
            &lt;/material&gt;
        &lt;/visual&gt;

        &lt;collision&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.4&quot; radius=&quot;0.06&quot;/&gt;
            &lt;/geometry&gt;
            &lt;origin rpy=&quot;0 0 0&quot; xyz=&quot;0 0 0.09&quot;/&gt;
        &lt;/collision&gt;

        &lt;inertial&gt;
            &lt;mass value=&quot;1&quot;/&gt;
            &lt;inertia ixx=&quot;1.0&quot; ixy=&quot;0.0&quot; ixz=&quot;0.0&quot; iyy=&quot;1.0&quot; iyz=&quot;0.0&quot; izz=&quot;1.0&quot;/&gt;
        &lt;/inertial&gt;
    &lt;/link&gt;

    &lt;joint name=&quot;tilt_joint&quot; type=&quot;revolute&quot;&gt; ## joint : tilt_joint
        &lt;parent link=&quot;pan_link&quot;/&gt;
        &lt;child link=&quot;tilt_link&quot;/&gt;
        &lt;origin xyz=&quot;0 0 0.2&quot;/&gt;
        &lt;axis xyz=&quot;0 1 0&quot; /&gt;
        &lt;limit effort=&quot;300&quot; velocity=&quot;0.1&quot; lower=&quot;-4.71239&quot; upper=&quot;-1.570796&quot;/&gt;
        &lt;dynamics damping=&quot;50&quot; friction=&quot;1&quot;/&gt;
    &lt;/joint&gt;

    &lt;link name=&quot;tilt_link&quot;&gt;   ## link : tilt_link
        &lt;visual&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.4&quot; radius=&quot;0.04&quot;/&gt;
            &lt;/geometry&gt;
            &lt;origin rpy=&quot;0 1.570796 0&quot; xyz=&quot;0 0 0&quot;/&gt;
            &lt;material name=&quot;green&quot;&gt;
                &lt;color rgba=&quot;1 0 0 1&quot;/&gt;
            &lt;/material&gt;
        &lt;/visual&gt;

        &lt;collision&gt;
            &lt;geometry&gt;
                &lt;cylinder length=&quot;0.4&quot; radius=&quot;0.06&quot;/&gt;
            &lt;/geometry&gt;
            &lt;origin rpy=&quot;0 1.570796 0&quot; xyz=&quot;0 0 0&quot;/&gt;
        &lt;/collision&gt;

        &lt;inertial&gt;
            &lt;mass value=&quot;1&quot;/&gt;
            &lt;inertia ixx=&quot;1.0&quot; ixy=&quot;0.0&quot; ixz=&quot;0.0&quot; iyy=&quot;1.0&quot; iyz=&quot;0.0&quot; izz=&quot;1.0&quot;/&gt;
        &lt;/inertial&gt;
    &lt;/link&gt;

&lt;/robot&gt;
</code></pre>
<p>여기서 아래의 명령어를 터미널 창에 입력하면 Link와 Joint 관계도를 PDF파일로 만들어준다.</p>
<p>🖥️terminal</p>
<pre><code class="language-terminal">$urdf_to_graphiz pan_tilt.urdf</code></pre>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/beead360-dd0d-4656-a229-54063d25e07c/image.png" alt="">
🖥️terminal</p>
<pre><code class="language-terminal">$sudo apt-get install liburdfdom-tools</code></pre>
<p>만약 찾을수 없는 명령이라고 나오면 위의 명령어를 터미널에 입력해줘야 한다.</p>
<p>🚀pan_tilt_urdf.launch</p>
<pre><code>&lt;launch&gt;
    &lt;arg name=&quot;model&quot; /&gt;

    &lt;param name=&quot;robot_description&quot; textfile=&quot;$(find ex_urdf)/urdf/pan_tilt.urdf&quot; /&gt;

    &lt;!-- Setting gui parameter to true for display joint slider --&gt;
    &lt;param name=&quot;use_gui&quot; value=&quot;true&quot;/&gt;
    &lt;!-- Starting Joint state publisher node which will publish the joint values --&gt;
    &lt;node name=&quot;joint_state_publisher&quot; pkg=&quot;joint_state_publisher&quot; type=&quot;joint_state_publisher&quot; /&gt;
    &lt;!-- Starting robot state publish which will publish tf --&gt;
    &lt;node name=&quot;robot_state_publisher&quot; pkg=&quot;robot_state_publisher&quot; type=&quot;state_publisher&quot; /&gt;
    &lt;!-- Launch visualization in rviz --&gt;
    &lt;node name=&quot;rviz&quot; pkg=&quot;rviz&quot; type=&quot;rviz&quot; args=&quot;-d $(find ex_urdf)/urdf.rviz&quot; required=&quot;True&quot; /&gt;
&lt;/launch&gt;</code></pre><p>Joint state 변경 gui 설치
🖥️terminal</p>
<pre><code>$sudo apt install ros-melodic-joint-state-publisher-gui </code></pre><p>해당 명령어를 터미널에 입력하여 Joint state 변경 gui를 설치해준다. 아래에서 어떤식으로 동작하는지 알아보자.</p>
<p>이제 코드를 모드 작성하였으니 launch 파일을 실행시켜 나타나는 화면을 확인하자.</p>
<h1 id="🚗5실행화면">🚗5.실행화면</h1>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/b0292431-eaa7-45d2-b0dd-c67a5ebbdf09/image.png" alt=""></p>
<p>위와 같은 기둥형태의 로봇을 확인 할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/b8d09e92-2780-4169-ac8b-4c5f3b8ec483/image.png" alt="">
아까 터미널을 통해 <code>ros-melodic-joint-state-publisher-gui</code>를 설치하였다면 위 화면을 확인 할 수 있다.
이 창은 Joint의 각도를 조절하는 gui이다. 아래 영상에서 버튼을 이용하여 Joint를 조절하는 모습을 볼 수 있다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/e669c78e-2fe8-4e46-a7d5-9ba47a6296ad/image.gif" alt=""></p>
<p><code>Randomize</code> 버튼을 통해 랜덤으로 조절할 수도 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ROS]RViz 3D자동차 주행프로그래밍]]></title>
            <link>https://velog.io/@dazi2_2/ROSRViz-3D%EC%9E%90%EB%8F%99%EC%B0%A8-%EC%A3%BC%ED%96%89%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
            <guid>https://velog.io/@dazi2_2/ROSRViz-3D%EC%9E%90%EB%8F%99%EC%B0%A8-%EC%A3%BC%ED%96%89%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</guid>
            <pubDate>Sun, 07 Jan 2024 08:18:43 GMT</pubDate>
            <description><![CDATA[<h1 id="🤔0개요">🤔0.개요</h1>
<p>지난시간에는 RViz에 3D모델링 된 자동차를 띄우고, 바퀴를 제어하는 프로그래밍을 진행하였다. 이번 시간에는 <code>odom</code>이라는 토픽을 만들어, RViz 오도메트리 프로그램에 전송하여 자동차를 가상공간에서 직접 움직여보고, 궤적을 남기는것을 추가하여 주행하는 프로그램을 작성하고 실제로 어떤식으로 움직이는지 확인해보자. (아래 사진은 지난시간 제어프로그래밍 사진이다.)
<img src="https://velog.velcdn.com/images/dazi2_2/post/e5b08f1c-38ee-454c-8b2e-513867d6ae48/image.gif" alt=""></p>
<h1 id="🚗1동작-방식">🚗1.동작 방식</h1>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/262511e1-563c-44dd-a465-ec3c9d32a946/image.png" alt="">
동작 방식은 위와 같다. <code>driver</code>노드에서 <code>xycar_motor</code>라는 모터 제어 토픽을 <code>convertor</code>노드에 전송하고, 전송 받은 토픽을 가공하여, <code>joint_states</code>토픽으로 <code>RViz viewer</code> 및 <code>rviz_odom</code>이라는 노드에 전송한다. <code>RViz viewer</code>는 가상 자동차 모델의 바퀴의 움직임을 제어하는 역할을 하고, <code>rviz_odom</code>노드는 가상 자동차 모델을 실제로 움직이는 <code>odom</code>토픽으로 전송한다.</p>
<h2 id="📬1-1msg-구성">📬1-1.Msg 구성</h2>
<p>각 토픽이 어떤 메세지를 주고받는지 터미널 명령어를 통해 알아보자.
<img src="https://velog.velcdn.com/images/dazi2_2/post/bbcfa94c-3ddf-45b8-96da-cf20e3c0fdb2/image.png" alt="xycar_motor">
<code>xycar_motor</code> 에는 <code>header</code> 및 <code>angle</code>, <code>speed</code> 로 구성되어있다. 
<img src="https://velog.velcdn.com/images/dazi2_2/post/77c99736-1e3e-41a5-884b-fcbd3ddc4e52/image.png" alt="JointState">
<code>JointState</code>는 <code>header</code> 및 <code>name</code>, <code>position</code>,<code>effort</code> 로 구성되어있으며, xycar_motor 으로 부터 받은 토픽을 해석하여 다시 토픽에 담아서 다음 토픽으로 전달한다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/cc0523d2-d434-4483-82b9-d07d2ff2d374/image.png" alt="odem">
<code>odem</code> 은 <code>position</code>,<code>orientation</code> 및 <code>선속도</code>, <code>각속도</code> 정보를 담고 있다.
코드를 통해 해당 메세지들이 어떻게 동작하는지 알아보자.</p>
<h2 id="💻1-2코드-구성">💻1-2.코드 구성</h2>
<p>🚀rviz_odom.launch</p>
<pre><code class="language-launch">&lt;launch&gt;
    &lt;param name=&quot;robot_description&quot; textfile=&quot;$(find rviz_xycar)/urdf/xycar_3d.urdf&quot;/&gt;
    &lt;param name=&quot;use_gui&quot; value=&quot;true&quot;/&gt;

    &lt;!-- rviz display --&gt;
    &lt;node name=&quot;rviz_visualizer&quot; pkg=&quot;rviz&quot; type=&quot;rviz&quot; required=&quot;true&quot; 
                args=&quot;-d $(find rviz_xycar)/rviz/rviz_odom.rviz&quot;/&gt;

    &lt;node name=&quot;robot_state_publisher&quot; pkg=&quot;robot_state_publisher&quot; 
                type=&quot;state_publisher&quot;/&gt;

    &lt;node name=&quot;driver&quot; pkg=&quot;rviz_xycar&quot; type=&quot;odom_8_drive.py&quot; /&gt;
    &lt;node name=&quot;odometry&quot; pkg=&quot;rviz_xycar&quot; type=&quot;rviz_odom.py&quot; /&gt;
    &lt;node name=&quot;converter&quot; pkg=&quot;rviz_xycar&quot; type=&quot;converter.py&quot; /&gt;

&lt;/launch&gt;
</code></pre>
<p>🐍odom_8_drive.py</p>
<pre><code class="language-python">import rospy
import time
from xycar_motor.msg import xycar_motor

motor_control = xycar_motor()

rospy.init_node(&#39;driver&#39;)
pub = rospy.Publisher(&#39;xycar_motor&#39;, xycar_motor, queue_size=1)

def motor_pub(angle, speed): 
    global pub
    global motor_control

    motor_control.angle = angle
    motor_control.speed = speed

    pub.publish(motor_control)

speed = 3

while not rospy.is_shutdown():
    angle = -50
    for i in range(70): 
        motor_pub(angle, speed) 
        time.sleep(0.1)

    angle = 0
    for i in range(20):
        motor_pub(angle, speed)
        time.sleep(0.1)

    angle = 50
    for i in range(70):
        motor_pub(angle, speed) 
        time.sleep(0.1)

    angle = 0
    for i in range(20):
        motor_pub(angle, speed) 
        time.sleep(0.1)</code></pre>
<p>8자 주행을 할 코드를 작성한다. <code>angle</code> , <code>speed</code> 값을 <code>xycar_motor</code> 토픽으로 전달한다.
🐍convertor.py</p>
<pre><code class="language-python">import rospy, rospkg
import math
from xycar_motor.msg import xycar_motor
from sensor_msgs.msg import JointState
from std_msgs.msg import Header

global pub
global msg_joint_states
global l_wheel, r_wheel

def callback(data):
    global msg_joint_states,l_wheel, r_wheel, pub
    Angle = data.angle
    msg_joint_states.header.stamp = rospy.Time.now()

    steering = math.radians(Angle * -0.4)

    if l_wheel &gt; 3.14:
        l_wheel = -3.14
        r_wheel = -3.14
    else:
        l_wheel += 0.01
        r_wheel += 0.01

    msg_joint_states.position = [steering, steering, r_wheel, l_wheel, 
                                    r_wheel, l_wheel]    
    pub.publish(msg_joint_states)

def start():
    global msg_joint_states,l_wheel, r_wheel, pub    
    rospy.init_node(&#39;converter&#39;)
    pub = rospy.Publisher(&#39;joint_states&#39;, JointState, queue_size=10)

    msg_joint_states = JointState()
    msg_joint_states.header = Header()
    msg_joint_states.name = [&#39;front_right_hinge_joint&#39;,
                &#39;front_left_hinge_joint&#39;,                       
                &#39;front_right_wheel_joint&#39;, 
                &#39;front_left_wheel_joint&#39;,                               
                &#39;rear_right_wheel_joint&#39;,
                &#39;rear_left_wheel_joint&#39;]
    msg_joint_states.velocity = []
    msg_joint_states.effort = []
    l_wheel = -3.14
    r_wheel = -3.14
    rospy.Subscriber(&quot;xycar_motor&quot;, xycar_motor, callback)
    rospy.spin()

if __name__ == &#39;__main__&#39;:
    start()
</code></pre>
<p><code>xycar_motor</code>의 토픽을 받아서 <code>joint_states</code> 토픽 형태로 가공하여 토픽을 보낸다.</p>
<p>🐍rviz_odom.py</p>
<pre><code class="language-python">import math
from math import sin, cos, pi
import rospy
import tf
from nav_msgs.msg import Odometry
from geometry_msgs.msg import Point, Pose, Quaternion, Twist, Vector3
from sensor_msgs.msg import JointState

global Angle

def callback(msg):  
       global Angle 
       Angle = msg.position[msg.name.index(&quot;front_left_hinge_joint&quot;)]

rospy.Subscriber(&#39;joint_states&#39;, JointState, callback)

rospy.init_node(&#39;odometry_publisher&#39;)

odom_pub = rospy.Publisher(&quot;odom&quot;, Odometry, queue_size=50)

odom_broadcaster = tf.TransformBroadcaster()

current_time = rospy.Time.now()
last_time = rospy.Time.now()

r = rospy.Rate(30.0)

current_speed = 0.4
wheel_base = 0.2
x_ = 0
y_ = 0
yaw_ = 0
Angle = 0

while not rospy.is_shutdown():
    current_time = rospy.Time.now()

    # compute odometry in a typical way given the velocities of the robot
    dt = (current_time - last_time).to_sec()

    current_steering_angle = Angle
    current_angular_velocity = current_speed * math.tan(current_steering_angle) / wheel_base

    x_dot = current_speed * cos(yaw_)
    y_dot = current_speed * sin(yaw_)   
    x_ += x_dot * dt;
    y_ += y_dot * dt;
    yaw_ += current_angular_velocity * dt 

    # since all odometry is 6DOF we&#39;ll need a quaternion created from yaw
    odom_quat = tf.transformations.quaternion_from_euler(0, 0, yaw_)

    # first, we&#39;ll publish the transform over tf
    odom_broadcaster.sendTransform(
        (x_, y_, 0.),
        odom_quat,
        current_time,
        &quot;base_link&quot;,
        &quot;odom&quot;
    )

    # next, we&#39;ll publish the odometry message over ROS
    odom = Odometry()
    odom.header.stamp = current_time
    odom.header.frame_id = &quot;odom&quot;

    # set the position
    odom.pose.pose = Pose(Point(x_, y_, 0.), Quaternion(*odom_quat))

    # set the velocity
    odom.child_frame_id = &quot;base_link&quot;
    #odom.twist.twist = Twist(Vector3(vx, vy, 0), Vector3(0, 0, yaw_))

    # publish the message
    odom_pub.publish(odom)

    last_time = current_time
    r.sleep()</code></pre>
<p><code>joint_states</code> 토픽에서 받은 데이터를 가공하여 <code>odom</code> 토픽을 오도메트리 프로그램으로 전달한다. 메세지에는 속도를 기반으로 계산한 <code>x</code>,<code>y</code> 좌표 및 오일러값을 쿼터니언 값으로 변환한 <code>yaw_</code> 을 담고 있고, Odometry의 헤더를 만든다.</p>
<h1 id="🐜2실행화면">🐜2.실행화면</h1>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/d4ccc00f-fbd9-4735-b8e4-d69fd10e582c/image.gif" alt="">
위 사진과 같이 8자 모양을 그리면서 운행되는 모습을 확인 할 수 있다. 만약 주행을 변경하고 싶으면 <code>odom_8_drive.py</code>에서 값을 일부 수정하면 된다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/48d2a338-86b4-47a4-bc35-00f15891cac0/image.png" alt="">
<code>rqt_graph</code> 로 노드간 통신을 확인.</p>
<h2 id="🚖2-1-번외-rviz-궤적-추가하기">🚖2-1. (번외) Rviz 궤적 추가하기</h2>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/3d3030e3-6a16-42a5-bac1-11225283aa2b/image.png" alt="">
RViz를 실행하면 위 화면과 같이 시뮬레이션 차가 이동해도 궤적이 남지 않는다. 궤적을 확인하기 위해서는 RViz의 Odmoetry 설정을 추가해야한다.</p>
<p><img src="https://velog.velcdn.com/images/dazi2_2/post/a0cae8cf-fa63-4d44-b1f9-7a30ca83223e/image.png" alt="">
RViz의 화면에서 순서대로 버튼을 눌러 <code>Odometry</code>를 추가해준다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/f7d05dd2-c4e2-4d7f-a966-5c32c8babd4e/image.png" alt=""></p>
<p>붉은 화살표가 추가된 모습.
왼쪽의 설정을 통해 궤적의 옵션을 조절 할 수 있다.
이중 <code>Shaft Length</code>항목을 조절하여 화살표의 크기를 작게 만들어 준다.
<img src="https://velog.velcdn.com/images/dazi2_2/post/09ba2036-c8c8-4f66-8bef-9b3255f53f5e/image.png" alt="">
적절한 크기로 조절된 모습 . 입맛에 맞춰 조절하면 된다.</p>
]]></description>
        </item>
    </channel>
</rss>