<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>yujin-shim.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Mon, 22 Apr 2024 13:12:12 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>yujin-shim.log</title>
            <url>https://velog.velcdn.com/images/yujin-shim/profile/7d8914d2-8832-4e73-b3f5-e7b51810eebc/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. yujin-shim.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/yujin-shim" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[OpenAI GYM으로 강화학습 시작하기(1)]]></title>
            <link>https://velog.io/@yujin-shim/OpenAI-GYM%EC%9C%BC%EB%A1%9C-%EA%B0%95%ED%99%94%ED%95%99%EC%8A%B5-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B01</link>
            <guid>https://velog.io/@yujin-shim/OpenAI-GYM%EC%9C%BC%EB%A1%9C-%EA%B0%95%ED%99%94%ED%95%99%EC%8A%B5-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B01</guid>
            <pubDate>Mon, 22 Apr 2024 13:12:12 GMT</pubDate>
            <description><![CDATA[<p>로보틱스에 대한 열정으로 차근차근 나아가보려고 한다!</p>
<p>그 첫걸음으로 강화학습에 대해 알아가보고자 한다.
그 이유는 최근 관심을 가지게 된 뛰어난 성능을 보이는 DreamWaQ가 Deep reinforcement learning을 통해 만들어졌기 때문이다.</p>
<p><a href="https://arxiv.org/abs/2301.10602">DreamWaQ 논문 링크</a></p>
<p>왕초보로서 강화학습에 첫단계부터 나아가고자 한다.</p>
<h2 id="강화학습">강화학습</h2>
<p>강화학습에서는 에이전트가 주변 환경을 observe하고 Policy에 따라 행동을 하게 되며 그 결과로 보상(+/-)을 받는다.
(+)의 보상이 최대가 되도록 학습한다.</p>
<h2 id="openai-gym-환경-만들기">OpenAI GYM 환경 만들기</h2>
<p>virtualenv 설치하고 환경 활성화하기
자신이 원하는 폴더를 만들어 그 안에서 환경을 활성화
그 후 OpenAI GYM을 만들어주면 된다</p>
<blockquote>
<pre><code class="language-linux">  sudo apt install python3-virtualenv
  virtualenv env
  source env/bin/activate
  pip install gym==0.23.1 ```</code></pre>
</blockquote>
<p>그 후 Python 파일에 아래 코드를 넣어주면 된다</p>
<blockquote>
<pre><code>import gym
env = gym.make(&#39;CartPole-v1&#39;)
for i_episode in range(20):
    observation = env.reset()
    for t in range(100):
        env.render()
        print(observation)
        action = env.action_space.sample()
        observation, reward, done, info = env.step(action)
        if done:
            print(&quot;Episode finished after {} timesteps&quot;.format(t+1))
            break
env.close()```</code></pre></blockquote>
<p>진행하면서 이 링크를 참고했다//    <a href="https://jonghyunho.github.io/reinforcement/learning/cartpole-reinforcement-learning.html">OpenAI gym Cartpole</a></p>
<p>이 python 파일을 실행하면 이렇게 막대를 쓰러뜨리지 않기 위한 스텝을 진행하게 된다.</p>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/b0e9a36c-7336-4981-b82f-694331b25a98/image.gif" alt=""></p>
<p>실행해보고 나면 마지막에 얼마나 많은 스텝을 진행했는지 알 수 있는데 내가 실행한 파일에서는 46 step밖에 진행하지 못했다.</p>
<p>그렇다면 계속되는 시리즈동안 신경망으로 더 좋은 정책(Policy)을 만들 수 있는지 알아가보자</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 게임 맵 최단거리(전역변수와 지역변수)]]></title>
            <link>https://velog.io/@yujin-shim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B2%8C%EC%9E%84-%EB%A7%B5-%EC%B5%9C%EB%8B%A8%EA%B1%B0%EB%A6%AC%EC%A0%84%EC%97%AD%EB%B3%80%EC%88%98%EC%99%80-%EC%A7%80%EC%97%AD%EB%B3%80%EC%88%98</link>
            <guid>https://velog.io/@yujin-shim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B2%8C%EC%9E%84-%EB%A7%B5-%EC%B5%9C%EB%8B%A8%EA%B1%B0%EB%A6%AC%EC%A0%84%EC%97%AD%EB%B3%80%EC%88%98%EC%99%80-%EC%A7%80%EC%97%AD%EB%B3%80%EC%88%98</guid>
            <pubDate>Fri, 04 Aug 2023 08:26:29 GMT</pubDate>
            <description><![CDATA[<p>그동안 풀었던 최단거리 문제 = BFS를 이용한 문제로 접근하여 풀었다.</p>
<p>오늘 글에서 다루고 싶은 내용은 문제풀이가 아니다.
벨로그를 조금 더 troubleshooting에 초점을 맞춰서 작성해보려고 한다.
내가 적은 velog를 다시 읽어보니 뭔가 나의 실력을 쌓을 때 도움이 되는 방향인 것 같지 않았다.
백준이나 프로그래머스 문제를 풀면서 만나는 다양한 문제들이 왜 나타났는지, 그리고 어떻게 해결했는지를 중점적으로 적고자 한다.</p>
<p>문제 자체는 전역변수&amp; 지역변수와 관련은 없다.
문제를 만나고 나는 익숙하게 큐를 이용한 BFS 형태로 문제를 풀었다.
그런데, 결과를 제출하고보니 <strong>효율성 부분에서 통과하지 못한 것</strong>이다.</p>
<p>처음에는 if문을 분리하지 않아서 그런가 했다.
수정해도 효율성 통과를 하지 못했으므로 아니다.</p>
<p>그동안 백지 상태에서 코드를 다 적어야하는 백준과 달리 프로그래머스는 기본적인 틀이 제공되어있다.</p>
<p>그래서 내가 아무런 의심이나 생각없이 main문 안에서 변수들을 선언한 것이다.
여기에서 효율성 통과를 하지 못한 이유가 나온다.</p>
<p>왤까?</p>
<p>그전에 한번 공부를 했었던 내용인데 코드를 짜다보니 간과한 부분이다.</p>
<blockquote>
<p>프로그램의 메모리 공간</p>
</blockquote>
<p>프로그램이 운영체제로부터 할당받는 대표적인 메모리공간이 있다.</p>
<ol>
<li>코드 (code) 영역</li>
<li>데이터 (data) 영역</li>
<li>힙 (heap) 영역</li>
<li>스택 (stack) 영역</li>
</ol>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/28997096-d619-480c-a191-aab06f51b741/image.png" alt=""></p>
<p>자료출처: <a href="https://junghn.tistory.com/entry/%EC%BB%B4%ED%93%A8%ED%84%B0-%EA%B8%B0%EC%B4%88-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0-%EC%8A%A4%ED%83%9DStack-%ED%9E%99Heap-%EB%8D%B0%EC%9D%B4%ED%84%B0Data%EC%98%81%EC%97%AD">코딩 시그널</a></p>
<p>내가 main문 안에서 변수를 선언하였으니 이는 지역변수로 선언한 것이다. 이렇게 되면 컴파일 시간에 영향을 미치게 된다.</p>
<p>전역변수는 DATA 영여긍로 훨씬 low memory로 선언할 수 있다.
그래서 내가 변수들을 전역변수로 (main 함수 밖으로) 선언하고 나니 효율성 테스트를 통과한 것이다.</p>
<p>코딩을 하다보면 이런 부분을 놓칠 수도 있다는 생각이 들며 약간의 위기감을 느꼈다...ㅎㅎ
왜냐하면 테스트케이스에서는 에러 없이 잘 작동했기 때문이다.</p>
<p>방심하지 말고 변수를 적는 위치도 신중하게 설정하자 ^^ </p>
<h3 id="전체-코드">전체 코드</h3>
<pre><code class="language-cpp">#include&lt;vector&gt;
#include &lt;queue&gt;
using namespace std;

int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
int count[101][101];
bool visited[101][101];
queue&lt;pair&lt;int,int&gt;&gt; q;

int solution(vector&lt;vector&lt;int&gt;&gt; maps)
{
    int answer = 0;
    int n = maps.size();
    int m = maps[0].size();

    q.push({0,0});
    visited[0][0] = true;
    count[0][0] = 1;

    while(!q.empty())
    {
        int x = q.front().first;
        int y = q.front(). second;
        q.pop();

        for(int step = 0; step&lt;4; step++)
        {
            int x_step = x + dx[step];
            int y_step = y + dy[step];

            if (x_step &lt; 0 || y_step &lt; 0 || x_step &gt;= n || y_step &gt;=m) continue;
            if (visited[x_step][y_step]) continue;
            if (maps[x_step][y_step] != 1) continue;


            visited[x_step][y_step] = true;
            q.push({x_step, y_step});
            count[x_step][y_step] = count[x][y] + 1;
        }
    }

    answer = count[n-1][m-1];
    if(!answer)
        answer = -1;
    return answer;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 2178 미로 탐색]]></title>
            <link>https://velog.io/@yujin-shim/%EB%B0%B1%EC%A4%80-2178-%EB%AF%B8%EB%A1%9C-%ED%83%90%EC%83%89</link>
            <guid>https://velog.io/@yujin-shim/%EB%B0%B1%EC%A4%80-2178-%EB%AF%B8%EB%A1%9C-%ED%83%90%EC%83%89</guid>
            <pubDate>Thu, 13 Jul 2023 10:21:40 GMT</pubDate>
            <description><![CDATA[<h1 id="문제-링크-미로-탐색">문제 링크: <a href="https://www.acmicpc.net/problem/2178">미로 탐색</a></h1>
<h2 id="문제">문제</h2>
<p>N×M크기의 배열로 표현되는 미로가 있다.</p>
<p>1    0    1    1    1    1
1    0    1    0    1    0
1    0    1    0    1    1
1    1    1    0    1    1</p>
<p>미로에서 1은 이동할 수 있는 칸을 나타내고, 0은 이동할 수 없는 칸을 나타낸다. 이러한 미로가 주어졌을 때, (1, 1)에서 출발하여 (N, M)의 위치로 이동할 때 지나야 하는 최소의 칸 수를 구하는 프로그램을 작성하시오. 한 칸에서 다른 칸으로 이동할 때, 서로 인접한 칸으로만 이동할 수 있다.</p>
<p>위의 예에서는 15칸을 지나야 (N, M)의 위치로 이동할 수 있다. 칸을 셀 때에는 시작 위치와 도착 위치도 포함한다.</p>
<h2 id="입력">입력</h2>
<p>첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 <strong>붙어서</strong> 입력으로 주어진다.</p>
<h2 id="출력">출력</h2>
<p>첫째 줄에 지나야 하는 최소의 칸 수를 출력한다. 항상 도착위치로 이동할 수 있는 경우만 입력으로 주어진다.</p>
<h1 id="문제-풀이-과정">문제 풀이 과정</h1>
<p>많이 풀어본 유형의 문제이기 때문에 푸는 과정에서 크게 어려움은 없었다.
이런 유형의 문제의 경우 자연스럽게 BFS의 활용이 떠올라야 한다.
DFS로도 풀 수는 있지만 출구를 찾을 때까지 완전 탐색의 방식이기 때문에 시간 복잡도가 높아진다.
넓게 갈 수 있는 step을 map 상에서 더해가면서 이 칸까지는 7step, 저 칸까지는 9step 이런식으로 숫자를 늘려가면서 모든 1이 있는 칸까지 이동한데 필요한 step 수를 적고 마지막에
출구 칸만 출력하면 된다.</p>
<p>코드를 보면 설명이 더 쉽다.</p>
<h2 id="code1---input-받기">code(1) - input 받기</h2>
<p>어쩌면 input을 받는 부분에서 흠칫했다 ㅎㅎ
1011011101 이런 값들이 다 붙어서 들어오기 때문이다.
이렇게 다 붙어서 들어오니 난 string으로 받아서 하나씩 분리, int로 바꿔서 matrix에 넣어줬다.</p>
<pre><code class="language-cpp">
void input()
{
    cin &gt;&gt; n &gt;&gt; m;
    for (int i = 0; i &lt; n; i++)
    {
        string temp;
        cin &gt;&gt; temp;
        for (int j = 0; j &lt; m; j++)
        {
            map[i][j] = temp[j] - &#39;0&#39;;
        }

    }
}</code></pre>
<h2 id="code2---bfs">code(2) - BFS</h2>
<p>codingtest 시리즈에서 앞에 풀었던 bfs와 다른 방식이 있다면 
상하좌우로 이동하면서 그 map의 값이 0 이상인 경우(이동 가능한 경우) queue에 넣고 
지금까지 이동해온 칸 수를 더해주었다.</p>
<pre><code class="language-cpp">

void bfs()
{
    // start point = 0,0
    visited[0][0] = true;
    q.push({ 0,0 });

    while (!q.empty())
    {
        int x = q.front().first;
        int y = q.front().second;
        q.pop();

        for (int d = 0; d &lt; 4; d++)
        {
            int x_step = x + dx[d];
            int y_step = y + dy[d];    
            if (!visited[x_step][y_step] and map[x_step][y_step] &gt; 0 and x_step &gt;= 0 and y_step &gt;= 0 and x_step &lt; n and y_step &lt; m)
            {
                // not visited, map value is 1 and not out range of map
                visited[x_step][y_step] = true;
                q.push({ x_step, y_step });
                map[x_step][y_step] = map[x][y] + 1;
            }
        }
    }
}</code></pre>
<h1 id="전체-코드">전체 코드</h1>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;queue&gt;

using namespace std;

//initialization
int n, m;
int map[101][101];
int visited[101][101];
queue &lt;pair&lt;int,int&gt;&gt; q;
int dx[4] = { -1, 0, 1, 0 };
int dy[4] = { 0, -1, 0, 1 };

void input()
{
    cin &gt;&gt; n &gt;&gt; m;
    for (int i = 0; i &lt; n; i++)
    {
        string temp;
        cin &gt;&gt; temp;
        for (int j = 0; j &lt; m; j++)
        {
            map[i][j] = temp[j] - &#39;0&#39;;
        }

    }
}

void bfs()
{
    // start point = 0,0
    visited[0][0] = true;
    q.push({ 0,0 });

    while (!q.empty())
    {
        int x = q.front().first;
        int y = q.front().second;
        q.pop();

        for (int d = 0; d &lt; 4; d++)
        {
            int x_step = x + dx[d];
            int y_step = y + dy[d];    
            if (!visited[x_step][y_step] and map[x_step][y_step] &gt; 0 and x_step &gt;= 0 and y_step &gt;= 0 and x_step &lt; n and y_step &lt; m)
            {
                // not visited, map value is 1 and not out range of map
                visited[x_step][y_step] = true;
                q.push({ x_step, y_step });
                map[x_step][y_step] = map[x][y] + 1;
            }
        }
    }
}
int main()
{
    input();
    bfs();
    cout &lt;&lt; map[n-1][m-1];
    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 1260 DFS와 BFS]]></title>
            <link>https://velog.io/@yujin-shim/CodingTestcpp-%EB%B0%B1%EC%A4%80-1260-DFS%EC%99%80-BFS</link>
            <guid>https://velog.io/@yujin-shim/CodingTestcpp-%EB%B0%B1%EC%A4%80-1260-DFS%EC%99%80-BFS</guid>
            <pubDate>Wed, 12 Jul 2023 09:47:17 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>코테 문제를 풀면서 내가 실수를 하는 부분은 크게 2가지다.</p>
<blockquote>
<ol>
<li>변수 크기 잘못 설정<ol start="2">
<li>for loop 변수 이름 (중첩 for loop을 다 i로 한다던지...) 혹은 범위 잘못 설정</li>
</ol>
</li>
</ol>
</blockquote>
</blockquote>
<h1 id="문제-링크-dfs와-bfs">문제 링크: <a href="https://www.acmicpc.net/problem/1260">DFS와 BFS</a></h1>
<h2 id="문제">문제</h2>
<p>그래프를 DFS로 탐색한 결과와 BFS로 탐색한 결과를 출력하는 프로그램을 작성하시오. 단, 방문할 수 있는 정점이 여러 개인 경우에는 <strong>정점 번호가 작은 것을 먼저 방문</strong>하고, 더 이상 방문할 수 있는 점이 없는 경우 종료한다. 정점 번호는 1번부터 N번까지이다.</p>
<p>.
.</p>
<hr>
<h1 id="문제-풀이-과정">문제 풀이 과정</h1>
<p>일단 오랜만에 다시 코테 문제들을 푸는 과정이라 쉬운 문제부터 차근차근 푸는 중이다.</p>
<h2 id="dfs-깊이-우선-탐색">DFS (깊이 우선 탐색)</h2>
<ul>
<li>재귀함수나 스택을 이용한다.</li>
</ul>
<h2 id="bfs-너비-우선-탐색">BFS (너비 우선 탐색)</h2>
<ul>
<li>큐를 이용한다.</li>
</ul>
<blockquote>
<p>이 부분은 코드를 통해 더 자세하게 설명해보겠다.</p>
</blockquote>
<p>.
.</p>
<h2 id="code1---input-받기">code(1) - input 받기</h2>
<p>input을 받는 부분에서 놓쳐서는 안되는 부분이 있다.
보통 문제에 내가 bold 처리해둔 것을 잘 보면 코딩에 챙겨야 할 부분들이 명시되어 있다.</p>
<p>방문할 정점이 여러개인 경우에는 정점번호가 작은 것부터 방문하라고 되어있으므로
노드별로 연결된 노드를 push_back으로 넣어준 후 sort를 통해 <span style='background-color:#dcffe4'>정렬을 해주어야</span> 한다.</p>
<p>또한 이 정렬을 할 때 노드별로 하나씩 진행하는데 여기서 주의할 부분은 for loop의 idx가 곧 노드의 번호라는 점이다. 따라서 1부터 시작해야 한다.</p>
<p>아래 코드를 진행하고 나면 아래와 같이 graph에 저장된다고 생각하면 된다.</p>
<p>예제 1번을 기준으로
graph[1] = {2, 3, 4}
graph[2] = {1, 4}
graph[3] = {1, 4}
graph[4] = {1, 2, 3}</p>
<pre><code class="language-cpp">
void input()
{
    cin &gt;&gt; n &gt;&gt; m &gt;&gt; start_point;
    for (int i = 1; i &lt;= m; i++)
    {
        cin &gt;&gt; a &gt;&gt; b;
        graph[a].push_back(b);
        graph[b].push_back(a);
    }
    for (int i = 1; i &lt;= n; i++)
    {
        sort(graph[i].begin(), graph[i].end());

    }
}
</code></pre>
<p>.
.</p>
<h2 id="code2---dfs">code(2) - DFS</h2>
<p>DFS는 깊이 우선 탐색으로 기준 노드에서 탐색할 수 있는 가장 깊은 노드까지 갔다가 돌아와 탐색하는 방식이다.
스택이나 재귀함수를 이용해 구현할 수 있다.</p>
<p>나는 재귀함수로 구현하였다.</p>
<p>일단 작동 방식을 설명하자면 
예제 1을 기준으로 </p>
<p>1번이 start이므로</p>
<ol>
<li>1번 노드 방문처리</li>
<li>1번 노드 출력</li>
<li>1번 노드와 연결된 노드 차례대로 방문 (위 input 함수에서 오름차순으로 정렬)</li>
<li>방문 안한 경우 재귀함수(방문처리, 출력, 연결된 노드 차례대로 방문)</li>
</ol>
<p>이런 방식으로 진행된다.
따라서 1 &rarr; 2 &rarr; 4 &rarr; 3</p>
<p>&lt; 그래프 형태 &gt;
graph[1] = {2, 3, 4}
graph[2] = {1, 4}
graph[3] = {1, 4}
graph[4] = {1, 2, 3}</p>
<p>dfs(1) -&gt; dfs(2) -&gt; dfs (4) -&gt; dfs(3) 
재귀함수의 흐름을 나타내자면 이런 형태 인 것이다.</p>
<p>dfs(2)에서 dfs(4)로 넘어가는 이유는 1은 이미 방문하여 방문처리 되었기 때문이다.</p>
<pre><code class="language-cpp">
void dfs(int start)
{
    visited[start] = true;
    cout &lt;&lt; start &lt;&lt; &#39; &#39;;
    for (int i = 0; i &lt; (int)graph[start].size(); i++)
    {
        int y = graph[start][i];
        if (!visited[y])
            dfs(y);
    }

}</code></pre>
<p>.
.</p>
<h2 id="code3---bfs">code(3) - BFS</h2>
<p>BFS는 큐를 사용한다.
방문하고자 하는 노드와 연결된 노드 중에서 방문하지 않은 노드를 모두 큐에 넣고
큐에서 하나씩 꺼내어 연결된 노드를 찾는 방식이다.</p>
<p>예시 1을 기준으로 설명해보자면</p>
<p>&lt; 그래프 형태 &gt;
graph[1] = {2, 3, 4}
graph[2] = {1, 4}
graph[3] = {1, 4}
graph[4] = {1, 2, 3}</p>
<ol>
<li>1번 노드 방문</li>
<li>queue에 1번 노드와 연결된 노드 중 방문하지 않은 노드 넣고 방문처리하기
 q = {2, 3, 4}</li>
<li>queue에서 앞에서부터 빼면서 출력, 또 연결된 노드 중 방문하지 않은 노드 큐에 넣기 (예시1에서는 없음)</li>
</ol>
<pre><code class="language-cpp">
void bfs(int start)
{
    visited_b[start] = true;
    q.push(start);
    while (!q.empty())
    {
        int x = q.front();
        q.pop();
        cout &lt;&lt; x &lt;&lt; &#39; &#39;;
        for (int i = 0; i &lt; (int)graph[x].size(); i++)
        {
            int y = graph[x][i];
            if (!visited_b[y])
            {
                visited_b[y] = true;
                q.push(y);
            }
        }
    }

}</code></pre>
<h1 id="전체-코드cpp">전체 코드(cpp)</h1>
<pre><code class="language-cpp">
#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;queue&gt;
#include &lt;algorithm&gt;
using namespace std;

//initialization
int n;
int m;
int a, b;
int start_point;
vector&lt;int&gt; graph[1001];
bool visited[1001];
bool visited_b[1001];
queue&lt;int&gt; q;

void input()
{
    cin &gt;&gt; n &gt;&gt; m &gt;&gt; start_point;
    for (int i = 1; i &lt;= m; i++)
    {
        cin &gt;&gt; a &gt;&gt; b;
        graph[a].push_back(b);
        graph[b].push_back(a);
    }
    for (int i = 1; i &lt;= n; i++)
    {
        sort(graph[i].begin(), graph[i].end());

    }
}

void dfs(int start)
{
    visited[start] = true;
    cout &lt;&lt; start &lt;&lt; &#39; &#39;;
    for (int i = 0; i &lt; (int)graph[start].size(); i++)
    {
        int y = graph[start][i];
        if (!visited[y])
            dfs(y);
    }

}

void bfs(int start)
{
    visited_b[start] = true;
    q.push(start);
    while (!q.empty())
    {
        int x = q.front();
        q.pop();
        cout &lt;&lt; x &lt;&lt; &#39; &#39;;
        for (int i = 0; i &lt; (int)graph[x].size(); i++)
        {
            int y = graph[x][i];
            if (!visited_b[y])
            {
                visited_b[y] = true;
                q.push(y);
            }
        }
    }

}

int main()
{
    input();
    dfs(start_point);
    cout &lt;&lt; endl;
    bfs(start_point);
    return 0;
}
</code></pre>
<p>.
.
.
.</p>
<h1 id="전체코드-python">전체코드 (python)</h1>
<p>코테를 준비하다보니 python으로도 문제를 풀어보고있다.</p>
<pre><code class="language-python">from sys import stdin
from collections import deque
# func
def dfs(start):
    visited[start] = True
    print(start, end=&quot; &quot;)
    for alpha in graph[start]:
        if not visited[alpha]:
            dfs(alpha)

def bfs(start):
    visited2[start] = True
    q = deque([start])
    while q:
        alpha = q.popleft()
        print(alpha, end=&quot; &quot;)
        for i in graph[alpha]:
            if not visited2[i]:
                visited2[i] = True
                q.append(i)


# input
node, line, stp = map(int, stdin.readline().split())
graph = [[] for _ in range(node+1)]
visited = [False] * (node+2)
visited2 = [False] * (node+2)

for _ in range(line):
    x,y = map(int,stdin.readline().split())
    graph[x].append(y)
    graph[y].append(x)

for i in graph:
    i.sort()

dfs(stp)
print()
bfs(stp)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 2775 부녀회장이 될테야]]></title>
            <link>https://velog.io/@yujin-shim/CodingTestcpp-%EB%B0%B1%EC%A4%80-2775-%EB%B6%80%EB%85%80%ED%9A%8C%EC%9E%A5%EC%9D%B4-%EB%90%A0%ED%85%8C%EC%95%BC-dynamic-programming</link>
            <guid>https://velog.io/@yujin-shim/CodingTestcpp-%EB%B0%B1%EC%A4%80-2775-%EB%B6%80%EB%85%80%ED%9A%8C%EC%9E%A5%EC%9D%B4-%EB%90%A0%ED%85%8C%EC%95%BC-dynamic-programming</guid>
            <pubDate>Wed, 12 Jul 2023 09:21:35 GMT</pubDate>
            <description><![CDATA[<h1 id="문제-링크-부녀회장이-될테야">문제 링크: <a href="https://www.acmicpc.net/problem/2775">부녀회장이 될테야</a></h1>
<p>주희의 반상회 주최를 도와봅시다...
DP를 완벽히 이해하고자 DP 문제를 연속으로 풀어보았다.</p>
<h2 id="문제">문제</h2>
<p>평소 반상회에 참석하는 것을 좋아하는 주희는 이번 기회에 부녀회장이 되고 싶어 각 층의 사람들을 불러 모아 반상회를 주최하려고 한다.</p>
<p>이 아파트에 거주를 하려면 조건이 있는데, “a층의 b호에 살려면 자신의 아래(a-1)층의 1호부터 b호까지 사람들의 수의 합만큼 사람들을 데려와 살아야 한다” 는 계약 조항을 꼭 지키고 들어와야 한다.</p>
<p>아파트에 비어있는 집은 없고 모든 거주민들이 이 계약 조건을 지키고 왔다고 가정했을 때, 주어지는 양의 정수 k와 n에 대해 k층에 n호에는 몇 명이 살고 있는지 출력하라. 단, 아파트에는 0층부터 있고 <strong>각층에는 1호부터 있으며</strong>, <strong>0층의 i호에는 i명이 산다</strong>.</p>
<h2 id="입력">입력</h2>
<p>첫 번째 줄에 Test case의 수 T가 주어진다. 그리고 각각의 케이스마다 입력으로 첫 번째 줄에 정수 k, 두 번째 줄에 정수 n이 주어진다</p>
<h2 id="출력">출력</h2>
<p>각각의 Test case에 대해서 해당 집에 거주민 수를 출력하라.</p>
<hr>
<h1 id="문제풀이-과정">문제풀이 과정</h1>
<p>Dynamic programming 문제이므로 dp 식에서 규칙을 찾는 게 중요하다.</p>
<h2 id="dp-규칙-찾기">DP 규칙 찾기</h2>
<blockquote>
<p>일단 0층 i호에는 i명이 산다고 했으므로 0층 1호는 1명, 0층 2호는 2명 이런식이다</p>
</blockquote>
<p>따라서 아래표에서 0층은 생략하겠다.</p>
<table>
<thead>
<tr>
<th>floor</th>
<th>room</th>
<th>people</th>
<th>DP 식</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>1</td>
<td>1</td>
<td>dp[1][1] = 1</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>dp[1][2] = dp[1][1] + dp[0][2]</td>
</tr>
<tr>
<td>1</td>
<td>3</td>
<td>6</td>
<td>dp[1][3] = dp[1][2] + dp[0][3]</td>
</tr>
<tr>
<td>.</td>
<td>.</td>
<td>.</td>
<td>...</td>
</tr>
<tr>
<td>2</td>
<td>1</td>
<td>1</td>
<td>dp[2][1] = 1</td>
</tr>
<tr>
<td>2</td>
<td>2</td>
<td>4</td>
<td>dp[2][2] = dp[2][1] + dp[1][2]</td>
</tr>
</tbody></table>
<p>이쯤 적다보면 규칙이 눈에 보이기 시작한다.
일단 모든 층의 1호는 1명이고, (0층 1호의 명수가 1명이므로)</p>
<h3 id="dp-식--span-stylebackground-colordcffe4dpij--dpij-1--dpi-1jspan">DP 식 : <span style='background-color:#dcffe4'><strong>dp[i][j] = dp[i][j-1] + dp[i-1][j]</strong></span></h3>
<hr>
<h1 id="전체-코드">전체 코드</h1>
<pre><code class="language-cpp">
#include &lt;iostream&gt;

using namespace std;

int input_f;
int input_r;
int input_floor[10000];
int input_room[10000];
int testcase;
int dp[15][15];

int count(int floor, int room)
{
    for (int f = 1; f &lt;= floor; f++)
    {
        for (int r = 2; r &lt;= room; r++)
        {
            dp[f][r] = dp[f][r - 1] + dp[f - 1][r];
        }
    }
    return 0;
}

int main()
{
    // initialization
    for (int i = 1; i &lt;= 14; i++)
    {
        // floor 0 - room 1~14
        dp[0][i] = i;
        dp[i][1] = 1;
    }

    cin &gt;&gt; testcase;

    for (int t = 0; t &lt; testcase; t++)
    {
        cin &gt;&gt; input_f &gt;&gt; input_r;
        input_floor[t] = input_f;
        input_room[t] = input_r;
    }
    for (int t = 0; t &lt; testcase; t++)
    {
        count(input_floor[t], input_room[t]);
        cout &lt;&lt; dp[input_floor[t]][input_room[t]] &lt;&lt; endl;
    }
    return 0;
}

</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 1463 1로 만들기 ]]></title>
            <link>https://velog.io/@yujin-shim/CodingTestcpp-%EB%B0%B1%EC%A4%80-1463-1%EB%A1%9C-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@yujin-shim/CodingTestcpp-%EB%B0%B1%EC%A4%80-1463-1%EB%A1%9C-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Wed, 12 Jul 2023 05:48:24 GMT</pubDate>
            <description><![CDATA[<h1 id="문제-링크-1로-만들기">문제 링크: <a href="https://www.acmicpc.net/problem/1463">1로 만들기</a></h1>
<hr>
<h1 id="문제-풀이-과정">문제 풀이 과정</h1>
<p>Dynamic Programming 문제이다.
DP를 공부하며 참고한 링크 : <a href="https://hongjw1938.tistory.com/47">Dynamic Programming</a></p>
<p>이 문제를 풀면서 고민을 정말 많이했다.
DP 문제를 많이 안 풀어봐서 DP를 공부하면서 풀 겸 결국 검색해서 풀이를 보았다.
<a href="https://ssinee.tistory.com/entry/%EB%B0%B1%EC%A4%80-1463%EB%B2%88-1%EB%A1%9C-%EB%A7%8C%EB%93%A4%EA%B8%B0-CDP">참고한 문제풀이</a></p>
<h2 id="dp-규칙-찾기">DP 규칙 찾기</h2>
<p>일단 전체적으로 한번씩 적으며 dp식이 어떻게 반복되는가를 찾았다.</p>
<table>
<thead>
<tr>
<th>idx = input</th>
<th>calculation</th>
<th>dp 식</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>0</td>
<td>dp[1] = 0</td>
</tr>
<tr>
<td>2</td>
<td>2 &rarr; 1</td>
<td>dp[2] = 1</td>
</tr>
<tr>
<td>3</td>
<td>3 &rarr; 1</td>
<td>dp[3] = 1</td>
</tr>
<tr>
<td>4</td>
<td>4 &rarr; 2 &rarr; 1</td>
<td>dp[4] = 1+dp[i/2]</td>
</tr>
<tr>
<td>5</td>
<td>5 &rarr; 4 &rarr; 2 &rarr; 1</td>
<td>dp[5] = 1+dp[i-1]</td>
</tr>
<tr>
<td>6</td>
<td>6 &rarr; 3 &rarr; 1 or 6 &rarr; 2 &rarr; 1</td>
<td>dp[6] = 1+dp[i/3] or dp[6] = 1+dp[i/2]</td>
</tr>
<tr>
<td>7</td>
<td>7 &rarr; 6 &rarr; 3 &rarr; 1</td>
<td>dp[7] = 1+dp[i]</td>
</tr>
<tr>
<td>8</td>
<td>8 &rarr; 4 &rarr; 2 &rarr; 1</td>
<td>dp[8] = 1+dp[i/2]</td>
</tr>
<tr>
<td>9</td>
<td>9 &rarr; 3 &rarr; 1</td>
<td>dp[9] = 1+dp[i/3]</td>
</tr>
<tr>
<td>10</td>
<td>10 &rarr; 9 &rarr; 3 &rarr; 1</td>
<td>dp[10] = 1+dp[i-1]</td>
</tr>
<tr>
<td>11</td>
<td>11 &rarr; 10 &rarr; 9 &rarr; 3 &rarr; 1</td>
<td>dp[11] = 1+dp[i-1]</td>
</tr>
</tbody></table>
<blockquote>
<p>반드시 dp[i] = 1+dp[i-1] 은 성립한다.</p>
</blockquote>
<p>이렇게 다 적고보면 규칙이 전체적으로 보인다.</p>
<ol>
<li>2로도 3으로도 안나눠지는 값</li>
<li>2로도 3으로도 나눠지는 값</li>
<li>2로 나눠지는 값</li>
<li>3으로 나눠지는 값</li>
</ol>
<p>.
.</p>
<h3 id="2로도-3으로도-안나눠지는-값">2로도 3으로도 안나눠지는 값</h3>
<p>위 표에서는 5, 7, 11이 될 수 있겠다.
이런 경우는 무조건 1을 뺀 후 이전 dp값을 사용하는 경우 뿐이기 때문에
<strong>dp[i] = 1+dp[i-1]</strong></p>
<h3 id="2로도-3으로도-나눠지는-값">2로도 3으로도 나눠지는 값</h3>
<p>6, 12와 같은 값!
이런 경우 2개의 경우의 수 중에 연산의 개수가 작은 것을 선택한다.
** min(d[i] = 1+dp[i/2], dp[i] = 1+dp[i/3]) **</p>
<h3 id="2로-나눠지는-값">2로 나눠지는 값</h3>
<p>4, 8, 10
이런 경우는 이전 값을 사용하는 dp 식과 i/2 dp식을 이용하는 것 중 작은 것을 선택
** min(dp[i] = 1+dp[i-1], dp[i] = 1+dp[i/2])**</p>
<h3 id="3으로-나눠지는-값">3으로 나눠지는 값</h3>
<p>9와 같은 값</p>
<p>** min(dp[i] = 1+dp[i-1], dp[i] = 1+dp[i/3])**</p>
<h1 id="전체코드">전체코드</h1>
<pre><code class="language-cpp">#include &lt;iostream&gt;

using namespace std;

// variable
int dp[1000001];
int input;

int main()
{
    cin &gt;&gt; input;
    dp[1] = 0;
    dp[2] = 1;
    dp[3] = 1;
    dp[4] = 2;

    for (int i = 5; i &lt;= input; i++)
    {
        if (i % 2 != 0 &amp;&amp; i % 3 != 0)
        {
            dp[i] = 1 + dp[i - 1];
        }
        else if (i % 2 == 0 &amp;&amp; i % 3 == 0)
        {
            dp[i] = min(1 + dp[i / 2], 1 + dp[i / 3]);
        }
        else if (i % 2 == 0)
        {
            dp[i] = min(1 + dp[i / 2], 1 + dp[i - 1]);
        }
        else if (i % 3 == 0)
        {
            dp[i] = min(1 + dp[i / 3], 1 + dp[i - 1]);
        }

    }

    cout &lt;&lt; dp[input];
    return 0;
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 2309 일곱 난쟁이]]></title>
            <link>https://velog.io/@yujin-shim/CodingTestcpp-%EB%B0%B1%EC%A4%80-2309</link>
            <guid>https://velog.io/@yujin-shim/CodingTestcpp-%EB%B0%B1%EC%A4%80-2309</guid>
            <pubDate>Wed, 05 Jul 2023 11:38:51 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-링크--일곱-난쟁이">문제 링크 : <a href="https://www.acmicpc.net/problem/2309">일곱 난쟁이</a></h2>
<h3 id="문제">문제</h3>
<p>왕비를 피해 일곱 난쟁이들과 함께 평화롭게 생활하고 있던 백설공주에게 위기가 찾아왔다. 일과를 마치고 돌아온 난쟁이가 <strong>일곱 명이 아닌 아홉 명</strong> 이었던 것이다.</p>
<p>아홉 명의 난쟁이는 모두 자신이 &quot;백설 공주와 일곱 난쟁이&quot;의 주인공이라고 주장했다. 뛰어난 수학적 직관력을 가지고 있던 백설공주는, 다행스럽게도 <strong>일곱 난쟁이의 키의 합이 100</strong>이 됨을 기억해 냈다.</p>
<p>아홉 난쟁이의 키가 주어졌을 때, 백설공주를 도와 일곱 난쟁이를 찾는 프로그램을 작성하시오.</p>
<h3 id="입력">입력</h3>
<p>아홉 개의 줄에 걸쳐 난쟁이들의 키가 주어진다. 주어지는 키는 100을 넘지 않는 자연수이며, 아홉 난쟁이의 <strong>키는 모두 다르며</strong>, 가능한 정답이 여러 가지인 경우에는 <strong>아무거나 출력</strong>한다.</p>
<h3 id="출력">출력</h3>
<p>일곱 난쟁이의 키를 <strong>오름차순으로 출력</strong>한다. 일곱 난쟁이를 찾을 수 없는 경우는 없다.</p>
<hr>
<hr>
<h2 id="문제-풀이-과정">문제 풀이 과정</h2>
<p>눈 여겨봐야 할 내용들을 bold 처리 해두었다.
문제를 보면 다행히 input이 모두 다르고, 아무거나 출력하므로 중복처리나 조건처리가 필요하지 않으므로 비교적 쉬운 문제이다! :)</p>
<p>코드의 구조를 뜯어보면 다음과 같다.</p>
<p>전체 코드는 맨 아래 나와있다. 👍</p>
<h3 id="input-받아-정렬하기">input 받아 정렬하기</h3>
<p>나는 vector를 사용하기로 했다.</p>
<pre><code class="language-cpp"> for (int i =0; i&lt;dwarf; i++)
    {
        int temp;
        cin &gt;&gt; temp;
        h.push_back(temp);
    }

 sort(h.begin(), h.end());</code></pre>
<h3 id="합쳐서-100이-되는-7명-찾기">합쳐서 100이 되는 7명 찾기</h3>
<p>거꾸로 생각하면 
전체 합친 키에서 빼서 100이 되는 2명을 찾으면 된다.</p>
<blockquote>
<p>이런식으로 전체 개수 (tot) 이 주어지고 그 중에 n개 찾기와 같은 문제는
(tot-n) 을 생각해보면 빨리 문제를 해결할 수 있다.</p>
</blockquote>
<p>중간에 주석처리한 cout 부분을 주석 해제하고 출력해보면
전체 키를 합한 값에서 뺀 2명의 조합과 뺀 값도 확인해 볼 수 있다.</p>
<ul>
<li>출력단을 따로 뺄 수도 있지만
문제에서 주어진 것에서 정답이 여러개면 <strong>아무거나 출력하는 것이 조건이길래
그냥 발견하는대로 출력하고 종료</strong>되도록 만들었다.</li>
</ul>
<p>따라서 if 문에서 (전체 키의 합) - (2명 난쟁이의 키의 합)이 100이면
그 두명을 제외하고 vector를 출력하도록 되어있다.</p>
<p>여기서 주의해야할 부분은
내가 <strong>i &lt; j 가 되도록 했기 때문에 뒤에 있는 index 값인 j를 먼저 제거</strong>해주어야 한다.</p>
<p>그렇지 않고 i 부터 제거하면 index가 앞으로 채워지면서 j의 index가 가르키는 값이 달라지기 때문이다.</p>
<p>(j+1을 해도된다, 항상 2명이기 때문에)</p>
<pre><code class="language-cpp">    ans = accumulate(h.begin(), h.end(), 0);

    // subtract 2 numbers untill the ans gets 100
    for (int i =0; i &lt;dwarf-1; i++)
    {
        for (int j=i+1; j&lt; dwarf; j++)
        {
            // cout &lt;&lt; &quot;(&quot; &lt;&lt; h[i] &lt;&lt;&quot;,&quot; &lt;&lt; h[j] &lt;&lt; &quot;)&quot; &lt;&lt; endl;
            // cout &lt;&lt; (ans -  (h[i] +h[j])) &lt;&lt; endl;

            if((ans -  (h[i] +h[j])) == 100)
            {
                h.erase(remove(h.begin(),h.end(), h[j]));
                h.erase(remove(h.begin(),h.end(), h[i]));

                for (auto loop:h)
                {
                    cout &lt;&lt; loop &lt;&lt; endl;
                }
                return 0;
            }

        }
    }

</code></pre>
<hr>
<hr>
<h2 id="전체-코드">전체 코드</h2>
<pre><code class="language-cpp">
#include &lt;iostream&gt;
#include &lt;algorithm&gt;
#include &lt;numeric&gt;
#include &lt;vector&gt;

using namespace std;
vector&lt;int&gt; h;
int dwarf = 9;
int ans;

int main()
{
    for (int i =0; i&lt;dwarf; i++)
    {
        int temp;
        cin &gt;&gt; temp;
        h.push_back(temp);
    }

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

    // add all the heights
    ans = accumulate(h.begin(), h.end(), 0);

    // subtract 2 numbers untill the ans gets 100
    for (int i =0; i &lt;dwarf-1; i++)
    {
        for (int j=i+1; j&lt; dwarf; j++)
        {
            // cout &lt;&lt; &quot;(&quot; &lt;&lt; h[i] &lt;&lt;&quot;,&quot; &lt;&lt; h[j] &lt;&lt; &quot;)&quot; &lt;&lt; endl;
            // cout &lt;&lt; (ans -  (h[i] +h[j])) &lt;&lt; endl;

            if((ans -  (h[i] +h[j])) == 100)
            {
                h.erase(remove(h.begin(),h.end(), h[j]));
                h.erase(remove(h.begin(),h.end(), h[i]));

                for (auto loop:h)
                {
                    cout &lt;&lt; loop &lt;&lt; endl;
                }
                return 0;
            }

        }
    }
}</code></pre>
<h2 id="끝">끝!</h2>
]]></description>
        </item>
        <item>
            <title><![CDATA[[cpp] Code Note]]></title>
            <link>https://velog.io/@yujin-shim/cpp-Code-Note</link>
            <guid>https://velog.io/@yujin-shim/cpp-Code-Note</guid>
            <pubDate>Wed, 05 Jul 2023 10:43:04 GMT</pubDate>
            <description><![CDATA[<p>예전에 코딩테스트를 준비하면서 
인상적인 함수나 몰랐던 방법들을 볼 때마다 notion에 적어두곤 했는데, 
이제 TIL를 velog로 옮기면서 조금씩 옮기고 있다. </p>
<p>(notion에서 내보내기 &gt; markdown 으로 하면 velog에 아주 잘 호환된다.)</p>
<h1 id="code-note">Code Note</h1>
<aside>
💡 To master Python & C++

</aside>

<ul>
<li>Tips<ul>
<li>for 문에서 len(list)같이 계산이 필요한 것들 loop에 넣으면 시간 복잡도 급 상승, for loop전에 변수로 선언해주고 변수만 넣어주기</li>
<li>vector에서는 마지막요소idx로 -1 안됨! v.back() 이용~</li>
</ul>
</li>
</ul>
<h3 id="char-to-int">Char to int</h3>
<h3 id="노트">노트</h3>
<pre><code class="language-cpp">char c = &#39;1&#39;;
int n = c - &#39;0&#39;;
// n = 1</code></pre>
<pre><code class="language-python"></code></pre>
<hr>
<h3 id="이진수">이진수</h3>
<h3 id="1의-갯수-세기">1의 갯수 세기</h3>
<pre><code class="language-cpp">int n = 10;
int x = __builtin_popcount(n);

//gcc 컴파일러 내장함수 - unsigned int를 받아서 1인 bit의 개수를 리턴
// unsigned int = 32bit -&gt; 시간복잡도 O(32)</code></pre>
<pre><code class="language-python"></code></pre>
<hr>
<h3 id="priority-queue">priority queue</h3>
<h3 id="조건에-맞춰-정렬하고-싶을-때---삼성코테예제-중-bfs-쓸때-유용">조건에 맞춰 정렬하고 싶을 때 - 삼성코테예제 중 BFS 쓸때 유용~</h3>
<pre><code class="language-cpp">// 값을 priority queue에 넣어주면 struct에 선언된 기준대로 정렬해줌!!
#include &lt;queue&gt;

struct standard {
    int x,y,x_cnt, y_cnt;
    bool operator &lt;(const standard&amp; i const{
        if(x == i.x)
        {
            if(y == i.y)
            {
                    return x_cnt &gt; i.x_cnt // x값도 y값도 같은 경우, x_cnt가 작은 순서대로 정렬
            } else return y &gt; i.y // 들어온 값들의 x값이 같은 겅우, y가 작은 순서대로 정렬
        }
        else return x &lt; i.x // x가 클수록 앞으로 정렬
    }
};

int main()
{
    priority_queue&lt;standard&gt; qp;
    pq.push({1,2,3,4});
    int x_first = pq.top().x;
}</code></pre>
<pre><code class="language-python"></code></pre>
<hr>
<h3 id="tuple">Tuple</h3>
<h3 id="tuple3개의-요소-한번에-넣고-싶을때---2개는-pair">Tuple(3개의 요소 한번에 넣고 싶을때) - 2개는 pair</h3>
<pre><code class="language-cpp">#include &lt;tuple&gt;
#include &lt;vector&gt;
vector &lt;tuple&lt;int,int,int&gt;&gt; v;

v.push_back({1,2,3});
a = get&lt;0&gt;(v.front());
b = get&lt;1&gt;(v.front());
c = get&lt;2&gt;(v.front());</code></pre>
<pre><code class="language-python"></code></pre>
<hr>
<h3 id="sort">Sort</h3>
<h3 id="sort-1">Sort</h3>
<pre><code class="language-cpp">#include &lt;vector&gt;
#include &lt;algorithm&gt;
vector &lt;int&gt; v;

// Vector
// 오름차순
sort(v.begin(), v.end());

// 내림차순
sort(v.begin(),v.end(), greater&lt;&gt;());
sort(v.rbegin(),v.rend());

// Array
sort(arr,arr+5);</code></pre>
<pre><code class="language-python"># 오름차순
list.sort()

# 내림차순
list.sort(reverse = True)</code></pre>
<h3 id="vector">Vector</h3>
<h3 id="basic-functions">Basic functions</h3>
<pre><code class="language-cpp">#include &lt;vector&gt;
vector &lt;int&gt; v;  // 1d vector
vector &lt;vector&lt;int&gt;&gt; v2; // 2d vector

v.push_back(a); // add a to end of vector
v.emplace_back(a);
v.pop_back(); // delete last element
v.size(); // size of the vector v
v.begin(); // address of first element
v.end(); // address of last element</code></pre>
<p><img src="Code%20Note%2080c5d50459754395941ddb683593bfe8/Untitled.png" alt="Untitled"></p>
<h3 id="vector-1">Vector</h3>
<h3 id="min-max-idx">min, max, idx</h3>
<pre><code class="language-cpp">#include &lt;vector&gt;
vector &lt;int&gt; v;

int min = *min_element(v.begin(),v.end());
int max = *max_element(v.begin(),v.end());

int max_idx = max_element(v.begin(),v.end()) - v.begin();
int min_idx = min_element(v.begin(),v.end()) - v.begin();

//그냥 값을 넣어서 구할수도 있다.
int min = min(v[0],v[1]); // 이렇게 !</code></pre>
<h3 id="vector-2">Vector</h3>
<h3 id="count-개수세기">Count (개수세기)</h3>
<pre><code class="language-cpp">#include &lt;vector&gt;
#include &lt;algorithm&gt;
vector &lt;int&gt; v;

// 2가 몇개 있는지 찾고싶을 때
int target = 2; 
int c = count(v.begin(),v.end(),target);

// 2 이하가 몇개인지 찾고싶을 때
int cc = count_if(v.begin(),v.end(), [](int elem){return elem &lt;= 2;});

or
bool cnt (int x)
{
    return (x &lt;= 2); // 진짜 숫자 상수 밖에 안됨, vector[i] 도 넣어봤는데 안됨. 아마 int main 안에서 선언되는게 아니라 그런듯
}
int cc = count_if(v.begin(),v.end(), cnt);
</code></pre>
<h3 id="vector-3">Vector</h3>
<h3 id="vector-값-모두-출력하기">Vector 값 모두 출력하기</h3>
<pre><code class="language-cpp">for (auto loop:v)
{
        cout &lt;&lt; loop &lt;&lt; &quot; &quot;;
}</code></pre>
<hr>
<h3 id="vector-4">Vector</h3>
<h3 id="특정값-idx-찾기">특정값 idx 찾기</h3>
<pre><code class="language-cpp">#include &lt;algorithm&gt;

auto idx = find(v.begin(), v.end(), target);
cout &lt;&lt; idx - v.begin()+1; // idx 출력
cout &lt;&lt; *idx; // vector의 idx값

// 있으면 
if (idx != v.end())
{
    cout &lt;&lt; &quot;Element found : &quot; &lt;&lt; *idx;
}
else
{
    cout &lt;&lt; &quot;Element Not found&quot;;
}</code></pre>
<hr>
<h3 id="vector-5">Vector</h3>
<h3 id="n번째-idx값-출력하기">n번째 idx값 출력하기</h3>
<pre><code class="language-cpp">vector &lt;int&gt; :: iterator idx = v.begin() + n + 1;
cout &lt;&lt; *idx;</code></pre>
<h3 id="vector-6">Vector</h3>
<h3 id="2차원-벡터-입력받기">2차원 벡터 입력받기</h3>
<pre><code class="language-cpp">int n, m; // row, col
vector &lt;vector &lt;int&gt;&gt; v2;

for (int i = 0; i&lt;n; i++)
{
        for (int j = 0; j &lt; m: j++)
        {
                int temp;
                cin &gt;&gt; temp;
                v2[i].push_back(temp);
        }
}</code></pre>
<hr>
<h3 id="inputtxt">input.txt</h3>
<h3 id="inputtxt로-불러오기">Input.txt로 불러오기</h3>
<pre><code class="language-cpp">리소스파일 폴더에 input.txt 만들어두고

#define LOCAL
#define _CRT_SECURE_NO_DEPRECATE

#include &lt;cstdio&gt;

int main()
{
#ifdef LOCAL
    freopen(&quot;input.txt&quot;, &quot;r&quot;, stdin);
#endif
}</code></pre>
<pre><code class="language-python"></code></pre>
<hr>
<h3 id="vector-7">Vector</h3>
<h3 id="string-일부만-추출하기">String 일부만 추출하기</h3>
<pre><code class="language-cpp">// substr(start, length)
v.substr(0,v[0].size());
</code></pre>
<pre><code class="language-python"></code></pre>
<hr>
<h3 id="set-중복x">Set (중복x)</h3>
<h3 id="set">Set</h3>
<pre><code class="language-cpp">#include &lt;set&gt;
set &lt;int&gt; s1;

s1.insert(10);
// 중복 x, 자동 오름차순 정렬</code></pre>
<pre><code class="language-python"># list to set
# 중복 제거

ls = [1,2,3,4,5,5,5]
ss = set(ls)</code></pre>
<h3 id="unordered_set">Unordered_set</h3>
<p>-장점: insert, find, erase =O(1)</p>
<p>-주의: 내부에 랜덤하게 저장됨</p>
<p>순서를 고려하는 경우, 사용 X</p>
<ul>
<li>시간복잡도가 낮은 이유: 해시함수</li>
<li>입력된 값으로부터 값을 저장할 array의 idx값을 계산 → 값으로부터 idx값을 알 수 있으므로 탐색X</li>
</ul>
<h3 id="unordered_set-find">unordered_set find</h3>
<pre><code class="language-cpp">#include &lt;unordered_set&gt;

unordered_set &lt;int&gt; mm;

if (mm.find(10) != mm.end()){
    // 10 mm안에 있음}

mm.find(x) == mm.end() 가 true면 없는거 (끝까지 탐색했는데 없어서 end idx와 같아지면)</code></pre>
<pre><code class="language-python"></code></pre>
<hr>
<h3 id="문자열공백찾기">문자열(공백찾기)</h3>
<h3 id="isspace">isspace</h3>
<pre><code class="language-cpp">//isspace : 공백이 아니면 0 반환

if (isspace(str) != 0) // 공백일 때
if (isspace(str) == 0) // 공백 아닐 때</code></pre>
<pre><code class="language-python"></code></pre>
<hr>
<h3 id="문자열">문자열</h3>
<h3 id="문자열-합치기list-→-word">문자열 합치기(list → word)</h3>
<pre><code class="language-cpp">...</code></pre>
<pre><code class="language-python">str_list = [&#39;a&#39;,&#39;b&#39;,&#39;c&#39;,&#39;d&#39;]
&#39;&#39;.join(str_list)

# result = &#39;abcd&#39;</code></pre>
<h3 id="문자열대문자-소문자">문자열(대문자, 소문자)</h3>
<h3 id="tolower-toupper">tolower, toupper</h3>
<pre><code class="language-cpp">#include &lt;cctype&gt;
string str = &quot;HOHO&quot;;
for (char&amp; ch:str)
{
        ch = tolower(ch);
}
//-------------------------
string word = &quot;HELLO&quot;;
transform(word.begin(), word.end(), word.begin(), ::tolower);
cout&lt;&lt;word&lt;&lt;&quot;\n&quot;; //hello

for(int i=0; i&lt;word.size(); i++) {
    word[i] = ::toupper(word[i]);
}
cout&lt;&lt;word&lt;&lt;&quot;\n&quot;; //HELLO</code></pre>
<pre><code class="language-python">str = &quot;hoho&quot;
str.upper()
str.lower()</code></pre>
<hr>
<h3 id="문자열string-to-int">문자열(String to int)</h3>
<h3 id="stoi">stoi</h3>
<pre><code class="language-cpp">#include &lt;string&gt;

// string to int
string s = &quot;12345&quot;;
int ss = stoi(s);

// int to string
to_string(ss)</code></pre>
<pre><code class="language-python">s = &quot;12345&quot;
ss = int(s)</code></pre>
<hr>
<h3 id="vector-remove--erase">Vector Remove / erase</h3>
<h3 id="vector에서-특정값-지우기">Vector에서 특정값 지우기</h3>
<pre><code class="language-cpp">#include &lt;algoritm&gt;
#include &lt;vector&gt;

target = 10;
v.erase(remove(v.begin(),v.end(),target));</code></pre>
<pre><code class="language-python">target = 10
list.remove(target)</code></pre>
<hr>
<h3 id="vector-8">Vector</h3>
<h3 id="vector-2개-이어붙이기">Vector 2개 이어붙이기</h3>
<pre><code class="language-cpp">// vector x 뒤에 y 붙이기

x.insert(x.end(),y.begin(),y.end());</code></pre>
<pre><code class="language-python">ans = x + y</code></pre>
<hr>
<h3 id="vector-sum">Vector sum</h3>
<h3 id="vector--list-값-모두-더하기">Vector / list 값 모두 더하기</h3>
<pre><code class="language-cpp">#include &lt;numeric&gt;

ans = accumulate(v.begin(), v.end(),0); // 0+요소값 전부</code></pre>
<pre><code class="language-python">ans = sum(ls)</code></pre>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Simulation][Project] 카페 서빙 로봇 ]]></title>
            <link>https://velog.io/@yujin-shim/SimulationProject</link>
            <guid>https://velog.io/@yujin-shim/SimulationProject</guid>
            <pubDate>Wed, 05 Jul 2023 09:49:48 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/yujin-shim/post/f2ef4857-6ea1-4aa0-a8ce-96a2f0221bd3/image.gif" alt=""></p>
<p>코드 및 설명 참고: <a href="https://github.com/yujin-shim/gazebo-serving-robot-simulation/tree/main">Github</a></p>
<p>그동안 진행했던 simulation 강의를 바탕으로 project를 진행중이다 :) 
1차 demo는 완성된 상태이며 navigation 성능을 개선하기 위해 노력 중이다.</p>
<p>더 자세한 설명과 완성된 코드는 계속해서 update 될 예정이다.</p>
<p>팀 mate : 정빈, 찬영, 건우, 유진 :)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[와이파이 비밀번호 찾기]]></title>
            <link>https://velog.io/@yujin-shim/%EC%99%80%EC%9D%B4%ED%8C%8C%EC%9D%B4-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%B0%BE%EA%B8%B0</link>
            <guid>https://velog.io/@yujin-shim/%EC%99%80%EC%9D%B4%ED%8C%8C%EC%9D%B4-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%B0%BE%EA%B8%B0</guid>
            <pubDate>Mon, 03 Jul 2023 05:22:42 GMT</pubDate>
            <description><![CDATA[<p>window에서 cmd라고 검색 후 <strong>관리자 권한으로 실행</strong>로 열기
<img src="https://velog.velcdn.com/images/yujin-shim/post/dc167bd1-f354-4c0b-80c3-ed8ca673c9c0/image.png" alt=""></p>
<pre><code>netsh wlan show profile &quot;wifiname&quot; key=clear</code></pre><p>이렇게 해서 나오는
보안설정의 키 콘텐츠가 비밀번호이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Simulation] 오픈소스 기반 시뮬레이션 패키지 활용]]></title>
            <link>https://velog.io/@yujin-shim/Simulation-%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%EA%B8%B0%EB%B0%98-%EC%8B%9C%EB%AE%AC%EB%A0%88%EC%9D%B4%EC%85%98-%ED%8C%A8%ED%82%A4%EC%A7%80-%ED%99%9C%EC%9A%A9</link>
            <guid>https://velog.io/@yujin-shim/Simulation-%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%EA%B8%B0%EB%B0%98-%EC%8B%9C%EB%AE%AC%EB%A0%88%EC%9D%B4%EC%85%98-%ED%8C%A8%ED%82%A4%EC%A7%80-%ED%99%9C%EC%9A%A9</guid>
            <pubDate>Mon, 03 Jul 2023 05:19:55 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>혹시 이 simulation을 따라하는 사람이 있다면...
심화과정으로 갈수록 virtual machine은 정신건강에 좋지 않다. (저도 알고싶진 않았어요)
듀얼부팅을 하거나 리눅스용 노트북을 장만하는 것을 추천한다.
나는 로봇에 관한 것들을 계속 할 것이기 때문에 리눅용 노트북을 하나 마련했다 💻
새 노트북에서 gazebo를 돌리니 아주 천국이다 :)</p>
</blockquote>
<h1 id="개요">개요</h1>
<ul>
<li>summit XL</li>
<li>PR2</li>
<li>Jackal</li>
<li>Turtlebot3</li>
</ul>
<ol>
<li>sim_ws/src/ 안에 chapter10 파일 넣어주기</li>
<li>hidden folders 중에서 .gazebo/models 에 extra_models 안에 있는 모든 파일  붙여넣기</li>
</ol>
<p>chapter10에 대한 dependency 설치</p>
<pre><code>cd sim_ws
rosdep install -i --from-path src/chapter10 --rosdistro noetic -y --os=ubuntu:focal
catkin_make

roslaunch turtlebot3_gazebo turtlebot3_world.launch
roslaunch turtlebot3_navigation turtlebot3_navigation.launch
roscd turtlebot3_navigation/rviz/
rviz -d turtlebot3_nav.rviz</code></pre><p>초기위치가 안 맞을 경우 2D Pose Estimate를 이용해 맞춰주면 된다.</p>
<pre><code>roslaunch turtlebot3_teleop turtlebot3_teleop_key.launch
or
rosrun teleop_twist_keyboard teleop_twist_keyboard.py</code></pre><p>launch 파일이 안되는 경우, rosrun으로 실행하면 키보드로 map 안에서 주행이 가능하다.
관성값을 잘 설정해준 관계로 벽에 부딪혀도 매우 잘 일어나는 것을 확인할 수 있다.</p>
<p>끝
.
.</p>
<h1 id="목적지-경유-waypoint">목적지 경유 (waypoint)</h1>
<h2 id="2d-pose-estimate로-map에-직접-찍어주기">2D pose estimate로 map에 직접 찍어주기</h2>
<p>turtlebot의 경우, 한번에 한 위치로만 이동할 수 있음.
여러 포인트를 이동하기 위해서는 어떻게 해야할까?_?</p>
<pre><code>roslaunch turtlebot3_gazebo turtlebot3_world.launch
roslaunch turtlebot3_navigation turtlebot3_navigation.launch
roscd turtlebot3_navigation/rviz
rviz -d turtlebot3_nav.rviz
roslaunch follow_waypoints follow_waypoints.launch</code></pre><p>follow_waypoints.launch 파일을 열고나면 /initialpose를 받기를 기다린다는 내용이 terminal에 출력된다.
<img src="https://velog.velcdn.com/images/yujin-shim/post/d97d5972-a4d5-4d41-a1b8-e08328ba7c34/image.png" alt=""></p>
<p>rviz 상에서 2D pose estimate를 찍어주면 /initialpose가 출력되므로 2D pose estimate 버튼을 이용해 여기저기 점을 찍어주면 terminal 상에서 계속
Received new waypoint라는 메세지가 나올 것이다.</p>
<pre><code>rostopic pub /start_journey std_msgs/Empty -1</code></pre><p>위 명령어를 쳐주면 찍어준 포인트들로 이동하는 것을 확인할 수 있다.</p>
<p>원하는 경유지를 하나씩 2D pose estimate로 찍어줄 수 있어서 편하지만 
costmap이 계속 남아있게되는데 이는 2D pose estimate로 지정해주는 /initialpose를 
amcl 노드도 받아 초기 위치를 localization하기 때문이다.
그래서 위치를 찍어줄 때마다 로봇의 초기 위치도 같이 이동하고 다니게 되면서 잔상이 남는다.
<img src="https://velog.velcdn.com/images/yujin-shim/post/ae2f8956-782a-484e-9df6-150e5e565492/image.png" alt=""></p>
<p><strong>따라서 코드를 통해 waypoint를 깔끔하게 설정해보자</strong></p>
<h2 id="code로-waypoint-찍어보기">Code로 waypoint 찍어보기</h2>
<h3 id="smach-state-machine">smach (state machine)</h3>
<blockquote>
<p>로봇을 여러 state로 만들고 그 state를 전환을 지정해주어 시스템 또는 로봇의 동작을 모델링하는 소프트웨어 모듈
센서데이터, 사용자 명령, 또는 내부 이벤트와 같은 input에 대해 응답하고 현재상태에 따라 작업을 수행하도록 설계되어 있다.</p>
</blockquote>
<p>로봇 팔을 제어할 때, 물체 감지 / 사용자 명령을 기반으로 동작 등 상태를 전환할 때 state machine을 사용할 수 있다.</p>
<ol>
<li>state 만들기</li>
</ol>
<pre><code>class Foo(smach.State):
    def _init_(self, outcomes=[‘outcome1’, ‘outcome2’]):

    def execute(self, userdata):
        if xxx:
            return ‘outcome1’
        else:    
            return ‘outcome2’</code></pre><ol start="2">
<li>State Machine에 state 추가하기
state machine : 여러 state를 보관하는 컨테이너
State machine is also state so you can add the states machine to another state machine</li>
</ol>
<pre><code>sm = smach.StateMachine(outcomes=[‘outcome4’, ‘outcome5’])
with sm:
    smach.StateMachine.add(‘FOO’, Foo(), transitions={‘outcome1’ : ‘BAR’, ‘outcome2’ : ‘outcome4’})
    smach.StateMachine.add(‘BAR’, Bar(), transitions={‘outcome2’ : ‘FOO’})</code></pre><p>위 코드를 그림으로 나타내면 아래와 같다.
FOO일 때는 결과 값이 outcome1이면 BAR로 가고 outcome2 면 FOO로 간다</p>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/9ff6e063-7de6-4c4d-9f87-dfcc4ffe4baa/image.png" alt=""></p>
<p>이것을 바탕으로 waypoint를 경유 하는 코드를 작성 해 보자.</p>
<pre><code>mkdir ~/sim_ws/src/chapter10/turtlebot3/t3_course/scripts
code ~/sim_ws/src/chapter10/turtlebot3/t3_course/scripts/custom_follow_waypoints.py</code></pre><p>&lt;custom_follow_waypoints.py&gt;</p>
<pre><code class="language-python">#!/usr/bin/env python3

import threading
import rospy
import actionlib

from smach import State, StateMachine
from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal

waypoints = []
class FollowPath(State):
    def __init__(self):
        State __init__(self, outcomes=[‘success’], input_keys=[‘waypoints’])
       self.frame_id = rospy.get_param(‘~goal_frame_id’, ‘map’)
</code></pre>
<pre><code>code ~/sim_ws/src/chapter10/turtlebot3/t3_course/launch/start_follow_waypoints.launch </code></pre><p>&lt; start_follow_waypoints.launch &gt;</p>
<pre><code class="language-XML">&lt;launch&gt;
    &lt;env name=&quot;ROSCONSOLE_FORMAT&quot; value=&quot;[${severity}][${thread}][${node}/${function}:${line}]: ${message}&quot;/&gt;
    &lt;arg name=&quot;waypoints_topic&quot; default=&quot;my_t3_waypoints&quot;/&gt;

    &lt;node pkg=&#39;t3_course&#39; type=&#39;custom_follow_waypoints.py&#39; name=&quot;custom_follow_waypoints&quot;
    output=&quot;screen&quot; clear_params=&quot;true&quot;&gt;
        &lt;param name=&quot;goal_frame_id&quot; value=&quot;map&quot;/&gt;
        &lt;param name=&quot;custom_waypoints_topic&quot; value=&quot;$(arg waypoints_topic)&quot;/&gt;
    &lt;/node&gt;
&lt;/launch&gt;</code></pre>
<p>또한 rviz 상에서 2D pose estimate로 waypoint를 설정할 경우, 
amcl노드도 동일하게 영향을 받기 때문에 코드로 직접 waypoint publish 해주도록 하겠다.</p>
<p>rostopic info /initialpose를 해보면 msg type이
geometry_msgs/PosewithCovarianceStamped 이므로 이 type으로 publish 해주면 된다.</p>
<blockquote>
<p>rviz상에서 좌표를 보기 어려울 때는 rostopic echo /clicked_point를 해주고
rviz ui에 있는 Publish Point로 찍어주면 msg가 출력된다.</p>
</blockquote>
<p>&lt;waypoint_publisher.py&gt;</p>
<pre><code class="language-python">#!/usr/bin/env python3

import rospy
import time
import tf.transformations

from std_msgs.msg import Empty
from geometry_msgs.msg import PoseWithCovarianceStamped

# wp_id : (x, y)
wp_list = {1 : (0.427, -1.795),
           2 : (0.444, 1.784),
           3 : (-1.833, -0.021)}

&quot;&quot;&quot;
user:~$ rosmsg show geometry_msgs/PoseWithCovarianceStamped
std_msgs/Header header
  uint32 seq
  time stamp
  string frame_id
geometry_msgs/PoseWithCovariance pose
  geometry_msgs/Pose pose
    geometry_msgs/Point position
      float64 x
      float64 y
      float64 z
    geometry_msgs/Quaternion orientation
      float64 x
      float64 y
      float64 z
      float64 w
  float64[36] covariance
&quot;&quot;&quot;

def talker():
    pub_wp = rospy.Publisher(&#39;my_t3_waypoints&#39;, PoseWithCovarianceStamped, queue_size=1)
    pub_path_ready = rospy.Publisher(&#39;path_ready&#39;, Empty, queue_size=1)

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

    my_wp = PoseWithCovarianceStamped()
    my_wp.header.stamp = rospy.Time.now()
    my_wp.header.frame_id = &#39;map&#39;

    init_x = -1.81
    init_y = -0.46
    init_roll = 0.0
    init_pitch = 0.0
    init_yaw = 0.7071

    quaternion = tf.transformations.quaternion_from_euler(init_roll, init_pitch, init_yaw)
    # transfromation from roll, pitch, yaw to x y z w

    my_wp.pose.pose.orientation.x = quaternion[0]
    my_wp.pose.pose.orientation.y = quaternion[1]
    my_wp.pose.pose.orientation.z = quaternion[2]
    my_wp.pose.pose.orientation.w = quaternion[3]

    for i in range(len(wp_list)):
        rospy.loginfo(&quot;Waypoint&quot; + str(i))
        # my_wp.pose.pose.position.x = float(i)*0.3 + init_x
        # my_wp.pose.pose.position.y = float(i)*0.3 + init_y
        my_wp.pose.pose.position.x = wp_list[i+1][0]
        my_wp.pose.pose.position.y = wp_list[i+1][1]

        while not rospy.is_shutdown():
            connections = pub_wp.get_num_connections()
            if connections &gt; 0:
                pub_wp.publish(my_wp)
                break
            rospy.loginfo(&quot;Wait for &#39;my_t3_waypoints&#39; topic&quot;)
            rate.sleep()

        rospy.loginfo(&quot;Published waypoint number &quot; + str(i))
        time.sleep(2)

    start_command = Empty()

    while not rospy.is_shutdown():
        connections = pub_path_ready.get_num_connections()
        if connections &gt; 0:
            pub_path_ready.publish(start_command)
            rospy.loginfo(&quot;Sent waypoint list execution command&quot;)
            break
        rospy.loginfo(&quot;Waiting for &#39;path_ready&#39; topic&quot;)
        rate.sleep()

if __name__==&#39;__main__&#39;:
    try:
        talker()
    except rospy.ROSInterruptException:
        pass</code></pre>
<p>실행해보자 </p>
<pre><code>roslaunch turtlebot3_gazebo turtlebot3_world.launch
roslaunch turtlebot3_navigation turtlebot3_navigation.launch
roscd turtlebot3_navigation/rviz/
rviz -d turtlebot3_nav.rviz
roslaunch t3_course start_follow_waypoint.launch
rosrun t3_course waypoint_publisher.py
</code></pre><p>이처럼 잘 이동하는 것을 볼 수 있다. (내 rviz가 지저분한 것은 초기위치 설정을 찾느라 좀 헤맸기 때문이다 ㅎ)</p>
<p>state machine은 꽤나 복잡해보이지만
다양한 종류의 동작을 하는 로봇을 설계할 때는 state machine을 쓰는 것이 유리하다.
각 동작의 결과에 따른 다음 동작을 설정하거나 각각 state일 때 수정할 부분을 찾기도 편하기 때문</p>
<p>재미있다!! 😎️ 😎️ 😎️ 👍
.
.</p>
<h1 id="line-follower">Line follower</h1>
<p>Opencv는 ros library는 아니지만 cv-bridge를 이용해서 통합할 수 있음
이 라이브러리를 이용해서 rosmsg-imagetype 간의 변환이 편하므로 다양하게 이용할 수 있다.</p>
<h2 id="line-따라-이동하도록-하기">Line 따라 이동하도록 하기</h2>
<ol>
<li>ROS 이미지를 가져와서 OpenCV 형식으로 변환</li>
<li>OpenCV 라이브러리를 사용하여 이미지를 처리하여 작업에 필요한 데이터를 얻음</li>
<li>얻은 데이터를 기반으로 노란색 선을 따라 로봇을 이동</li>
</ol>
<h3 id="간단하게-cv-bridge를-알아보자">간단하게 cv bridge를 알아보자</h3>
<blockquote>
<p>message type을 알고 싶을 때는 둘 중 하나를 해주면 된다.</p>
</blockquote>
<ol>
<li>rosmsg show sensor_msgs/Image</li>
<li>google에 sensor_msgs/Image를 검색하면 된다.</li>
</ol>
<p>/t3_course/scripts/example_cv_bridge.py 를 만들어 주자</p>
<p>&lt;example_cv_bridge.py&gt;</p>
<pre><code class="language-python">#!/usr/bin/env python3

import rospy
import cv2
from cv_bridge import CvBridge, CvBridgeError
from sensor_msgs.msg import Image


class LineFollower(object):

    def __init__(self):
        self.bridge_object = CvBridge()
        self.image_sub = rospy.Subscriber(&quot;/camera/rgb/image_raw&quot;,Image,self.camera_callback)

    def camera_callback(self,data):
        try:
            # We select bgr8 because its the OpneCV encoding by default
            cv_image = self.bridge_object.imgmsg_to_cv2(data, desired_encoding=&quot;bgr8&quot;)
        except CvBridgeError as e:
            print(e)

        cv2.imshow(&quot;Image window&quot;, cv_image)
        cv2.waitKey(1)


def main():
    line_follower_object = LineFollower()
    rospy.init_node(&#39;line_following_node&#39;, anonymous=True)
    try:
        rospy.spin()
    except KeyboardInterrupt:
        print(&quot;Shutting down&quot;)
    cv2.destroyAllWindows()

if __name__ == &#39;__main__&#39;:
    main()</code></pre>
<p>실행시켜보자!</p>
<pre><code class="language-bash">roslaunch t3_course follow_line.launch
rosrun t3_course example_cv_bridge.py</code></pre>
<p>실행해보면 robot이 보는 image가 나오게 된다.</p>
<h3 id="line을-따라-이동하도록-코드를-만들어보자">Line을 따라 이동하도록 코드를 만들어보자</h3>
<p>파일 2개를 만들어줄 것이다.
로봇을 움직이기 위한 파일과
/turtlebot3/t3_course/scripts/move_robot.py</p>
<p>노란선을 검출하고 따라가도록 하는 파일
/turtlebot/t3_course/scripts/follow_line_step_hsv.py
&lt;move_robot.py&gt;</p>
<pre><code class="language-python">#!/usr/bin/env python3
import rospy
from geometry_msgs.msg import Twist

class MoveRobot(object):
    def __init__(self):
        self.cmd_vel_pub = rospy.Publisher(&#39;cmd_vel&#39;, Twist, queue_size=10)
        self.last_cmd_vel = Twist()

        self.pub_rate = rospy.Rate(10)

        self.shutdown_detected = False

    def move_robot(self, cmd_vel_msg):
        self.cmd_vel_pub.publish(cmd_vel_msg)

    def stop_robot(self):
        cmd_vel = Twist()
        cmd_vel.angular.z = 0.0
        self.move_robot(cmd_vel_msg=cmd_vel)
        self.shutdown_detected = True

def main():
    rospy.init_node(&#39;move_robot_node&#39;, anonymous=True)

    move_robot_obj = MoveRobot()
    cmd_vel = Twist()

    # Make it start turning
    cmd_vel.angular.z = 0.15

    rate = rospy.Rate(5)

    ctrl_c = False

    def shutdownhook():
        # works better than the rospy.is_shut_down()
        move_robot_obj.stop_robot()
        rospy.loginfo(&quot;shutdown time!&quot;)
        ctrl_c = True

    rospy.on_shutdown(shutdownhook)

    while not ctrl_c:
        move_robot_obj.move_robot(cmd_vel_msg=cmd_vel)
        rate.sleep()

if __name__==&#39;__main__&#39;:
    main()
</code></pre>
<p>&lt;follow_line_step_hsv.py&gt;</p>
<pre><code class="language-python"></code></pre>
<p>python code를 만들고나면 항상 terminal에서 chmod +x * 해주는 것을 잊지말자</p>
<h3 id="결과">결과</h3>
<p>노란선을 잘 따라가는 귀여운 turtlebot을 볼 수 있다. ㅎㅎ
<img src="https://velog.velcdn.com/images/yujin-shim/post/33cf0381-37ef-4e6b-ba2c-f3c951e26a4a/image.gif" alt=""></p>
<p>.</p>
<h1 id="blob-tracking">Blob tracking</h1>
<ul>
<li>비디오 sequance의 객체를 감지하고 추적하는데 사용되는 기술</li>
<li>blob = 색 / intensity 이런 속성을 공유하는 연결된 그룹을 의미한다.</li>
<li>위치 크기 모양 등이 추출되어 사용가능하다</li>
</ul>
<p>.
.</p>
<h1 id="object-detection">Object Detection</h1>
<p>.
.</p>
<h1 id="object-recognition">Object Recognition</h1>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Simulation] Path planning]]></title>
            <link>https://velog.io/@yujin-shim/Simulation-Path-planning</link>
            <guid>https://velog.io/@yujin-shim/Simulation-Path-planning</guid>
            <pubDate>Thu, 29 Jun 2023 14:54:15 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>ROS에서 경로계획이 어떻게 작동하는지 알아보자</p>
</blockquote>
<h1 id="rviz에서-path-planning-시각화-하기">Rviz에서 path planning 시각화 하기</h1>
<h2 id="rviz-display">Rviz Display</h2>
<ul>
<li>Map display (for costmap)</li>
<li>Path display (plans)</li>
<li>2D Nav Goal</li>
</ul>
<pre><code>roslaunch husky_navigation_launch main.launch
roslaunch husky_navigation move_base_demo.launch
rviz</code></pre><p>rviz를 켰으면 아래 적힌 내용들을 설정해주자 
.</p>
<p>add &gt; map
add &gt; robotmodel
add &gt; laserscan &gt; size &gt; 0.1
add &gt; map &gt; topic &gt; /move_base/gloabal_costmap/costmap &gt; remane &gt; global costmap &gt; color scheme &gt; costmap
add &gt; map &gt; topic &gt; /move_base/local_costmap/costmap &gt; rename &gt; local costmap &gt; color scheme &gt; costmap
add &gt; by topic &gt; /move_base &gt; /global_plan &gt; path &gt; rename &gt; global planner
add &gt; by topic &gt; /move_base &gt; /local_plan &gt; path (topic: /move_base/DWAPlannerROS/local_plan) &gt; rename &gt; local planner
add &gt; by topic &gt; /global_costmap &gt; /footprint &gt; polygon &gt; rename &gt; global polygon -&gt; 충돌 영역 표시
add &gt; by topic &gt; /local_costmap &gt; /footprint &gt; polygon &gt; rename &gt; local polygon -&gt; 충돌 영역 표시
add &gt; by_topic &gt; /particlecloud &gt; poseArray &gt; rename &gt; AMCL particles
add &gt; by_topic &gt; /move_base_simple &gt; /goal &gt; pose &gt; rename &gt; goal pose</p>
<ul>
<li>참고로 이미있는 것과 속성이 같다면 duplicate해서 topic만 바꿔주면 된다.</li>
</ul>
<p>2D Nav Goal로 갈 곳을 지정해주면
    빨간색 선 : global plan
    초록색 선 : local plan 
을 확인해 볼 수 있다.</p>
<p>보기 편하도록 색을 바꿔주면 좋다</p>
<p>이렇게 설정한 rviz를 아래 directory에 navigation.rviz로 저장해주도록 하자</p>
<pre><code>~/sim_ws/src/husky/husky_navigation/rviz</code></pre><p>.
.</p>
<h1 id="오픈소스-path-planning-패키지">오픈소스 path planning 패키지</h1>
<h2 id="move_base-노드">Move_base 노드</h2>
<ul>
<li>navigation stack에 주요 노드</li>
<li>목적: 로봇을 현재 위치에서 목표 위치까지 이동 (SimpleActionServer 이용)</li>
<li>목적지에 대한 Msg Type : geometry_msgs/PoseStamped</li>
<li>Published Topic : move_base/goal</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/f3bf2005-9039-4f3f-baff-0bb2ad89f0a1/image.png" alt=""></p>
<pre><code>rostopic echo /move_base/goal</code></pre><p>rviz 상에서 2D NAV GOAL을 찍어준 후 메세지를 출력해보면 goal 정보가 출력되는 것을 확인할 수 있다.</p>
<p>이처럼 rviz의 UI 상에서 2D nav goal로 목표 지점을 찍어줄 때마다 move_base가 /move_base/goal을 publish하는 것이다.</p>
<p>goal:
    target_pose:
        header:
            seq:
            stamp:
                secs:
                nsecs:
            frame_id : 
        pose :
              position:
                x:
                y:
                z:
            orientation:
                x:
                y:
                z:
                w:</p>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/698147c1-7fd6-4421-b35b-a5ee72ed04d6/image.png" alt=""></p>
<h2 id="move_base-노드의-topics">move_base 노드의 topics</h2>
<ul>
<li><p>/move_base/goal (move_base_msgs/MoveBaseActionGoal)</p>
</li>
<li><p>/move_base/cancel (actionlib_msgs/GoalID)</p>
</li>
<li><p>/move_base/feedback (move_base_msgs/MoveBaseActionFeedback)</p>
<ul>
<li>현재 map상에서 robot의 위치 업데이트, goal로 잘 이동하는지 확인하기 위함</li>
</ul>
</li>
<li><p>/move_base/status (actionlib_msgs/GoalStatusArray)</p>
<ul>
<li>현재 상태가 나온다 (goal로 이동중: status:1, 도착하면 goal reached 나옴)</li>
</ul>
</li>
<li><p>/move_base/result (move_base_msgs/MoveBaseActionResult)</p>
<ul>
<li>목적지에 도달하고 나면 topic 생성 (goal reached)</li>
</ul>
</li>
</ul>
<p>꼭 rviz 상에서 goal을 2d nav goal로 정해주지않아도 터미널 상에서 message를 publish해서 지정해줄 수 있다.</p>
<p>rostopic까지 적어준 후 tab을 치면 자동적으로 구조가 나온다.
(terminal이므로 커서가 좌우로만 움직인다..)</p>
<pre><code class="language-bash">rostopic pub /move_base/goal move_base_msgs/MoveBaseActionGoal</code></pre>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/5b7f9c0f-a966-4a3e-a030-aaf476f71272/image.gif" alt=""></p>
<p>.
.</p>
<h2 id="실습">실습</h2>
<h3 id="목표">목표</h3>
<ul>
<li>move_base 노드의 액션서버로 메세지 전송을 위한 액션 클라이언트 만들기 (send_goal_client.py)</li>
</ul>
<h3 id="mission">mission</h3>
<ol>
<li>액선 클라이언트를 사용하여 로봇을 map 의 세 가지 다른 목적지로 이동</li>
<li>로봇이 3개의 목적지에 도달하면 루프를 다시 생성
(로봇이 3개의 목적지로 계속 반복해서 이동하도록 함)</li>
</ol>
<p>천천히 코드를 발전시키면서 movebase를 이해해보자</p>
<h4 id="mission---goal-위치-지정-상태-출력">mission - goal 위치 지정, 상태 출력</h4>
<p>&lt;send_goal_client.py&gt; - version1</p>
<pre><code class="language-python">#! /usr/bin/env python3

import rospy
import time
import actionlib

from move_base_msgs.msg import MoveBaseGoal, MoveBaseAction, MoveBaseResult, MoveBaseFeedback

def feedback_callback(msg):
    rospy.loginfo(&quot;[Feedback] Going to Goal Pose...&quot;)

rospy.init_node(&#39;move_base_action_client&#39;)
client = actionlib.SimpleActionClient(&#39;move_base&#39;,MoveBaseAction)
client.wait_for_server()

goal = MoveBaseGoal()
goal.target_pose.header.frame_id = &#39;map&#39;
goal.target_pose.pose.position.x = 2.0
goal.target_pose.pose.position.y = 2.0
goal.target_pose.pose.position.z = 0.0

goal.target_pose.pose.orientation.x = 0.0
goal.target_pose.pose.orientation.y = 0.0
goal.target_pose.pose.orientation.z = 0.0
goal.target_pose.pose.orientation.w = 1.0


client.send_goal(goal = goal, feedback_cb = feedback_callback)
client.wait_for_result()

rospy.loginfo(&quot;[Result] State: %d&quot; %(client.get_state()))</code></pre>
<h5 id="결과">결과</h5>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/72bc2b6e-ab73-471a-a69d-f4e1bfdcee4d/image.gif" alt=""></p>
<p>.
.</p>
<h1 id="global-planner--global-costmap">Global planner &amp; Global costmap</h1>
<p>.
.</p>
<h1 id="local-planner--local-costmap">Local planner &amp; Local costmap</h1>
<p>.
.</p>
<h1 id="common-costmap-parameters">Common Costmap Parameters</h1>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Simulation] Robot Localization]]></title>
            <link>https://velog.io/@yujin-shim/Simulation-Robot-Localization</link>
            <guid>https://velog.io/@yujin-shim/Simulation-Robot-Localization</guid>
            <pubDate>Wed, 28 Jun 2023 16:16:08 GMT</pubDate>
            <description><![CDATA[<h1 id="rviz에서-localization-시각화하기">Rviz에서 Localization 시각화하기</h1>
<h2 id="rviz-display">Rviz Display</h2>
<ul>
<li>map display</li>
<li>laserscan display</li>
<li>PoseArray display</li>
</ul>
<h2 id="실습">실습</h2>
<h3 id="husky-다운로드--설치">Husky 다운로드 &amp; 설치</h3>
<p>~/src 에 다운받은 husky 파일을 넣고</p>
<p>아래 명령어로 의존성 설치를 해준다.
ros와 ubuntu 버전에 맞게 설치해주면 된다.</p>
<pre><code>rosdep install -i --from-path src/husky --rosdistro melodic -y --os=ubuntu:bionic

sudo apt install ros-melodic-ur-description
sudo apt install ros-melodic-lms1xx
catkin_make</code></pre><h3 id="husky-launch-해보기">husky launch 해보기</h3>
<pre><code>roslaunch husky_navigation_launch main.launch
roslaunch husky_navigation_launch keyboard_teleop.launch</code></pre><p><img src="https://velog.velcdn.com/images/yujin-shim/post/9a4fc681-be7c-41b3-ac6b-87e11dfb72a5/image.png" alt=""></p>
<ul>
<li>gazebo상에서 실외 네비게이션도 시뮬레이션 가능함 (가짜 topic - 경도 위도 값으로 가능)
오호 다음에 한번 해보고 싶당😲</li>
</ul>
<h3 id="localization-해보기">localization 해보기</h3>
<pre><code>roslaunch husky_navigation_launch main.launch
roslaunch husky_navigation amcl_demo.launch
rviz</code></pre><p>rviz &gt; add &gt; Map &gt; topic &gt; /map
rviz &gt; add &gt; Laserscan
rviz &gt; add &gt; RobotModel
rviz &gt; add &gt; PoseArray</p>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/85dbc575-387e-438f-b9f8-9730ab4c2680/image.png" alt=""></p>
<pre><code>roslaunch husky_navigation_launch keyboard_teleop.launch</code></pre><p>로봇을 점점 움직일수록 화살표들 (불확실성) 이 로봇 쪽으로 모이는 것을 확인할 수 있다.</p>
<p>.
.</p>
<h1 id="localization-다루기">Localization 다루기</h1>
<h2 id="monte-carlo-localization-mcl">Monte Carlo Localization (MCL)</h2>
<p>로봇은 다음에 어디로 이동할지 무작위로 추정 = particle
Lidar와 같은 sensor 값을 바탕으로 가능성이 낮은 영역을 배제하면서 particle은 가장 로봇이 있을만한 위치로 수렴하게 된다.</p>
<p>로봇이 움직일수록 화살표가 로봇쪽으로 모였던 이유는
움직이면서 sensor를 통해 더 많은 값을 얻을 수 있으므로 위치파악이 더 정확해진 것이다.</p>
<h2 id="amcl-adaptive-monte-carlo-localization-package">AMCL (Adaptive Monte Carlo Localization) package</h2>
<ol>
<li>Launch 파일로 amcl 노드 켜주기</li>
</ol>
<pre><code>roslaunch husky_navigation amcl_demo.launch</code></pre><ol start="2">
<li><p>2D pose estimate를 통해 초기 로봇 위치 설정</p>
</li>
<li><p>로봇을 움직이기 및 시각화</p>
</li>
</ol>
<ul>
<li>Subscribing Topic: /scan, /map, /tf</li>
<li>Publishing Topic: /amcl_pose, /particlecloud</li>
</ul>
<pre><code>mkdir ~/sim_ws/src/husky/husky_launch/scripts 
code ~/sim_ws/src/husky/husky_launch/scripts/get_pose_service.py</code></pre><p>&lt;get_pose_service.py&gt;</p>
<pre><code>#! /usr/bin/env python3

import rospy
from std_srvs.srv import Empty, EmptyResponse # Import the service message python classes generated from Empty.srv.
from geometry_msgs.msg import PoseWithCovarianceStamped, Pose

robot_pose = Pose()

def service_callback(request):
    rospy.loginfo(&quot;Robot Pose: &quot; + str(robot_pose))

    return EmptyResponse() # the service Response class, in this case EmptyResponse


def sub_callback(msg):
    global robot_pose
    robot_pose = msg.pose.pose

rospy.init_node(&#39;get_pose_service&#39;)
my_service = rospy.Service(&#39;get_pose_service&#39;, Empty, service_callback) # create the Service called get_pose_service with the defined callback
sub_pose = rospy.Subscriber(&#39;amcl_pose&#39;, PoseWithCovarianceStamped, sub_callback)

rospy.spin() # mantain the service open.</code></pre><pre><code>rosrun husky_launch get_pose_service.py
rqt or rosservice call /get_pose_service &quot;{}&quot;</code></pre><p>rqt &gt; plugins &gt; service &gt; service caller &gt; /get_pose_service 선택 후 call</p>
<p>rqt 상에서도 가능하고 terminal에서 명령어를 쳐도 가능하다. </p>
<p>map상에서 로봇의 좌표가 terminal 상에서 출력되는 것을 이야기한다.</p>
<h1 id="transforms">Transforms</h1>
<h2 id="localization을-위한-하드웨어-요구사항">Localization을 위한 하드웨어 요구사항</h2>
<ul>
<li>Good Laser data (/scan)</li>
<li>Good odometry data (/odom/filtered)</li>
<li>Good Map data (/map)</li>
</ul>
<h2 id="localization을-위해-꼭-필요한-변환관계">Localization을 위해 꼭 필요한 변환관계</h2>
<ol>
<li>base_laser =&gt; odom<ul>
<li>이 변환관계를 AMCL이 계산, 이를 통과하는 경로가 있어야 함</li>
</ul>
</li>
<li>base_laser =&gt; base_link<ul>
<li>LaserScan frame에 대한 로봇 base의 고정된 TF 관계가 필요</li>
</ul>
</li>
</ol>
<p>.
.</p>
<h2 id="오픈소스-localization-패키지">오픈소스 Localization 패키지</h2>
<ul>
<li>더 자세한 정보는 링크 참고 : <a href="http://wiki.ros.org/amcl">amcl ros</a></li>
</ul>
<h3 id="amcl-노드의-일반-parameter">amcl 노드의 일반 parameter</h3>
<ul>
<li>odom_model_type (default: &quot;diff&quot;)<ul>
<li>사용할 주행거리 측정 모델 설정</li>
<li>&quot;diff&quot;, &quot;omni&quot;, &quot;diff-corrected&quot;, &quot;omni-corrected&quot;</li>
</ul>
</li>
<li>odom_frame_id (default: &quot;odom&quot;)<ul>
<li>odometry 시스템에 연결된 프레임의 이름</li>
</ul>
</li>
<li>base_frame_id (default: &quot;base_link&quot;)<ul>
<li>로봇의 base와 연결된 프레임의 이름</li>
</ul>
</li>
<li>global_frame_id (default: &quot;map&quot;)<ul>
<li>localization 시스템에 의해 publising되는 기준좌표 프레임</li>
</ul>
</li>
<li>use_map_topic (default: false)<ul>
<li>노드가 topic에서 지도 데이터를 가져올지 아니면 서비스 호출에서 가져올지를 결정</li>
</ul>
</li>
</ul>
<p>.
.</p>
<h3 id="amcl-노드의-filter-관련-파라미터">amcl 노드의 filter 관련 파라미터</h3>
<ul>
<li>min_particles (default: 100)<ul>
<li>필터에 허용되는 최소 입자수 | CPU 사용량에 영향을 미침</li>
</ul>
</li>
<li>max_particles (default: 5000)<ul>
<li>필터에 허용되는 최대 파티클 수 | CPU 사용량에 영향을 미침</li>
</ul>
</li>
<li>kid_err (default: 0.01)<ul>
<li>실제 분포와 추정 분포 사이에 허용되는 최대오차</li>
</ul>
</li>
<li>update_min_d (default: 0.2)</li>
<li>update_min_a (default: pi/6.0)</li>
<li>resample_interval (default: 2)</li>
</ul>
<h2 id="실습-1">실습</h2>
<h3 id="mission">mission</h3>
<ol>
<li>min_particle 및 max_particle를 각각 1과 5로 설정하고 결과 확인</li>
<li>laser_max_range를 1로 변경하고 결과 확인</li>
<li>모든 파라미터 변경은 yaml 파일에서 하도록 함 (amcl_params.yaml)
(launch 파일도 새로 만들기 - my_amcl.launch)</li>
</ol>
<pre><code>code /home/yujin/sim_ws/src/husky/husky_launch/my_amcl.launch</code></pre><p>&lt;my_amcl.launch&gt;</p>
<pre><code>&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><pre><code>code ~/sim_ws/src/husky/husky/husky_navigation/config/amcl_params.yaml</code></pre><p>&lt;amcl_params.yaml&gt;</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><pre><code>roslaunch husky_navigation_launch main.launch
roslaunch husky_launch my_amcl.launch</code></pre><p>나는 이렇게 해도 particle이 보이지 않았다... 아직 해결하지 못함</p>
<h2 id="amcl-노드-관련-service">amcl 노드 관련 service</h2>
<ul>
<li>/global_localization (std_srvs/Empty)<ul>
<li>모든 파티클이 map 의 빈 공간에 무작위로 분산되는 global localization을 시작</li>
</ul>
</li>
<li>/static_map (nav_msgs/GetMap)<ul>
<li>Laser 기반 위치 추정에 사용되는 map을 가져옴</li>
</ul>
</li>
</ul>
<p>로봇의 위치를 알려줄 수 없을 때 (2D pose estimate로 지정 불가)
global_localization을 통해서 할 수 있다.</p>
<h2 id="실습-2">실습</h2>
<ul>
<li>목표 : /global_localization 서비슬르 호출하는 노드 만들기</li>
</ul>
<h3 id="mission-1">Mission</h3>
<ol>
<li>AMCL의 파티클 초기 위치를 랜덤하게 설정하기 위해 /global_localization 서비스를 사용</li>
<li>파일 안에 서비스 클라이언트를 위한 코드 작성</li>
<li>이 클라이언트를 통해 파티클을 분산</li>
</ol>
<pre><code>code ~/sim_ws/src/husky/husky_launch/scripts/init_particles_caller.py
</code></pre><p>&lt;init_particles_caller.py&gt;</p>
<pre><code>#! /usr/bin/env python3

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><pre><code>rosrun husky_launch init_particles_caller.py</code></pre><p><img src="https://velog.velcdn.com/images/yujin-shim/post/c6c5c2a5-8e25-407f-9899-b71124adfb42/image.png" alt=""></p>
<p>흩어진 불확실성 particle들이 로봇을 움직여주면 robot의 위치로 모두 모이는 것을 확인할 수 있다.</p>
<p>map과 scan 데이터가 어느정도 matching되면 로봇의 위치를 잘 추정했다고 생각하는 방식이다.
문제는 만약 환경이 우리가 사용하는 환경처럼 직사각형 모양이라면 위아래가 뒤집혀도
map과 scan 데이터가 matching 되므로 로봇의 위치를 잘 추정했다고 생각할 수 있다는 것이다.</p>
<p>따라서 환경의 특징이 매우 뚜렷하다면 global localization을 사용하기 좋지만 
그렇지 않다면 위치 추정에 실패할수도 있음을 기억해두어야 한다.</p>
<p>.
.</p>
<h1 id="과제---localization">과제 - Localization</h1>
<h2 id="목표">목표</h2>
<ul>
<li>로봇이 모르는, 알 수 없는 환경에서 위치를 추정한다고 가정</li>
<li>위치에 대한 공분상(불확실성, covariance) 값이 일정 수준으로 떨어지도록 로봇을 정사각형 형태로 이동시키기</li>
</ul>
<h2 id="mission-2">Mission</h2>
<ol>
<li>Square_move.py 파일 생성</li>
<li>Particle을 맵 전체에 분산시키는 서비스 호출</li>
<li>로봇은 정사각형 형태로 이동을 수행</li>
<li>주행 중 공분산(covariance)이 0.65 미만이면 로봇이 올바르게 위치를 파악했다는 뜻이므로 프로그램 종료</li>
<li>공분산이 0.65보다 클 경우, 파티클 분산, 이동수행, 공분산 확인 과정을 계속 반복</li>
<li>프로그램 종료 전에, 최종 파티클 필터의 공분산을 터미널에 출력</li>
</ol>
<blockquote>
<p>hint</p>
</blockquote>
<ul>
<li>amcl과 관련된 서비스를 호출하려면 amcl 노드가 실행중이어야 함</li>
<li>파티클의 공분산은 /amcl_pose 토픽을 통해 확인할 수 있음</li>
<li>파티클 공분산은 행렬형태로 publish
  (첫번째 값은 x의 공분산, 8번째 값은 y의 공분산, 마지막 값은 z의 공분산</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Simulation] SLAM]]></title>
            <link>https://velog.io/@yujin-shim/Simulation-SLAM</link>
            <guid>https://velog.io/@yujin-shim/Simulation-SLAM</guid>
            <pubDate>Wed, 28 Jun 2023 04:15:05 GMT</pubDate>
            <description><![CDATA[<h1 id="rviz에서-mapping-과정-시각화">Rviz에서 Mapping 과정 시각화</h1>
<h2 id="rviz-display">Rviz Display</h2>
<ul>
<li>Map display</li>
<li>LaserScan display</li>
<li>RobotModel display</li>
</ul>
<h3 id="rviz-구성하기">Rviz 구성하기</h3>
<p>지난번 처럼 rviz_launcher에 있는 launch 파일로 열었지만 
이번에는 직접 rviz를 꾸며보자</p>
<pre><code>roslaunch turtlebot_navigation_gazebo main.launch
roslaunch turtlebot_navigation_gazebo gmapping_demo.launch
rviz</code></pre><ol>
<li><p>빈 rviz 창에서 add를 눌러 LaserScan을 추가해준 후
topic에서 kobuki/laser/scan으로 설정해준다.
size &gt; 0.05 (=5cm)</p>
</li>
<li><p>Global option &gt; Fixed frame &gt; map</p>
</li>
<li><p>add &gt; RobotModel</p>
</li>
<li><p>add &gt; map &gt; topic &gt; /map</p>
</li>
</ol>
<pre><code>roslaunch turtlebot_teleop keyboard_teleop.launch</code></pre><p>로봇을 움직이며 mapping을 하면 된다.</p>
<h3 id="rviz-configuration-저장하기">Rviz configuration 저장하기</h3>
<ul>
<li>File &gt; Save Config As &gt; Save</li>
</ul>
<p>view_mapping.launch에서 저장한 .rviz파일을 실행할 수 있음</p>
<p>teleop_keyboard로 mapping을 하고나면
map을 저장해야 한다.
.
.</p>
<h1 id="map-다루기">Map 다루기</h1>
<ul>
<li>오픈소스 기반 SLAM 패키지 : gmapping</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/2edfab8b-476b-4eb9-9b77-44b8ddf2251b/image.png" alt="">
(자료출처; programmers)</p>
<p>rviz는 topic을 시각화하는 tool이므로 nav_msgs/OccupancyGrid를 시각화한다.</p>
<ul>
<li><p>오픈소스 기반 Map 관리 패키지 : map_server</p>
</li>
<li><p>2가지 노드를 제공 (map_saver &amp; map_server)</p>
</li>
<li><p>map_saver 노드</p>
<ul>
<li>현재 얻은 지도를 저장할 수 있다
(확장자 .pgm &amp; .yaml 파일이 저장된다)</li>
</ul>
</li>
</ul>
<p>.
키보드로 mapping을 모두 한 이후에 아래 명령어로 mapping한 map을 저장해주자
-f 뒤에 적는 것이 파일명이 된다.</p>
<pre><code>roscd turtlebot_navigation_gazebo/maps
rosrun map_server map_saver -f my_map</code></pre><blockquote>
<p>&lt;my_map.yaml&gt;</p>
<blockquote>
<p>image : grid map pgm 파일이름 지정
resolution : map의 해상도를 pixel 당 m 단위로 지정 (e.g. 0.05m x 0.05m = 1 pixel)
origin : 전역 좌표계에서 지도 원점을 지정 - 2D (x, y, rotate)
negate : map 반전 여부 결정 (boolean), 빈칸 - 점유된 칸 반전 -&gt; 장애물 map을 만들 때 유용 
occupied_thresh : occupied 되었는지 결정하는 threshold, 이 값보다 크거나 같은 값은 점유된 것으로 간주
free_thresh : map에서 cell이 사용가능한지 결정하는 threshold, 이 값보다 작거나 같은 값은 모두 비어있는 것으로 간주</p>
</blockquote>
</blockquote>
<blockquote>
<p>&lt;my_map.pgm&gt;</p>
<blockquote>
<p>pixel의 점유 / 빈칸 여부를 색으로 표시
ros_msgs로 map의 정보가 전달될 때는 0~100까지의 정수를 이용하는데
0 = 빈칸
100 = 장애물 (occupied)
-1 = unknown</p>
</blockquote>
</blockquote>
<ul>
<li>map_server 노드 - 다른 노드가 지도를 요청하면 지도 제공<ol>
<li>map_server 노드가 제공하는 service<pre><code> * static_map(nav_msgs/GetMap)</code></pre><ol start="2">
<li>map_server 노드가 제공하는 latched topic = 토픽의 마지막 msg가 저장됨<ul>
<li>map_metadata(nav_msgs/MapMetaData) - .yaml파일에 있는 정보들</li>
<li>map (nav_msgs/OccupancyGrid) - -1,0,100으로 그린 map</li>
</ul>
</li>
</ol>
</li>
</ol>
</li>
</ul>
<p>아래 명령어로 지도의 정보를 또ㅍ다른 노드 (e.g move_base) 에게 제공할 수 있다.
(파일이 저장된 경로에서는 경로 지정없이 파일 이름만으로 가능)</p>
<pre><code>rosrun map_server map_server map_file.yaml
rqt</code></pre><p>rqt를 열어보면 /map 과 /map_metadata가 topic으로 publishing되는 것을 확인할 수 있다.</p>
<p>rviz를 켜고 map을 add 한 후 rosrun으로 map을 켜주면 rviz에 map이 나타난다.</p>
<h2 id="실습">실습</h2>
<h3 id="mission">mission</h3>
<h4 id="map_server-노드를-rosrun이-아닌-launch-파일로-실행하기">map_server 노드를 rosrun이 아닌 launch 파일로 실행하기</h4>
<ul>
<li>arg 태그를 활용해 .yaml 파일도 받을 수 있도록 만들기</li>
</ul>
<pre><code>code ~/sim_ws/src/programmers_turtlebot/turtlebot_navigation_gazebo/launch/map_server.launch</code></pre><p>&lt;map_server.launch&gt;</p>
<pre><code>&lt;launch&gt;
    &lt;arg name=&quot;map_file&quot; default=&quot;/home/yujin/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><pre><code>roscore
rviz
roslaunch turtlebot_navigation_gazebo map_server.launch</code></pre><p>.</p>
<h4 id="static_map-service를-처리할-수-있는-service-client-node-call_map_servicepy-작성하기">/static_map service를 처리할 수 있는 service client node (call_map_service.py) 작성하기</h4>
<ul>
<li>지도 데이터를 가져오기 위해 /static_map 서비스를 호출</li>
<li>결과로 터미널에 지도의 크기와 해상도를 인쇄</li>
</ul>
<pre><code>mkdir ~/sim_ws/src/programmers_turtlebot/turtlebot_navigation_gazebo/script
code ~/sim_ws/src/programmers_turtlebot/turtlebot_navigation_gazebo/script/call_map_service.py</code></pre><p>&lt;call_map_service.py&gt;</p>
<pre><code>#! /usr/bin/env python3

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><pre><code>chmod +x call_map_service.py
roscore
roslaunch turtlebot_navigation_gazebo map_server.launch
rosrun turtlebot_navigation_gazebo call_map_service.py</code></pre><p>(rviz)</p>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/89d03e3f-f326-4066-a1e3-27e0bc80cc4b/image.png" alt=""></p>
<p>terminal에서 map이 출력된 것을 확인할 수 있다.
static map - 호출했을 때의 상태를 유지 (환경이 변화해도 반영되지 않음, 2D map이므로 높이 정보 없음 - 드론에는 적용 불가)</p>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/d9bbbb1b-6d42-4dc9-9c5d-e025e8e86d9c/image.png" alt=""></p>
<p>.
.</p>
<h1 id="transforms">Transforms</h1>
<h2 id="좋은-mapping을-위한-다양한-조건들">좋은 mapping을 위한 다양한 조건들</h2>
<h3 id="2d-lidar-slam을-위한-하드웨어-요구사항">2D Lidar SLAM을 위한 하드웨어 요구사항</h3>
<ul>
<li>Good Laser Data</li>
<li>Good Odometry Data</li>
</ul>
<p>Odometry : 원점으로부터 로봇이 얼마나 이동했는가를 나타내는 값</p>
<h3 id="transforms-1">Transforms</h3>
<p>.
    - 한 프레임에 표현된 데이터를 다른 프레임으로 변환하는 방법
    - Laser 정보를 이용해 map을 구성하기 위해서는 Laser의 위치와 방향을 로봇에게 알려줘야함 = transform
    - Position, Orientation 정보를 포함</p>
<p>예를 들어 lidar가 30cm 앞에 장애물이 있다고 인지했다면 로봇의 중심으로부터의 거리는 또 다를 것이다.
그래서 lidar frame에서 robot frame으로 변환이 필요하다.</p>
<h4 id="mapping을-위해-꼭-필요한-변환-관계">Mapping을 위해 꼭 필요한 변환 관계</h4>
<ul>
<li>laser_link -&gt; base_link<ul>
<li>일반적으로 고정된 값<ul>
<li>robot_state_publisher or static_transfrom_publisher 노드에 의해 주기적으로 broadcast</li>
</ul>
</li>
</ul>
</li>
<li>base_link -&gt; odom (기준위치로부터 로봇이 얼마나 움직였는가)<ul>
<li>Odometry 시스템에 의해 제공 (wheel odometry, visual odometry)</li>
</ul>
</li>
</ul>
<p>.
.</p>
<p>tf 시각화하기</p>
<pre><code>roslaunch turtlebot_navigation_gazebo main.launch
rqt</code></pre><p>rqt에서
plugins &gt; visualization &gt; tf tree</p>
<p>변환관계를 확인할 수 있음!</p>
<h4 id="tf-broadcast---tf-관계-설정하는-방법">TF Broadcast - TF 관계 설정하는 방법</h4>
<ul>
<li>static_transform_pulbisher<ul>
<li>x, y, z: 프레임간의 거리 오프셋</li>
<li>yaw, pitch, roll (rad): 프레임 간의 회전 오프셋</li>
<li>frame_id, child_frame : 각각의 frame ID</li>
<li>period_in_ms : TF를 퍼블리싱할 주기</li>
</ul>
</li>
</ul>
<p>방법1</p>
<pre><code>rosrun tf static_transform_publisher x y z yaw pitch roll frame_id child_frame_id period_in_ms</code></pre><p>방법2</p>
<pre><code>&lt;launch&gt;
    &lt;node pkg=&quot;tf&quot; type=&quot;static_transform_publisher name=&quot;node_name&quot;
          arg=&quot;x y z yaw pitch roll frame_id child_frame_id period_in_ms&quot;&gt;
    &lt;/node&gt;
&lt;/launch&gt;</code></pre><h2 id="실습-1">실습</h2>
<h3 id="mission-1">mission</h3>
<ul>
<li>static_transform_publisher 노드를 시작하기 위한 launch 파일 만들기(pub_static.launch)</li>
<li>로봇 최상단에 button을 부착한다고 가정, button과 로봇의 base_link 사이의 TF publishing</li>
<li>TF 시각화를 통해 프레임 그래프를 다시 생성하고 새 Transform이 게시되는지 확인<pre><code>code ~/sim_ws/src/programmers_turtlebot/turtlebot_navigation_gazebo/launch/pub_static.launch</code></pre></li>
</ul>
<p>&lt;pub_static.launch&gt;</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><pre><code>roslaunch turtlebot_navigation_gazebo main.launch
rviz
roslaunch turtlebot_navigation_gazebo pub_static.launch
rqt</code></pre><p>button 노드가 추가되었다.
rviz 상에서 RobotModel과 TF 를 추가하여 만든 tf를 확인할 수 있다.
<img src="https://velog.velcdn.com/images/yujin-shim/post/1da6e6f8-6a7d-4e97-b6f5-a39f9ade7942/image.png" alt=""></p>
<ul>
<li>모든 로봇을 설계할 때 이런 식으로 하나씩 다 tf를 정의해야하는 걸까??
  -&gt; 아니다. 로봇 모델의 정의를 하는 URDF에서 TF를 정의한다. 가끔 하나씩 추가를 해줘야할 떄 사용하기에 유용하다.</li>
</ul>
<p>.
.</p>
<h1 id="오픈소스-slam-패키지">오픈소스 SLAM 패키지</h1>
<h2 id="gmapping의-일반-파라미터">gmapping의 일반 파라미터</h2>
<ul>
<li><p>roslaunch, yaml 파일에서 별도로 설정할 수 있음</p>
</li>
<li><p>base_frame (default: &quot;base_link&quot;)</p>
<ul>
<li>모바일 베이스의 기준 프레임 - 모바일 베이스, 로봇의 기준점을 의미    </li>
</ul>
</li>
<li><p>map_frame (default: &quot;map&quot;)</p>
<ul>
<li>지도의 기준이 될 프레임의 이름 - map을 기준으로 baseframe이 어디있는지 알기 위해</li>
</ul>
</li>
<li><p>odom_frame (default: &quot;odom&quot;)</p>
<ul>
<li>Odometry 시스템에 연결된 프레임의 이름 - map -&gt; odom -&gt; base</li>
</ul>
</li>
<li><p>map_update_interval(default: 5.0)</p>
<ul>
<li>지도를 업데이트할 때까지 대기할 시간을 설정 </li>
</ul>
</li>
</ul>
<h2 id="gmapping의-laser-관련-파라미터">gmapping의 laser 관련 파라미터</h2>
<ul>
<li>maxRange (float)<ul>
<li>Laser의 최대 범위를 설정</li>
<li>이 값은 실제 센서의 최대 범위보다 약간 높은 값으로 설정</li>
</ul>
</li>
<li>maxUrange (default: 80.0m)<ul>
<li>Laser의 최대 사용 가능 범위를 설정</li>
<li>Laser의 beam이 이 값으로 잘림</li>
</ul>
</li>
<li>minimumScore (default: 0.0)<ul>
<li>Laser의 센서 값이 양호하다고 간주하는 최소 점수를 설정</li>
</ul>
</li>
</ul>
<h2 id="gmapping의-map-관련-파라미터">gmapping의 Map 관련 파라미터</h2>
<ul>
<li>xmin (default: -100.0)<ul>
<li>initial map size</li>
</ul>
</li>
<li>ymin (default: -100.0)<ul>
<li>initial map size</li>
</ul>
</li>
<li>xmax (default: 100.0)<ul>
<li>initial map size</li>
</ul>
</li>
<li>ymax (default: 100.0)<ul>
<li>initial map size</li>
</ul>
</li>
<li>delta (default: 0.05)<ul>
<li>sets the resolution of the map</li>
</ul>
</li>
</ul>
<h2 id="gmapping의-기타-파라미터">gmapping의 기타 파라미터</h2>
<ul>
<li>linearUpdate (default:1.0)<ul>
<li>laser 센서 값을 처리하기 위해, 로봇이 이동해야 하는 선형 거리</li>
<li>최소한 1m는 이동해야 map이 업데이트 된다</li>
</ul>
</li>
<li>angularUpdate (default: 0.5)<ul>
<li>laser 센서값을 처리하기 위해, 로봇이 이동해야 하는 각도</li>
<li>최소한 0.5rad는 회전을 해야 map이 업데이트 된다</li>
</ul>
</li>
</ul>
<h2 id="yaml-파일을-이용한-파라미터-load">YAML 파일을 이용한 파라미터 load</h2>
<h3 id="launch-파일에서의-yaml-파일-업로드">launch 파일에서의 yaml 파일 업로드</h3>
<pre><code>&lt;rosparam file=$(find my_mapping_launcher)/params/gmapping_params.yaml&quot; commad = &quot;load&quot; /&gt;</code></pre><h3 id="yaml-파일-내부구조">yaml 파일 내부구조</h3>
<pre><code>name_of_parameter: value_of_parameter</code></pre><h2 id="실습-2">실습</h2>
<h3 id="mission-2">mission</h3>
<ol>
<li><p>map_update_interval을 15로 바꾸고 map update 주기 확인</p>
<ul>
<li>키보드로 로봇을 움직여 보면서 update주기가 바뀌었는지 확인</li>
</ul>
</li>
<li><p>maxUrange를 2로 바꾸고 mapping 영역 확인</p>
<ul>
<li><p>map이 없데이트되는 범위가 확 줄어든다.</p>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/de905cce-a29d-4210-84c0-06ac9c5f5380/image.png" alt=""></p>
</li>
</ul>
</li>
<li><p>xmin, ymin를 -100d으로 xmax, ymax를 100으로 설정하고 초기 map이 어떻게 보이는지 확인</p>
<ul>
<li>scan할 수 있는 범위가 매우 넓어짐 (로봇이 움직이면 더 확장됨)</li>
</ul>
</li>
</ol>
<ol start="4">
<li>모든 파라미터 변겅은 yaml파일에서 하도록 함 (gmapping_params.yaml)</li>
</ol>
<p>.</p>
<pre><code>code ~/srm_ws/src/programmers_turtlebot/turtlebot_navigation_gazebo/param/gmapping_params.yaml</code></pre><p>programmers_turtlebot/turtlebot_navigation/launch/includes/gmapping/gmapping.launch.xml 파일을 참고해서 yaml 파일의 양식에 맞게 작성해주면 된다.</p>
<p>&lt;gmapping_params.yaml&gt;</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>이제 이 파라미터들을 읽어올 launch 파일을 만들어보자</p>
<pre><code>code ~/sim_ws/src/programmers_turtlebot/turtlebot_navigation_gazebo/launch/my_gmapping.launch</code></pre><p>&lt;my_gmapping.launch&gt;</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><p>일단 launch 파일을 잘 설정했는지 확인하기 위해 실행해본 후 
.yaml 파일의 파라미터를 바꿔보면 된다.
(yaml 파일에서 파라미터를 바꿀 때마다 launch 다시 해야함)</p>
<pre><code>roslaunch turtlebot_navigation_gazebo main.launch
roslaunch turtlebot_navigation_gazebo my_gmapping.launch
roslaunch turtlebot_rviz_launchers my_gmapping.launch</code></pre><p>.
.</p>
<h1 id="기타-tip">기타 Tip</h1>
<h2 id="map을-수동으로-조작하기">Map을 수동으로 조작하기</h2>
<ul>
<li>직접 포토샵을 이용해서 지울 수 있다. - gimp</li>
</ul>
<h2 id="logging된-데이터를-이용해-map-생성하기">Logging된 데이터를 이용해 Map 생성하기</h2>
<ul>
<li>매번 mapping을 할 수 없으니까 bag 파일로 저장해서 slam 만 추후에 진행하는 방식</li>
</ul>
<ol>
<li>bag file 만들기<ul>
<li>LaserScan topic과 TF 정보를 recording 하기</li>
</ul>
</li>
</ol>
<pre><code>robag record -O mylaserdata /laser_topic /tf_topic</code></pre><p>2.bag file을 재생하고 map 생성하기</p>
<pre><code>rosbag play bag_file_name</code></pre><h3 id="실습-3">실습</h3>
<p>키보드로 로봇을 움직이며 그동안의 bag file 을 저장</p>
<pre><code>roslaunch turtlebot_navigation_gazebo main.launch
roslaunch turtlebot_teleop keyboard_teleop.launch
rosbag record -O mylaser /kobuki/laser/scan /tf /tf_static</code></pre><p>모두 종료 후 저장된 bag 파일로 mapping</p>
<pre><code>roslaunch turtlebot_navigation_gazebo my_gmapping.launch
roslaunch turtlebot_rviz_launchers view_mapping.launch
rosbag info mylaser.bag (잘 저장되었나 확인)
rosbag play mylaser.bag</code></pre><p>rviz상에서 map이 점점 그려지는 것을 확인할 수 있다.</p>
<p>.
.</p>
<h1 id="과제---map-만들기">과제 - Map 만들기</h1>
<h2 id="목표">목표</h2>
<ul>
<li>corridor.world 환경의 Map 구성하기</li>
<li>또는 4강 과제에서 만들었던 나만의 world를 load해서 map 구성하기
  (.pgm &amp; .yaml 파일 제출)</li>
</ul>
<h2 id="mission-3">Mission</h2>
<h3 id="기존-실행했던-mainlaunch-파일을-수정해-다른-world-load">기존 실행했던 main.launch 파일을 수정해 다른 world load</h3>
<p>나는 corridor_main.launch 라는 이름으로 파일을 만들어주었다.
나머지는 main.launch 파일과 모두 동일하고 world 파일만 corridor로 바꾸어주면 된다.</p>
<p>&lt;corridor_main.launch&gt;</p>
<pre><code>&lt;launch&gt;
  &lt;arg name=&quot;world_file&quot;  default=&quot;$(find turtlebot_navigation_gazebo)/worlds/corridor.world&quot;/&gt; 
  &lt;!-- &lt;arg name=&quot;world_file&quot;  default=&quot;$(find summit_xl_gazebo)/worlds/test_zone.sdf&quot;/&gt; --&gt;

  &lt;arg name=&quot;base&quot;      value=&quot;$(optenv TURTLEBOT_BASE kobuki)&quot;/&gt; &lt;!-- create, roomba --&gt;
  &lt;!-- arg name=&quot;battery&quot;   value=&quot;$(optenv TURTLEBOT_BATTERY /proc/acpi/battery/BAT0)&quot;/ --&gt;  &lt;!-- /proc/acpi/battery/BAT0 --&gt; 
  &lt;arg name=&quot;stacks&quot;    value=&quot;$(optenv TURTLEBOT_STACKS hexagons)&quot;/&gt;  &lt;!-- circles, hexagons --&gt; 
  &lt;arg name=&quot;3d_sensor&quot; value=&quot;$(optenv TURTLEBOT_3D_SENSOR kinect)&quot;/&gt;  &lt;!-- kinect, asus_xtion_pro --&gt; 

  &lt;arg name=&quot;paused&quot; default=&quot;false&quot;/&gt;
  &lt;arg name=&quot;use_sim_time&quot; default=&quot;true&quot;/&gt;
  &lt;arg name=&quot;gui&quot; default=&quot;true&quot;/&gt;
  &lt;arg name=&quot;headless&quot; default=&quot;false&quot;/&gt;
  &lt;arg name=&quot;debug&quot; default=&quot;false&quot;/&gt;

  &lt;include file=&quot;$(find gazebo_ros)/launch/empty_world.launch&quot;&gt;
    &lt;arg name=&quot;world_name&quot; value=&quot;$(arg world_file)&quot; /&gt;
    &lt;arg name=&quot;paused&quot; value=&quot;$(arg paused)&quot;/&gt;
    &lt;arg name=&quot;use_sim_time&quot; value=&quot;$(arg use_sim_time)&quot;/&gt;
    &lt;arg name=&quot;gui&quot; value=&quot;$(arg gui)&quot;/&gt;
    &lt;arg name=&quot;headless&quot; value=&quot;$(arg headless)&quot;/&gt;
    &lt;arg name=&quot;debug&quot; value=&quot;$(arg debug)&quot;/&gt;
  &lt;/include&gt;

  &lt;include file=&quot;$(find turtlebot_navigation_gazebo)/launch/includes/$(arg base).launch.xml&quot;&gt;
    &lt;arg name=&quot;base&quot; value=&quot;$(arg base)&quot;/&gt;
    &lt;arg name=&quot;stacks&quot; value=&quot;$(arg stacks)&quot;/&gt;
    &lt;arg name=&quot;3d_sensor&quot; value=&quot;$(arg 3d_sensor)&quot;/&gt;
  &lt;/include&gt;

  &lt;node pkg=&quot;robot_state_publisher&quot; type=&quot;robot_state_publisher&quot; name=&quot;robot_state_publisher&quot;&gt;
    &lt;param name=&quot;publish_frequency&quot; type=&quot;double&quot; value=&quot;30.0&quot; /&gt;
  &lt;/node&gt;

  &lt;!-- Fake laser --&gt;
  &lt;!-- node pkg=&quot;nodelet&quot; type=&quot;nodelet&quot; name=&quot;laserscan_nodelet_manager&quot; args=&quot;manager&quot;/&gt;
  &lt;node pkg=&quot;nodelet&quot; type=&quot;nodelet&quot; name=&quot;depthimage_to_laserscan&quot;
        args=&quot;load depthimage_to_laserscan/DepthImageToLaserScanNodelet laserscan_nodelet_manager&quot;&gt;
    &lt;param name=&quot;scan_height&quot; value=&quot;10&quot;/&gt;
    &lt;param name=&quot;output_frame_id&quot; value=&quot;/camera_depth_frame&quot;/&gt;
    &lt;param name=&quot;range_min&quot; value=&quot;0.45&quot;/&gt;
    &lt;remap from=&quot;image&quot; to=&quot;/camera/depth/image_raw&quot;/&gt;
    &lt;remap from=&quot;scan&quot; to=&quot;/scan&quot;/&gt;
  &lt;/node --&gt;
&lt;/launch&gt;
</code></pre><p>.
.</p>
<h3 id="slam_gmapping-노드를-실행시키고-mapping-수행하기">slam_gmapping 노드를 실행시키고 mapping 수행하기</h3>
<pre><code>roslaunch turtlebot_navigation_gazebo corridor_main.launch
roslaunch turtlebot_navigation_gazebo gmapping_demo.launch
roslaunch turtlebot_rviz_launchers view_mapping.launch
roslaunch turtlebot_teleop keyboard_teleop.launch</code></pre><p>위 launch 파일들을 모두 실행하고 키보드로 world를 돌아다니며 mapping을 해주면 된다.</p>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/f385fb3d-52eb-4555-97d7-e218eef9e445/image.gif" alt=""></p>
<h3 id="mapping-결과-저장하기">mapping 결과 저장하기</h3>
<pre><code>rosrun map_server map_saver -f corridor_map_yj</code></pre><p>이 명령어를 실행하고 나면 .pgm파일과 .yaml 파일로 map이 저장된다.</p>
<p>&lt;corridor_map_yj.pgm&gt;</p>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/27d7ec9e-c174-4698-9238-745c0c1e8780/image.png" alt=""></p>
<p>&lt;corridor_map_yj.yaml&gt;</p>
<pre><code>image: corridor_map_yj.pgm
resolution: 0.050000
origin: [-12.200000, -13.800000, 0.000000]
negate: 0
occupied_thresh: 0.65
free_thresh: 0.196</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Simulation] Navigation]]></title>
            <link>https://velog.io/@yujin-shim/Simulation-Navigation</link>
            <guid>https://velog.io/@yujin-shim/Simulation-Navigation</guid>
            <pubDate>Mon, 26 Jun 2023 14:37:31 GMT</pubDate>
            <description><![CDATA[<h1 id="심화편-소개">심화편 소개</h1>
<h2 id="시뮬레이션에-활용될-로봇들">시뮬레이션에 활용될 로봇들</h2>
<ul>
<li>kobuki = turtlebot</li>
<li>Husky</li>
<li>Summit XL</li>
</ul>
<h2 id="목적">목적</h2>
<ol>
<li>ROS 기반의 Navigation Stack 학습</li>
<li>Navigation Stack으로 프로젝트 진행</li>
</ol>
<h2 id="필요한-파일-다운로드-받기">필요한 파일 다운로드 받기</h2>
<p>~/sim_ws/src에 programmers_turtlebot 파일 다운로드 받기
아래 명령어로 dependency 설치 후 build 해주기</p>
<p>ubuntu 20.04의 code name = focal
ubuntu 18.04의 code name = bionic</p>
<p>자신의 ubuntu 버전에 맞게 code name을 바꿔서 아래내용을 terminal에 쳐주자</p>
<pre><code>rosdep install --from-paths src --ignore-src -r -y --os=ubuntu:bionic
catkin_make
roslaunch turtlebot_navigation_gazebo main.launch</code></pre><p>.
.</p>
<h1 id="자율주행에-필요한-요소">자율주행에 필요한 요소</h1>
<ul>
<li>ROS기반의 Navigation stack을 구성해서 로봇이 특정 환경을 자율적으로 탐색하도록 만들기 위해서는</li>
</ul>
<ol>
<li>Mapping (로봇이 움직이며 lidar와 같은 센서로 주변환경을 지도로 얻는 과정)<pre><code>roslaunch turtlebot_navigation_gazebo main.launch
roslaunch turtlebot_navigation_gazebo gmapping_demo.launch
roslaunch turtlebot_teleop keyboard_teleop.launch
roslaunch turtlebot_rviz_launchers view_mapping.launch</code></pre></li>
</ol>
<p>keyboard로 로봇을 움직이며 환경을 mapping 해보면 된다.</p>
<p>.
.</p>
<ol start="2">
<li>Localization (지도 속에서 내가 어느 위치에 있는지, 어느 방향을 향하고 있는지 알아야 함)</li>
</ol>
<pre><code>roslaunch turtlebot_navigation_gazebo main.launch
roslaunch turtlebot_navigation_gazebo amcl_demo.launch
roslaunch turtlebot_rviz_launchers view_localization.launch
roslaunch turtlebot_teleop keyboard_teleop.launch
</code></pre><p>녹색화살표 = localization 추정치</p>
<p>로봇을 움직이다 보면 laser scan (red)와 map이 어느정도 일치하는 것을 확인할 수 있다.</p>
<ol start="3">
<li>Path planning (경로 계획)</li>
</ol>
<p>rviz 상에서</p>
<ul>
<li>2D Pose Estimate : 로봇의 위치와 방향을 지정해줄 수 있음</li>
<li>2D Nav Goal : 로봇의 목표위치 지정 (로봇을 목표위치로 보내줄 수 있음)</li>
</ul>
<pre><code>roslaunch turtlebot_navigation_gazebo main.launch
roslaunch turtlebot_navigation_gazebo move_base_demo.launch
roslaunch turtlebot_rviz_launchers view_planning.launch
</code></pre><ol start="4">
<li>Obstacle Avoidance<pre><code>roslaunch turtlebot_navigation_gazebo main.launch
roslaunch turtlebot_navigation_gazebo move_base_demo.launch
roslaunch turtlebot_rviz_launchers view_navigation.launch
</code></pre></li>
</ol>
<pre><code>
민트색 cost map과 로봇 주위에 local cost map이 생성된 것을 확인할 수 있다.
</code></pre><p>cd ~/turtlebot_navigation/urdf
rosrun gazebo_ros spawn_model -file object.urdf -urdf -x 0 -y 0 -z 1 -model my_object</p>
<pre><code>장애물을 spawn하고 로봇을 근처로 이동해보면 장애물을 감지하는 것을 확인할 수 있다
rviz상에서 장애물을 넘어가도록 2D Nav Goal을 설정해주면 장애물을 피해서 가는 것을 확인할 수 있다.
갑자기 object를 앞에 두어도 로봇이 잘 피해가는 것을 확인할 수 있다.


&gt; 소소한 TIP
roslaunch를 실행하다보면 계속 다른 terminal을 켜야하는 경우가 생긴다.
이때는 terminator를 이용하면 편하다 
&gt;&gt; sudo apt-get install terminator
ctrl + shift + O (가로분할) or 한영키 옆 menu + O
ctrl + shift + E (세로분할) or 한영키 옆 menu + E
.
.

# Navigation Stack

- 사용하려는 로봇을 올바르게 구성하고 정의하는 것이 중요
- 로봇의 구성 및 정의 : URDF 파일

## 하드웨어 요구사항

- kinematics
    - Differential drive
    - Holonomic Drive (Omnidirectional Drive)
    - Skid steer Drive
    - Ackermann Drive
- Velocity control
    - x, y (linear)
    - z (angualr)
- Sensor
    - LiDAR, Camera ...
- Mobile Robot Shape
    - square, circular ...

[Navigation Stack Setup](http://wiki.ros.org/navigation/Tutorials/RobotSetup)


## 실습

### 목표
- Navigation Stack 구성 요소를 시뮬레이션 환경에서 확인해보기

### Mission
- Kobuki와 Navigation 관련 Launch file 실행
- Navigation stack에 필요한 Topic 체크
</code></pre><p>roslaunch turtlebot_navigation_gazebo main.launch
roslaunch turtlebot_navigation_gazebo move_base_demo.launch
rqt_graph
rostopic list
rosmsg show nav_msgs/Odometry</p>
<pre><code>
### move_base 노드
- move_base 노드와 연결되는 패키지들
 &gt; global-planner
 local planner
 rotate-recovery
 clear-costmap-recovery
 costmap-2D

- move_base 노드와 연결되는 외부 패키지들
&gt; map-server
AMCL
gmapping, slam-karto 등</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[{-ing}[Simulation][Project][Gazebo] 시뮬레이션 기초 전부 활용해보자! 프로젝트]]></title>
            <link>https://velog.io/@yujin-shim/SimulationProject-%EC%8B%9C%EB%AE%AC%EB%A0%88%EC%9D%B4%EC%85%98-%EA%B8%B0%EC%B4%88-%EB%AA%A8%EB%91%90-%ED%99%9C%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@yujin-shim/SimulationProject-%EC%8B%9C%EB%AE%AC%EB%A0%88%EC%9D%B4%EC%85%98-%EA%B8%B0%EC%B4%88-%EB%AA%A8%EB%91%90-%ED%99%9C%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Sun, 25 Jun 2023 11:45:50 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>지금까지 배운 simulation 기초를 모두 활용해볼 프로젝트가 과제로 나왔다!
simulation을 배우는 건 처음인데 너무너무 재미있다. 잘 배워서 나중에 다양한 로봇과 환경에서 활용해보고 싶다.
무엇보다 너무 좋은 강의해주시는 신승렬강사님 너무 감사합니다! &gt;&lt;</p>
</blockquote>
<ul>
<li>과제를 수행한 것이라 모든 내용은 내가 직접 했다 😊</li>
<li>나는 프로그래머스 Planning &amp; Control 1기 과정을 진행중이다 ㅎㅎ</li>
<li>내가 프로젝트를 수행하면서 과정을 기록한 내용이기 때문에 뒤로 갈수록 코드에 변화가 생겼다.
이걸보고 비슷하게 진행해보고 싶은 사람은 나의 github에 올라와있는 코드(=최종본)를 보는 것이 이해하는데 더 편할 것 같다.</li>
</ul>
<h1 id="프로젝트---시뮬레이터-기초편-전체-활용">프로젝트 - 시뮬레이터 기초편 전체 활용</h1>
<h2 id="개발-환경-준비하기">개발 환경 준비하기</h2>
<h3 id="새-ros-패키지-만들기">새 ROS 패키지 만들기</h3>
<ul>
<li>프로젝트와 관련한 모든 것을 수행할 mid_project라는 새 패키지 생성</li>
<li>지금까지 작성했던 dependency들을 잘 활용할 것</li>
</ul>
<p>그동안 배운 simulation 기초를 모두 활용해보는 과제이므로
지금까지 이용한, 그리고 지금 프로젝트에 필요한 dependency를 모두 추가해서 package를 만들었다.
.
.</p>
<pre><code>cd ~/sim_ws/src
catkin_create_pkg mid_project rospy roscpp urdf xacro gazebo gazebo_ros gazebo_plugins</code></pre><p>.
.</p>
<h2 id="새로운-world-만들기">새로운 world 만들기</h2>
<h3 id="launch-파일과-world-파일-만들기">Launch 파일과 world 파일 만들기</h3>
<pre><code>(가이드)
~/sim_ws/src/mid_project/launch/gazebo.launch
~/sim_ws/src/mid_project/launch/spawn.launch
~/sim_ws/src/mid_project/models/custom_mid_obj
    (이 directory에 model.config와 model.sdf 포함)</code></pre><p>일단 나는 헷갈릴까봐 모든 폴더를 다 만들어 두었다.</p>
<pre><code>mkdir -p ~/sim_ws/src/mid_project/launch
mkdir -p ~/sim_ws/src/mid_project/models/custom_mid_obj
mkdir -p ~/sim_ws/src/mid_project/worlds
mkdir -p ~/sim_ws/src/mid_project/</code></pre><h3 id="world-만들기-wallsworld">World 만들기 (walls.world)</h3>
<pre><code>code ~/sim_ws/src/mid_project/worlds/walls.world
code ~/sim_ws/src/mid_project/launch/gazebo.launch</code></pre><h4 id="gazebo에서-기본으로-제공하는-shape들을-활용해-custom_mid_obj-만들기">Gazebo에서 기본으로 제공하는 shape들을 활용해 custom_mid_obj 만들기</h4>
<p>강의에서 주어진 모델은 이렇게 생겼다. <img src="https://velog.velcdn.com/images/yujin-shim/post/1fdaf96d-ec61-40e3-8e1f-dd22285f36be/image.png" alt=""></p>
<p>기본으로 제공되는 box와 cylinder를 이용해서 shape을 만들어 주었다.</p>
<ul>
<li>만드는 파일 : model.sdf &amp; model.config</li>
<li><ul>
<li>만들고 나서 /.gazebo/models에 폴더 자체를 추가해주는 것 잊지 말자~</li>
</ul>
</li>
<li>** 나중에 로봇이 라이더로 돌아다닐 예정이므로 collision 설정도 해주어야 한다!
.
.<blockquote>
<p>[Error] : [parser.cc:759] XML Attribute[length] in element[cylinder] not defined in SDF, ignoring.
.sdf 파일에서 cylinder 태그를 아래와 같이 radius와 length를 적어서 만들어졌었는데 내가 원하는대로 반지름이나 길이가 반영이 안되서 터미널을 보니 에러가 나고 있었다.
<img src="https://velog.velcdn.com/images/yujin-shim/post/2725475a-1435-4b6d-8e85-c04177408cdd/image.png" alt=""></p>
<blockquote>
<p>[Solution] 아래처럼 태그로 달아주면 해결~
<img src="https://velog.velcdn.com/images/yujin-shim/post/477078b1-33a0-4c7b-8d26-2f9c91d2b101/image.png" alt=""></p>
</blockquote>
</blockquote>
</li>
</ul>
<p>.
.</p>
<p>.sdf 파일에서 정해주는 model name이 앞으로 world에서 include 할 때 적는 이름이니까 잘 설정해두자 :)</p>
<p>&lt;model.sdf&gt;</p>
<pre><code>&lt;?xml version=&#39;1.0&#39;?&gt;
&lt;sdf version=&quot;1.4&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;!-- collision --&gt;
            &lt;collision name=&quot;collision1_box1&quot;&gt;
                &lt;pose&gt;1.55 1 0 0 0 0.523&lt;/pose&gt;
                &lt;geometry&gt;
                    &lt;box&gt;
                        &lt;size&gt;0.3 2 0.5&lt;/size&gt;
                    &lt;/box&gt;
                &lt;/geometry&gt;
            &lt;/collision&gt;

            &lt;collision name=&quot;collision2_box2&quot;&gt;
                &lt;pose&gt;1 0 0 0 0 0&lt;/pose&gt;
                &lt;geometry&gt;
                    &lt;box&gt;
                        &lt;size&gt;2 0.3 0.5&lt;/size&gt;
                    &lt;/box&gt;
                &lt;/geometry&gt;
            &lt;/collision&gt;

            &lt;collision name=&quot;collision3_box3&quot;&gt;
                &lt;pose&gt;0.55 1 0 0 0 1.0472&lt;/pose&gt;
                &lt;geometry&gt;
                    &lt;box&gt;
                        &lt;size&gt;2 0.3 0.5&lt;/size&gt;
                    &lt;/box&gt;
                &lt;/geometry&gt;
            &lt;/collision&gt;


            &lt;collision name=&quot;collision4_cylinder1&quot;&gt;
                &lt;pose&gt;0 0 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;collision name=&quot;collision5_cylinder2&quot;&gt;
                &lt;pose&gt;2 0 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;collision name=&quot;collision6_cylinder3&quot;&gt;
                &lt;pose&gt;1 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;/collision&gt;


            &lt;!-- visual --&gt;
            &lt;visual name=&quot;box1(left)&quot;&gt;
                &lt;pose&gt;1.55 1 0 0 0 0.523&lt;/pose&gt;
                &lt;geometry&gt;
                    &lt;box&gt;
                        &lt;size&gt;0.3 2 0.5&lt;/size&gt;
                    &lt;/box&gt;
                &lt;/geometry&gt;
            &lt;/visual&gt;
            &lt;visual name=&quot;box2(xaxis)&quot;&gt;
                    &lt;pose&gt;1 0 0 0 0 0&lt;/pose&gt;
                    &lt;geometry&gt;
                        &lt;box&gt;
                            &lt;size&gt;2 0.3 0.5&lt;/size&gt;
                        &lt;/box&gt;
                    &lt;/geometry&gt;
            &lt;/visual&gt;

            &lt;visual name=&quot;box3(right)&quot;&gt;
                &lt;pose&gt;0.55 1 0 0 0 1.0472&lt;/pose&gt;
                &lt;geometry&gt;
                    &lt;box&gt;
                        &lt;size&gt;2 0.3 0.5&lt;/size&gt;
                    &lt;/box&gt;
                &lt;/geometry&gt;
            &lt;/visual&gt;

            &lt;visual name=&quot;cylinder1&quot;&gt;
                &lt;pose&gt;0 0 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;visual name=&quot;cylinder2&quot;&gt;
                &lt;pose&gt;2 0 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;visual name=&quot;cylinder3&quot;&gt;
                &lt;pose&gt;1 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;/link&gt;
    &lt;/model&gt;
&lt;/sdf&gt;
</code></pre><p>&lt;model.config&gt;</p>
<pre><code>&lt;?xml version=&quot;1.0&quot;?&gt;

&lt;model&gt;
    &lt;name&gt;Custom 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;yujin&lt;/name&gt;
        &lt;email&gt;aaaa@gmail.com&lt;/email&gt;
    &lt;/author&gt;

    &lt;description&gt;
        custom object
    &lt;/description&gt;
&lt;/model&gt;</code></pre><p>.
.
.</p>
<h4 id="gazebo-model-library에서-제공하는-grey_wall을-활용해-벽-세우기-20mx20m의-정사각형">Gazebo Model Library에서 제공하는 &quot;grey_wall&quot;을 활용해 벽 세우기 (20mx20m의 정사각형)</h4>
<p>&lt;walls.world&gt; 에 population으로 벽을 만들어주었다.
벽이 규격대로 사각형이 딱 맞게 나타나는 것이 아니라서 
일단 위치를 맞춘 후에 보면서 약간의 미세 조정이 필요하다. (약간 오래 걸림)
(그래서 20x20이지만 xy의 좌표가 10,-10만 있는 것은 아님)</p>
<p>가능하면 Gazebo ui 상에서 벽을 배치한 후 .world파일로 저장하는 것이 편할 것 같다.
(나는 virtual machine을 쓰고 있어서 그런지 world를 저장하면 자꾸 UI가 멈춰서..... population으로 만들어줬다.)</p>
<p>&lt;walls.world&gt;</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;grey_wall&quot;&gt;
            &lt;model name=&quot;grey_wall_1&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 10 0.3 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 barriers --&gt;
        &lt;population name=&quot;grey_wall&quot;&gt;
            &lt;model name=&quot;grey_wall_2&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;2.8 -10.6 0.3 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 barriers --&gt;
        &lt;population name=&quot;grey_wall&quot;&gt;
            &lt;model name=&quot;grey_wall_3&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 3 0.3 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 barriers --&gt;
        &lt;population name=&quot;grey_wall&quot;&gt;
            &lt;model name=&quot;grey_wall_4&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;-11 3 0.3 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/yujin-shim/post/8ee03e12-bdcf-4f86-8d1c-5dd47a2b20a5/image.png" alt=""></p>
<p>.
.</p>
<h4 id="벽-내부에-생성한-custom_mid_obj-4개를-10mx10m의-영역에-균일하게-배치정적-장애물-4개">벽 내부에 생성한 custom_mid_obj 4개를 10mx10m의 영역에 균일하게 배치(정적 장애물 4개)</h4>
<p>population에서 distribution tag는 <a href="https://classic.gazebosim.org/tutorials?tut=model_population&amp;cat=build_world">Population of model</a>를 참고해 <strong>uniform으로</strong> 설정했다.</p>
<p>아래 코드를 위에서 작성한 walls.world에 넣어주면 된다.
&lt;+walls.world&gt;</p>
<pre><code>        &lt;!-- custom_mid_obj --&gt;
        &lt;population name=&quot;custom_mid_obj&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;-1 -1 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;</code></pre><p>/</p>
<p>성공!</p>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/e148c91c-2f46-4d16-8fcd-d31c45b8a071/image.png" alt=""></p>
<p>collision 설정도 잘 된 것을 확인했다.</p>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/f49246b1-c555-4b10-9fb3-aac5843c3ed3/image.png" alt=""></p>
<p>.
.
.
.
.</p>
<h2 id="model-plugin-작성하기">Model Plugin 작성하기</h2>
<h3 id="관련-파일-준비하기">관련 파일 준비하기</h3>
<ul>
<li>Plugin 파일 만들기<pre><code>~/sim_ws/src/mid_project/src/moving_plugin.cc</code></pre></li>
<li>새 Plugin을 컴파일할 수 있도록 CMakeList.txt 파일 수정</li>
</ul>
<h3 id="plugin-작성하기">Plugin 작성하기</h3>
<h4 id="고정해놨던-custom-object-외에-동적-장애물-만들기-동적-장애물-4개">고정해놨던 custom object 외에 동적 장애물 만들기 (동적 장애물 4개)</h4>
<h4 id="moving_plugincc-작성">moving_plugin.cc 작성</h4>
<blockquote>
<p>plugin을 만들어서 모델을 world에 올렸더니 이렇게 신나게 날아다니고 있었다.
의도한 바는 바닥에 붙어서 얌전히 움직이는 것이었는데.. 해결방법은 아직 찾는중이다. 🤣
<img src="https://velog.velcdn.com/images/yujin-shim/post/4e112895-d8b5-42c2-a7a3-8a21ba8a60f7/image.gif" alt=""></p>
</blockquote>
<p>.
.
.
.</p>
<h2 id="로봇-가져오기">로봇 가져오기</h2>
<h3 id="관련-file-준비하기">관련 file 준비하기</h3>
<ul>
<li>urdf 디렉터리와 파일 만들기<pre><code>~/sim_ws/src/mid_project/urdf/robot.xacro
~/sim_ws/src/mid_project/urdf/robot.gazebo</code></pre></li>
</ul>
<h3 id="robot-구성하기">Robot 구성하기</h3>
<ul>
<li>로봇은 4개의 wheel을 보유</li>
<li>이전 강의에서 만들었던 로봇보다 작아야함 (장에물 회피에 용이)</li>
<li>skid steering drive를 활용하여 로봇을 제어하고 odometry 데이터를 읽음</li>
<li>장애물을 피하기 위해 laser scan을 배치</li>
<li>부드러운 움직임을 위해 마찰 계수 조절하기</li>
</ul>
<h2 id="간단한-장애물-회피-알고리즘-구현하기">간단한 장애물 회피 알고리즘 구현하기</h2>
<h3 id="obstacle_aviodancepy-작성하기">obstacle_aviodance.py 작성하기</h3>
<ul>
<li>아래 쪽의 sample_script를 개선한 프로그램 작성하기</li>
<li>로봇이 장애물에 접근하는 경우, 후진을 해야할 수 있음</li>
<li>알고리즘은 laser scan topic을 읽어 callback 함수에서 범위별 측정한 평균값을 지정</li>
<li>로봇을 충분한 여유 공간이 있을 때마다 전진하고, 장애물에 가까워지며 왼쪽으로 방향을 전환하면서 이동</li>
</ul>
<h2 id="결과확인">결과확인</h2>
<pre><code>export GAZEBO_MODEL_PATH=~/sim_ws/src/mid_project/models:$GAZEBO_MODEL_PATH
roslaunch mid_project gazebo.launch
roslaunch mid_project spawn.launch
rosrun mid_project obstacle_aviodance.py</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Visual Studio Code] 파일 확장자 인식 설정 방법 ]]></title>
            <link>https://velog.io/@yujin-shim/Visual-Studio-Code-%ED%8C%8C%EC%9D%BC-%ED%99%95%EC%9E%A5%EC%9E%90-%EC%9D%B8%EC%8B%9D-%EC%84%A4%EC%A0%95-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@yujin-shim/Visual-Studio-Code-%ED%8C%8C%EC%9D%BC-%ED%99%95%EC%9E%A5%EC%9E%90-%EC%9D%B8%EC%8B%9D-%EC%84%A4%EC%A0%95-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Fri, 23 Jun 2023 11:47:04 GMT</pubDate>
            <description><![CDATA[<p>simulation 코딩을 하다보면 .world 파일 같은 확장자를 가진 파일이 xml 인식이 안되는 경우가 있다.</p>
<p>이런 경우는 다 같은 색으로 보여 불편할 수 있으므로 vsc상에서 설정해줄 수 있다.</p>
<p>먼저 visual studio code에서 좌측 하단에 있는 설정 모양을 눌러 setting으로 들어가거나
단축키 :** ctrl+, **로 들어갈 수도 있다.</p>
<p>setting 창이 켜지면 &quot;file association&quot;이라고 검색해 &quot;Add Item&quot;을 해주면 된다.</p>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/0236c123-3564-46a7-b662-14b410645990/image.png" alt=""></p>
<p>나는 .world 파일을 xml로 인식하도록 설정해주었다.</p>
<p>그러면 이후부터 .world 파일을 만들면 자동으로 xml로 인식하게 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Simulation] Gazebo Plugin]]></title>
            <link>https://velog.io/@yujin-shim/Simulation-Gazebo-Plugins</link>
            <guid>https://velog.io/@yujin-shim/Simulation-Gazebo-Plugins</guid>
            <pubDate>Fri, 23 Jun 2023 07:53:36 GMT</pubDate>
            <description><![CDATA[<h1 id="gazebo-plugin이란🤷♀️">Gazebo Plugin이란?🤷‍♀️</h1>
<blockquote>
<p>Gazebo plugins give your URDF models greater functionality and can tie in ROS messages and service calls for sensor output and motor input. </p>
</blockquote>
<blockquote>
<p>Gazebo API는 물체를 움직이거나 센서 데이터를 받아오거나 등등의 다양한 기능을 제공하는데 이때 plugin은 이 API에 접근이 가능하도록 해준다.
Plugin은 C++ library로 </p>
</blockquote>
<h1 id="plugin-개발-환경-준비">Plugin 개발 환경 준비</h1>
<ul>
<li>C++로 만들어짐</li>
</ul>
<h2 id="plugin-만들기">Plugin 만들기</h2>
<pre><code>cd ~/sim_ws/src
catkin_create_pkg writing_plugins gazebo gazebo_ros gazebo_plugins roscpp
code ~/sim_ws/src/writing_plugins/src/my_gazebo_plugin.cc</code></pre><blockquote>
<p>.cc 파일이 처음인 분도 있을 것이다.
  오래된 code base나 embedded system programming에서 사용되는 확장자로 
  c with classes의 약어이고 대부분 cpp compiler에서 작동한다.
  gazebo는 plugin을 만들 때 .cc 파일을 활용한다.</p>
</blockquote>
<p>&lt;my_gazebo_plugin.cc&gt;</p>
<pre><code>#include &lt;gazebo/gazebo.hh&gt;
#include &lt;gazebo/physics/physics.hh&gt;
#include &lt;iostream&gt;
namespace gazebo{
        class MyGazeboPlugin : public WorldPlugin {
            public:
            MyGazeboPlugin() : WorldPlugin() {
                std::cout &lt;&lt; &quot;Plugin constructor method!&quot; &lt;&lt; std::endl;
            }

            public:
            void Load(physics::WorldPtr _world, sdf::ElementPtr _sdf) {
                std::cout &lt;&lt; &quot;Everything is awesome!&quot; &lt;&lt; std::endl;
            }
        };

        // register plugin - why no semicolon..?
        GZ_REGISTER_WORLD_PLUGIN(MyGazeboPlugin)
}</code></pre><p>GZ_REGISTER_WORLD_PLUGIN : gazebo에 plugin을 등록하는데 사용되는 매크로 호출이다.</p>
<p>.</p>
<p>&lt;CMakeLists.txt&gt; 도 수정해줘야한다.</p>
<pre><code>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(my_gazebo_plugin SHARED src/my_gazebo_plugin.cc)
target_link_libraries(my_gazebo_plugin ${GAZEBO_LIBRARIES})</code></pre><p>.</p>
<ul>
<li>find_package : 우리가 사용할 package의 필요한 dependency를 찾고 구성해주는 역할
(패키지 의존성(Package Dependencies)이란 한 패키지가 정상적으로 동작하기 위해서 다른 패키지나 라이브러리 패키지)</li>
<li>catkin_package : package.xml을 바탕으로 cmake에 지시를 줌</li>
<li>include_directories / link_directoies : directory나 link library를 프로젝트에 추가해줌</li>
<li>add_library : 라이브러리를 추가해주고</li>
<li>target_link_libraries : 라이브러리를 연결해주는 역할</li>
</ul>
<p>이제 build를 해준다. 빌드가 되고나면 이제 plugin을 사용할 준비가 되었다는 뜻이다.</p>
<p>나는 catkin_make를 이용해서 계속 package를 빌드해왔다.
정확히는 alias를 정의해서 &#39;cd ~/sim_ws &amp;&amp; catkin_make&#39; 이렇게 한번에 해주고 있다.
문득 catkin build랑 catkin_make는 뭐가 다른지 궁금해서 정리해뒀으니 궁금한 사람은 읽어보자 👍
*한번 catkin_make로 빌드하고 나면 catkin build로 빌드가 안됨</p>
<pre><code>cd ~/sim_ws
catkin build</code></pre><p>.</p>
<blockquote>
<p>[궁금증] catkin_make vs catkin build ?</p>
<blockquote>
<ul>
<li>catkin build를 사용하면 전체적인 build configuration이 훨씬 견고해지고 
isolated package는 병렬로 빌드되므로 훨씬 빠르다.</li>
</ul>
</blockquote>
</blockquote>
<ul>
<li>catkin build는 아무 directory에서나 가능하지만 catkin_make는 top level directory에서만 가능하다.</li>
<li>single package만 build할수도 있다. - catkin build (package_name)</li>
<li>catkin build는 catkin clean, install, list ... 등등의 다양한 subcommands도 제공한다.</li>
</ul>
<p>.
.</p>
<h3 id="plugin-경로설정">Plugin 경로설정</h3>
<ul>
<li>Gazebo plugin 환경변수를 선언해주자.</li>
<li>이렇게 하면 새로 생성한 plugin을 gazebo에게 알려줄 수 있다.</li>
</ul>
<p>simulation을 실행할 때마다 terminal에서 선언해주면 된다.
.
참고로 띄어쓰기를 하면 안된다 ㅎㅎ</p>
<pre><code>export GAZEBO_PLUGIN_PATH=${GAZEBO_PLUGIN_PATH}:~/sim_ws/devel/lib</code></pre><p>.</p>
<h2 id="실행을-위한-파일-생성">실행을 위한 파일 생성</h2>
<h3 id="launch--world-파일-만들기">launch &amp; world 파일 만들기</h3>
<ul>
<li>우리가 만든 package안에 launch &amp; world 폴더와 파일을 만들어주자<pre><code>mkdir -p ~/sim_ws/src/writing_plugins/launch
code ~/sim_ws/src/writing_plugins/launch/gazebo.launch
mkdir -p ~/sim_ws/src/writing_plugins/worlds
code ~/sim_ws/src/writing_plugins/launch/gazebo.world</code></pre><blockquote>
<p>[궁금증] mkdir -p는?
mkdir 뒤에 옵션으로 붙는 -p는 상위폴더도 함께 생성하라는 의미이다.
그 외에도 -v는 생성하고 생성된 경로를 메세지 출력 등의 옵션이 있습니다.
.</p>
</blockquote>
</li>
</ul>
<p>.
&lt;gazebo.launch&gt;</p>
<pre><code>&lt;launch&gt;
    &lt;include file=&quot;$(find gazebo_ros)/launch/empty_world.launch&quot;&gt;
        &lt;arg name=&quot;world_name&quot; value=&quot;$(find writing_plugins)/worlds/gazebo.world&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>&lt;gazebo.world&gt;</p>
<pre><code>&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;sdf version=&quot;1.4&quot;&gt;
    &lt;world name=&quot;default&quot;&gt;
        &lt;plugin name=&quot;my_gazebo_plugin&quot; filename=&quot;libmy_gazebo_plugin.so&quot;/&gt;
    &lt;/world&gt;
&lt;/sdf&gt;</code></pre><blockquote>
<p>.world 파일이 visual studio code에서 xml로 인식이 잘 안된다면 
아래 링크를 참고해 vsc상에서 설정해주자
<a href="https://velog.io/@yujin-shim/Visual-Studio-Code-%ED%8C%8C%EC%9D%BC-%ED%99%95%EC%9E%A5%EC%9E%90-%EC%9D%B8%EC%8B%9D-%EC%84%A4%EC%A0%95-%EB%B0%A9%EB%B2%95">vsc 파일 확장자 설정하기</a></p>
</blockquote>
<h2 id="결과확인">결과확인</h2>
<pre><code>roslaunch writing_plugins gazebo.launch</code></pre><p>실행시키고 나면 위에서 &lt;my_gazebo_plugin.cc&gt;가 실행되어 아래내용이 print된다.</p>
<blockquote>
<p>Plugin constructor method!
Everything is awesome!</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/6cb58d2e-6d8d-4888-b73e-0cc378166851/image.png" alt=""></p>
<p>.
.</p>
<h1 id="world-plugin">World Plugin</h1>
<h2 id="plugin으로-gazebo상에-model-넣기">Plugin으로 gazebo상에 model 넣기</h2>
<p>위에서 만든 my_gazebo_plugin.cc에서 Load 함수 부분만 변경해주면 되지만
나는 더 정확히 이해하기 위해 
다른 이름 my_gazebo_plugin_world.cc 를 만들어주었다.</p>
<h3 id="cc-파일-만들기">.cc 파일 만들기</h3>
<p>&lt;my_gazebo_plugin_world.cc&gt;</p>
<pre><code>#include &lt;gazebo/gazebo.hh&gt;
#include &lt;gazebo/physics/physics.hh&gt;
#include &lt;iostream&gt;
namespace gazebo{
    class MyGazeboPlugin : public WorldPlugin {
        public:
        MyGazeboPlugin() : WorldPlugin() {
            std::cout &lt;&lt; &quot;Plugin constructor method!&quot; &lt;&lt; std::endl;
        }

        public:
        void Load(physics::WorldPtr _world, sdf::ElementPtr _sdf){
            //set a node
            transport::NodePtr node(new transport::Node());
            node-&gt;Init(_world-&gt;Name());

            //set publisher
            transport::PublisherPtr publisher=node-&gt;Advertise&lt;msgs::Factory&gt;(&quot;~/factory&quot;);

            //create msg obj
            msgs::Factory msg;

            //model to use
            msg.set_sdf_filename(&quot;model://jersey_barrier&quot;);

            //send the message
            publisher-&gt;Publish(msg);
        }
    };
    GZ_REGISTER_WORLD_PLUGIN(MyGazeboPlugin)
}</code></pre><p>.
.
.</p>
<h3 id="cmakelisttxt-수정하기">CMakeList.txt 수정하기</h3>
<p>이미 위에서 CMakeList.txt를 수정했기 때문에 1줄만 추가해주면 된다.</p>
<pre><code>add_library(my_gazebo_plugin_world SHARED src/my_gazebo_plugin_world.cc)
</code></pre><p>그 다음 build를 해주면 .so 파일이 생성된 걸 확인할 수 있다.
<img src="https://velog.velcdn.com/images/yujin-shim/post/b8399950-2c8a-43e3-a67b-84b748b49cca/image.png" alt=""></p>
<p>.</p>
<h3 id="world-파일-만들기">world 파일 만들기</h3>
<pre><code>code ~/sim_ws/src/writing_plugins/worlds/gazebo_world.world</code></pre><p>&lt;gazebo_world.world&gt;</p>
<pre><code>&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;sdf version=&quot;1.4&quot;&gt;
    &lt;world name=&quot;default&quot;&gt;
        &lt;plugin name=&quot;my_gazebo_plugin_world&quot; filename=&quot;libmy_gazebo_plugin_world.so&quot;/&gt;
    &lt;/world&gt;
&lt;/sdf&gt;</code></pre><h3 id="launch-파일-만들기">Launch 파일 만들기</h3>
<pre><code>code ~/sim_ws/src/writing_plugins/launch/gazebo_world.launch</code></pre><p>&lt;gazebo_world.launch&gt;</p>
<pre><code>&lt;launch&gt;
    &lt;include file=&quot;$(find gazebo_ros)/launch/empty_world.launch&quot;&gt;
        &lt;arg name=&quot;world_name&quot; value=&quot;$(find writing_plugins)/worlds/gazebo_world.world&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>.
.</p>
<h3 id="결과">결과</h3>
<p>launch 파일을 실행하면 
gazebo에 jersey_barrier가 생긴 것을 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/5bf7d678-5e36-4b52-b74c-78ce4f6f2a39/image.png" alt=""></p>
<h2 id="plugin을-이용해-모델을-특정위치에-배치하기">Plugin을 이용해 모델을 특정위치에 배치하기</h2>
<ul>
<li>world_plugin.cc 파일 생성하고, CMakeList.txt에 필요한 코드 추가하기</li>
<li>gazebo.world도 이에 맞게 수정하기</li>
<li>Plugin을 통해 모델을 특정 위치에 배치하기</li>
</ul>
<p>힌트</p>
<pre><code>msgs::Set(msg.mutable_pose(),
    ignition::math::Pose3d(
        ignition::math::Vector3d(5,5,0),
        ignition::math::Quaterniond(0,0,0))
);
.
</code></pre><p>&lt;world_plugin.cc&gt;</p>
<pre><code>#include &lt;gazebo/gazebo.hh&gt;
#include &lt;gazebo/physics/physics.hh&gt;
#include &lt;iostream&gt;
namespace gazebo{
    class MyGazeboPlugin : public WorldPlugin {
        public:
        MyGazeboPlugin() : WorldPlugin() {
            std::cout &lt;&lt; &quot;Plugin constructor method!&quot; &lt;&lt; std::endl;
        }

        public:
        void Load(physics::WorldPtr _world, sdf::ElementPtr _sdf){
            //set a node
            transport::NodePtr node(new transport::Node());
            node-&gt;Init(_world-&gt;Name());

            //set publisher
            transport::PublisherPtr publisher=node-&gt;Advertise&lt;msgs::Factory&gt;(&quot;~/factory&quot;);

            //create msg obj
            msgs::Factory msg;

            //model to use
            msg.set_sdf_filename(&quot;model://jersey_barrier&quot;);

            msgs::Set(msg.mutable_pose(),
                ignition::math::Pose3d(
                    ignition::math::Vector3d(5,5,0),
                    ignition::math::Quaterniond(0,0,0))
            );
            //send the message
            publisher-&gt;Publish(msg);
        }
    };
    GZ_REGISTER_WORLD_PLUGIN(MyGazeboPlugin)
}</code></pre><h3 id="결과-1">결과</h3>
<ul>
<li>(5,5) 위치에 잘 놓여있는 것을 확인할 수 있다~
<img src="https://velog.velcdn.com/images/yujin-shim/post/8d808871-af0f-4416-8e1e-032b13ecee13/image.png" alt=""></li>
</ul>
<p>.
.</p>
<h1 id="model-plugin">Model Plugin</h1>
<h2 id="모델-움직이기">모델 움직이기</h2>
<ul>
<li>world plugin과 다른 class를 상속해주면 model plugin을 만들 수 있다.</li>
</ul>
<pre><code>code ~/sim_ws/src/writing_plugins/worlds/model.world
code ~/sim_ws/src/writing_plugins/src/my_model_plugin.cc
code ~/sim_ws/src/writing_plugins/launch/model.launch</code></pre><h3 id="모델을-움직이는-plugincc-만들기">모델을 움직이는 plugin.cc 만들기</h3>
<p>&lt;my_model_plugin.cc&gt;</p>
<pre><code>#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 MyModelPlugin : public ModelPlugin {
        public:
        void Load(physics::ModelPtr _parent, sdf::ElementPtr) {
            // Store the pointer to the model
            this-&gt;model = _parent;

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

        // Called by the world update start event
        public:
        void OnUpdate() {
            // Apply a small linear velocity to the model.
            if (this-&gt;counter &lt; 10000) {
                this-&gt;model-&gt;SetLinearVel(ignition::math::Vector3d(0, 0, 0.4));
            }

            this-&gt;counter++;
        }

        // Pointer to the model
        private:
        physics::ModelPtr model;

        private:
        int counter;

        // Pointer to the update event connection
        private:
        event::ConnectionPtr updateConnection;
    };

    // Register this plugin with the simulator
    GZ_REGISTER_MODEL_PLUGIN(MyModelPlugin)
} // namespace gazebo</code></pre><p>OnUpdate : 반복 주기에 의한 callback 함수</p>
<p>모델이 위쪽으로 움직이게 되는 이유는 setLinearVel에서 Z 축 방향으로 0.4만큼의 velocity를 계속 인가하고 있기 때문, 관련링크 : <a href="https://osrf-distributions.s3.amazonaws.com/gazebo/api/dev/classgazebo_1_1physics_1_1Model.html#aa75eba1cc9a826a83588a80512ec2984">setLinearVel</a></p>
<ul>
<li>API에 대한 내용은 홈페이지를 참고하자 : <a href="https://osrf-distributions.s3.amazonaws.com/gazebo/api/dev/classgazebo_1_1physics_1_1Model.html">Gazebo:Model class</a></li>
</ul>
<p>.
.</p>
<h3 id="cmakelisttxt-수정하기-1">CMakeList.txt 수정하기</h3>
<p>&lt;CMakeList.txt&gt; 파일에서 아래 내용을 수정해준 후 빌드를 해주면 된다.</p>
<pre><code>add_library(my_model_plugin SHARED src/my_model_plugin.cc)
target_link_libraries(my_model_plugin ${GAZEBO_LIBRARIES})</code></pre><h3 id="world-파일-만들기-1">.world 파일 만들기</h3>
<p>model plugin의 경우, world 파일에서 불러와줘야 하므로
&lt;model.world&gt; 파일에서 <strong>model 태그 안에</strong> 내용을 수정해줘야 한다.</p>
<pre><code>&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;sdf version=&quot;1.4&quot;&gt;
    &lt;world name=&quot;default&quot;&gt;
        &lt;!-- Ground Plane --&gt;
        &lt;include&gt;
            &lt;uri&gt;model://ground_plane&lt;/uri&gt;
        &lt;/include&gt;

        &lt;include&gt;
            &lt;uri&gt;model://sun&lt;/uri&gt;
        &lt;/include&gt;

        &lt;model name=&quot;box&quot;&gt;
            &lt;pose&gt;0 0 0.5 0 0 0&lt;/pose&gt;
            &lt;link name=&quot;link&quot;&gt;
                &lt;collision name=&quot;collision&quot;&gt;
                    &lt;geometry&gt;
                        &lt;box size=&quot;1.0 1.0 1.0&quot;/&gt;
                    &lt;/geometry&gt;
                &lt;/collision&gt;

                &lt;visual name=&quot;visual&quot;&gt;
                    &lt;geometry&gt;
                        &lt;box size=&quot;1.0 1.0 1.0&quot;/&gt;
                    &lt;/geometry&gt;
                &lt;/visual&gt;
            &lt;/link&gt;

            &lt;!-- import plugin --&gt;
            &lt;plugin name=&quot;my_model_plugin&quot; filename=&quot;libmy_model_plugin.so&quot;/&gt;
        &lt;/model&gt;

    &lt;/world&gt;
&lt;/sdf&gt;</code></pre><p>launch 파일도 생성해준다 ( 앞에서 많이 했으니 생략하겠다. )</p>
<h3 id="결과-2">결과</h3>
<p>정육면체가 위로 올라가는 것을 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/95778178-2c34-4620-915b-ee7837e99c96/image.gif" alt=""></p>
<p>.
.</p>
<h2 id="모델을-움직이게-하는-plugin">모델을 움직이게 하는 Plugin</h2>
<ul>
<li><p>목표 : 모델을 움직이게 하는 Plugin (a_model_plugin.cc)</p>
</li>
<li><p>Mission</p>
<ul>
<li>모델이 반복하며 정사각형 패턴으로 움직이도록 프로그래밍</li>
<li>OnUpdate() callback 함수를 다시 활용</li>
</ul>
</li>
<li><p>Load() 매서드 안에 hint 코드를 활용하기 (반복횟수 &quot;iterations&quot;와 선형속도 &quot;linear_vel&quot;을 파라미터로 활용)</p>
</li>
<li><p>설정한 파라미터는 model.world에서 설정할 수 있다!</p>
</li>
</ul>
<p>&lt;hint code - load() - .cc 코드에 넣기&gt;</p>
<pre><code>this -&gt; iterations = 10 * 1000;
if (_sdf-&gt;HasElement(&quot;iterations&quot;)){
    this-&gt;iterations=_sdf-&gt;Get&lt;int&gt;(&quot;iterations&quot;);
}</code></pre><p>&lt;model.world&gt;</p>
<pre><code>&lt;plugin name=&quot;a_model_plugin&quot; filename=&quot;liba_model_plugin.so&quot;&gt;
    &lt;linear_vel&gt;0.2&lt;/linear_vel&gt;
    &lt;iterations&gt;15000&lt;/iterations&gt;
&lt;/plugin&gt;</code></pre><p>.
.</p>
<h3 id="cc-파일-만들기-1">.cc 파일 만들기</h3>
<p>포인터가 많이나와서 .. 포인터를 열심히 공부해야겠다 생각했다..</p>
<p>&lt;a_model_plugin.cc&gt;</p>
<pre><code>#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;
#include &lt;ros/ros.h&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.0;
            if (_sdf-&gt;HasElement(&quot;linear_vel&quot;)){
                this-&gt;linear_vel=_sdf-&gt;Get&lt;double&gt;(&quot;linear_vel&quot;);
            }


            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(this-&gt;counter &lt; this-&gt;iterations)
            {
                this-&gt;model-&gt;SetLinearVel(ignition::math::Vector3d(this-&gt;linear_vel,0,0));
            }
            else if(this-&gt;counter &lt; 2 * this-&gt;iterations)
            {
                this-&gt;model-&gt;SetLinearVel(ignition::math::Vector3d(0,this-&gt;linear_vel,0));
            } 
            else if(this-&gt;counter &lt; 3 * this-&gt;iterations)
            {
                this-&gt;model-&gt;SetLinearVel(ignition::math::Vector3d(-this-&gt;linear_vel,0,0));
            }
             else if(this-&gt;counter &lt; 4 * this-&gt;iterations)
            {
                this-&gt;model-&gt;SetLinearVel(ignition::math::Vector3d(0,-this-&gt;linear_vel,0));
            }
            else
            {
                this-&gt;counter = 0;
            }
            this-&gt;counter++;
        }

        private:
        physics::ModelPtr model;

        private:
        int counter;
        double linear_vel;
        int iterations;

        // Pointer to the update event connection
        private:
        event::ConnectionPtr updateConnection;
    };

    // register this plugin with the simulation
    GZ_REGISTER_MODEL_PLUGIN(AModelPlugin)
}</code></pre><h3 id="cmakelisttxt-수정하기-2">CMakeList.txt 수정하기</h3>
<p>아래 내용을 추가해주고~</p>
<pre><code>add_library(a_model_plugin SHARED src/a_model_plugin.cc)
target_link_libraries(a_model_plugin ${GAZEBO_LIBRARIES})</code></pre><h3 id="world-만들기">.world 만들기</h3>
<pre><code>&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;sdf version=&quot;1.4&quot;&gt;
    &lt;world name=&quot;default&quot;&gt;
        &lt;!-- Ground Plane --&gt;
        &lt;include&gt;
            &lt;uri&gt;model://ground_plane&lt;/uri&gt;
        &lt;/include&gt;

        &lt;include&gt;
            &lt;uri&gt;model://sun&lt;/uri&gt;
        &lt;/include&gt;

        &lt;model name=&quot;a_barrier&quot;&gt;
            &lt;pose&gt;1 1 0 0 0 0&lt;/pose&gt;
            &lt;static&gt;false&lt;/static&gt;
            &lt;include&gt;
                &lt;uri&gt;model://suv&lt;/uri&gt;
            &lt;/include&gt;

            &lt;!-- import plugin --&gt;
            &lt;plugin name=&quot;a_model_plugin&quot; filename=&quot;liba_model_plugin.so&quot;&gt;
                &lt;linear_vel&gt;5.0&lt;/linear_vel&gt;
                &lt;iterations&gt;5000&lt;/iterations&gt;
            &lt;/plugin&gt;
        &lt;/model&gt;

    &lt;/world&gt;
&lt;/sdf&gt;</code></pre><p>launch 파일은 어렵지 않으니 생략하겠다...</p>
<h3 id="결과-3">결과</h3>
<p>사각형패턴으로 움직이는 것을 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/671502e5-1f23-420e-9b85-43ba0658eae9/image.gif" alt=""></p>
<h1 id="마치며">마치며</h1>
<p>정말 분량이 많아서 오래걸리고 힘들었지만.....
그래도 ... 반복해서 하다보니 plugin을 만드는 flow를 이해하는데 도움이 되었다!!
c++ 공부에 더 매진해야겠다.</p>
<p>난 이제 지쳤어요 땡벌땡벌
<img src="https://velog.velcdn.com/images/yujin-shim/post/900cca61-449a-4b98-ae05-80f2840196da/image.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Simulation] 나의 world에서 키보드로 로봇 움직이기]]></title>
            <link>https://velog.io/@yujin-shim/Simulation-%EB%82%98%EC%9D%98-world%EC%97%90%EC%84%9C-%ED%82%A4%EB%B3%B4%EB%93%9C%EB%A1%9C-%EB%A1%9C%EB%B4%87-%EC%9B%80%EC%A7%81%EC%9D%B4%EA%B8%B0</link>
            <guid>https://velog.io/@yujin-shim/Simulation-%EB%82%98%EC%9D%98-world%EC%97%90%EC%84%9C-%ED%82%A4%EB%B3%B4%EB%93%9C%EB%A1%9C-%EB%A1%9C%EB%B4%87-%EC%9B%80%EC%A7%81%EC%9D%B4%EA%B8%B0</guid>
            <pubDate>Thu, 22 Jun 2023 11:58:43 GMT</pubDate>
            <description><![CDATA[<h1 id="목표">목표</h1>
<ul>
<li>나만의 World 만들기</li>
<li>다양한 모델과 population을 이용하기</li>
<li>world를 열기 위한 launch 파일 작성하고 실행시키기</li>
<li>로봇 spawn하여 teleop_twist_keyboard로 주행해보기</li>
</ul>
<p>.
.</p>
<h2 id="step1-나만의-world-만들기">step1. 나만의 world 만들기</h2>
<ul>
<li>나의 world concept: 학교!
<img src="https://velog.velcdn.com/images/yujin-shim/post/71bf06e9-f8de-417d-a06e-ba6e0cfef866/image.png" alt=""></li>
</ul>
<p>.
.
&lt;.world 파일&gt;</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;include&gt;
            &lt;uri&gt;model://school&lt;/uri&gt;
            &lt;static&gt;true&lt;/static&gt;
            &lt;pose&gt;-20 -10 0.3 0 0 3.14 &lt;/pose&gt;
        &lt;/include&gt;
        &lt;include&gt;
            &lt;uri&gt;model://playground&lt;/uri&gt;
            &lt;static&gt;true&lt;/static&gt;
            &lt;pose&gt;10 10 0.3 0 0 3.14 &lt;/pose&gt;
        &lt;/include&gt;


        &lt;!-- +X barriers --&gt;
        &lt;population name=&quot;pine_tree_population&quot;&gt;
            &lt;model name=&quot;pine_tree&quot;&gt;
                &lt;include&gt;
                    &lt;static&gt;true&lt;/static&gt;
                    &lt;uri&gt;model://pine_tree&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;20&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;pine_tree_population&quot;&gt;
            &lt;model name=&quot;pine_tree_2&quot;&gt;
                &lt;include&gt;
                    &lt;static&gt;true&lt;/static&gt;
                    &lt;uri&gt;model://pine_tree&lt;/uri&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;10 5 0.3 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;10 20 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;pine_tree_population&quot;&gt;
            &lt;model name=&quot;pine_tree_3&quot;&gt;
                &lt;include&gt;
                    &lt;static&gt;true&lt;/static&gt;
                    &lt;uri&gt;model://pine_tree&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 10 0.3 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;linear-y&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;

        &lt;!-- random models population --&gt;
        &lt;population name=&quot;person_walking_population&quot;&gt;
            &lt;model name=&quot;person_walking&quot;&gt;
                &lt;include&gt;
                &lt;static&gt;false&lt;/static&gt;
                &lt;uri&gt;model://person_walking&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;5&lt;/model_count&gt;
            &lt;distribution&gt;
                &lt;type&gt;random&lt;/type&gt;
            &lt;/distribution&gt;
        &lt;/population&gt;

            &lt;!-- random models population --&gt;
        &lt;population name=&quot;oak_tree_population&quot;&gt;
            &lt;model name=&quot;oak_tree&quot;&gt;
                &lt;include&gt;
                &lt;static&gt;true&lt;/static&gt;
                &lt;uri&gt;model://oak_tree&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;60 60 0.01&lt;/size&gt;
            &lt;/box&gt;
            &lt;model_count&gt;5&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><p>.
.</p>
<h2 id="step2-robot-spawn">step2. Robot spawn</h2>
<pre><code>roslaunch robot_description spawn.launch</code></pre><p>로봇 배치! - 이건 그전 포스팅 로봇만들기 편 참고</p>
<h2 id="step3-robot-keyboard-control">step3. Robot keyboard control</h2>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/aec10b9e-cf7f-4659-9444-e6ec4cc74347/image.gif" alt=""></p>
<p>방법은..</p>
<pre><code>rosrun teleop_twist_keyboard teleop_twist_keyboard.py</code></pre><blockquote>
<p>package가 없다는 에러가 난다면 설치가 안되어서 그럴 수 있으므로
terminal에서 설치를 해주면 된다.
이때 자신의 ros version에 맞게 melodic을 바꿔줘야한다.
melodic이면 아래처럼 적고 noetic이면 ros-noetic-teleop-twist-keyboard 이런식으로</p>
</blockquote>
<pre><code>sudo apt-get install ros-melodic-teleop-twist-keyboard</code></pre><p>이제 키보드를 눌러 만든 world에서 자유롭게 이동하는 로봇을 구경하면 된다!
.
.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Simulation] World & 모델 만들기]]></title>
            <link>https://velog.io/@yujin-shim/Simulation-World-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@yujin-shim/Simulation-World-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Thu, 22 Jun 2023 09:29:30 GMT</pubDate>
            <description><![CDATA[<h1 id="world-만들기">World 만들기</h1>
<h2 id="패키지-생성">패키지 생성</h2>
<pre><code>cd ~/sim_ws/src
catkin_create_pkg studying_gazebo rospy urdf
cd ~/sim_ws
catkin build
gazebo (안되면 roslaunch robot_description empty_world.launch)</code></pre><h2 id="gui-상에서-insert하는-방법">GUI 상에서 insert하는 방법</h2>
<blockquote>
<p>UI 상에서 Map을 만드는 것이 가장 쉽다.
Insert 탭에서 다양한 것들을 추가할 수 있다.
save를 통해 world를 저장할 수 있다.</p>
</blockquote>
<h2 id="world-파일에-직접-추가하는-방법">.world 파일에 직접 추가하는 방법</h2>
<p>.world 파일에 직접 추가하는 방법도 있다.</p>
<pre><code>code /usr/share/gazebo-9/worlds/empty.world</code></pre><p>gazebo 버전이 달라 위처럼 쳐서 안될 수도 있다.
그럴때는 empty_world.launch 파일을 켜서 insert 탭에 들어가면 경로를 확인해볼 수 있다.
(* insert 탭에 경로는 사전 설정되어있음)
<img src="https://velog.velcdn.com/images/yujin-shim/post/9f28f140-3401-4364-87f2-7bda1b2ed973/image.png" alt=""></p>
<p>.
.</p>
<p>&lt;empty.world&gt; 파일에 직접 추가하기</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;!--add ambulance and construction_barrel--&gt;
    &lt;include&gt;
        &lt;pose&gt;-6.0 -1.0 0 0 -0 0 &lt;/pose&gt;
        &lt;url&gt;model://ambulance&lt;/url&gt;
    &lt;/include&gt;

    &lt;include&gt;
        &lt;pose&gt;-5.5 -5.5 0 0 -0 0 &lt;/pose&gt;
        &lt;url&gt;model://construction_barrel&lt;/url&gt;
    &lt;/include&gt;
  &lt;/world&gt;
&lt;/sdf&gt;
</code></pre><ul>
<li>아래서 한 git clone을 안했다면 모델이 없어서 실행이 안될 수 있음.</li>
</ul>
<p>.
.
.</p>
<blockquote>
<p>Gazebo 빨리 끄는 법 😊</p>
<blockquote>
<p>cd
gedit ~/.bashrc
(.bashrc 파일이 열리면 맨 아래 alias를 추가해주자)
alias killgzb=&#39;killall -9 gazebo &amp; killall -9 gzserver &amp; killall -9 gzclient&#39;
이후 부터는 terminal에 killgzb만 치면 빠르게 gazebo를 종료할 수 있다.</p>
</blockquote>
</blockquote>
<p>/</p>
<h1 id="gazebo-models">Gazebo Models</h1>
<h2 id="gazebo-models-database">Gazebo Models Database</h2>
<p>모델 데이터베이스 Github : <a href="http://github.com/osrf/gazebo_models">github.com/osrf/gazebo_models</a></p>
<pre><code>cd 
cd ./download
git clone http://github.com/osrf/gazebo_models</code></pre><p>다운로드 폴더에 위 git를 clone한 후 /.gazebo/models 폴더에 model 파일들을 옮겨준다.
(+ &lt;ctrl+h&gt;를 누르면 히든 폴더들을 볼 수 있다.)
(+ models 폴더가 없다면 당황하지 말고 그냥 만들면 된다. mkdir models)</p>
<p>.
.</p>
<h3 id="models-directory-구조">Models directory 구조</h3>
<ul>
<li><p>model.config (this file includes name, version, description, dependency ...)</p>
</li>
<li><p>model.sdf (this file includes structure, physical informations ...)</p>
<ul>
<li>pose ( X Y Z R P Y) : world에 배치될 때 위치</li>
<li>static (true or false) : true면 외부의 힘에 의해 움직이지 않음</li>
<li>link<ul>
<li>inertial : 질량과 관성 행렬 정의 -&gt; 이를 이용해 object의 물리적반응 계산<ul>
<li>collision : 물리엔진이 외부와의 충돌을 계산할 때 고려하는 치수</li>
</ul>
</li>
<li>visual : 시뮬레이션에서 볼 수 있는 모양을 정의(대체로 collision과 동일)</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>.
.
.
.</p>
<h2 id="빈-world-만들기">빈 world 만들기</h2>
<pre><code>mkdir ~/sim_ws/src/studying_gazebo/launch/
mkdir ~/sim_ws/src/studying_gazebo/worlds/
touch ~/sim_ws/src/studying_gazebo/launch/empty_world.launch
code ~/sim_ws/src/studying_gazebo/launch/empty_world.launch</code></pre><p>&lt;empty_world.launch&gt; 파일</p>
<pre><code>&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>.
.
.</p>
<h2 id="나만의-모델-만들기">나만의 모델 만들기</h2>
<p><img src="https://velog.velcdn.com/images/yujin-shim/post/157a9e01-b3fb-4ff4-9a61-5179366f62cb/image.gif" alt=""></p>
<ul>
<li>간단한 tutorial이므로 material이나 texture는 사용하지 않겠습니당</li>
<li>박스 모델을 만들어 봅시다🔰</li>
</ul>
<pre><code>cd ~/sim_ws/src/studying_gazebo
mkdir -p models/box_model
cd models/box_model
touch model.config
touch model.sdf</code></pre><p>&lt;model.config&gt;</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;yujin&lt;/name&gt;
        &lt;email&gt;aaa@gmail.com&lt;/email&gt;
    &lt;/author&gt;

    &lt;description&gt;
        A simple box model
    &lt;/description&gt;
&lt;/model&gt;</code></pre><p>&lt;model.sdf&gt;</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;/link&gt;
    &lt;/model&gt;
&lt;/sdf&gt;</code></pre><blockquote>
<p>위와 같이 모델을 만들었다면 Gazebo에서 불러올 수 있도록 경로 설정을 해줘야 한다.
Gazebo insert 탭에 들어가서 add path에 우리가 만든 폴더를 추가해주면 된다.</p>
</blockquote>
<pre><code>roslaunch robot_description empty_world.launch</code></pre><p><img src="https://velog.velcdn.com/images/yujin-shim/post/9f28f140-3401-4364-87f2-7bda1b2ed973/image.png" alt=""></p>
<h3 id="mission1">Mission(1)</h3>
<p>위에서 만든 box model을 수정하여
visual은 설정되어 있지만 collision은 막혀있는 모델 만들기
2개의 visual 태그 사용</p>
<p>collision을 설정하지 않으면 물리엔진상의 계산을 받지 않게 된다.
정확히 뭐가 달라지는 건지 직접 해보자!</p>
<p>model.sdf파일에 collision을 설정하지 않은 원통을 하나 만들어 박스위에 둬보자보자</p>
<p>&lt;model.sdf&gt;</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>Gazebo UI 상에서 view &gt;&gt; collision을 선택하면 collision 영역이 주황색으로 나타난다.
cylinder는 collision을 설정하지 않았으므로 주황색 영역이 없다.</p>
<blockquote>
<p>visual2 인 cylinder는 collision을 설정해주지 않았다. 
따라서 보이는 영상처럼
충돌의 상황에서 계산에 사용될 요소를 설정해주지 않은 것이므로 cylinder는 관통하고 box에서는 collision이 일어나는 것을 확인할 수 있다.
<img src="https://velog.velcdn.com/images/yujin-shim/post/2da8d5e6-5ef0-43ac-96ea-91dac4682611/image.gif" alt=""></p>
</blockquote>
<p>.
.
.</p>
<h2 id="digital-elevation-models">Digital Elevation Models</h2>
<p>지표면을 그래픽으로 표현한 모델 - 카메라나 라이다 정보로 생성할 수 있음</p>
<h3 id="dem-world-만들기">DEM world 만들기</h3>
<pre><code>touch ~/sim_ws/src/studying_gazebo/worlds/dem.world
cd ~/sim_ws/src/studying_gazebo/worlds
wget http://github.com/osrf/gazebo_tutorials/raw/master/dem/files/mtsthelens_before.zip
unzip mtsthelens_before.zip
rm mtsthelens_before.zip</code></pre><p>&lt;dem.world&gt;</p>
<pre><code>&lt;?xml version=&quot;1.0&quot; ?&gt;
&lt;sdf version=&quot;1.4&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;model name=&quot;heightmap&quot;&gt;
        &lt;static&gt;true&lt;/static&gt;
        &lt;link name=&quot;link&quot;&gt;
            &lt;collision name=&quot;collision&quot;&gt;
            &lt;geometry&gt;
                &lt;heightmap&gt;
                &lt;uri&gt;/home/yujin/sim_ws/src/studying_gazebo/worlds/30.1.1.1282760.dem&lt;/uri&gt;
                &lt;size&gt;75 75 25&lt;/size&gt;
                &lt;pos&gt;0 0 0&lt;/pos&gt;
                &lt;/heightmap&gt;
            &lt;/geometry&gt;
            &lt;/collision&gt;

            &lt;visual name=&quot;visual_abcedf&quot;&gt;
            &lt;geometry&gt;
                &lt;heightmap&gt;
                &lt;texture&gt;
                    &lt;diffuse&gt;file://media/materials/textures/dirt_diffusespecular.png&lt;/diffuse&gt;
                    &lt;normal&gt;file://media/materials/textures/flat_normal.png&lt;/normal&gt;
                    &lt;size&gt;1&lt;/size&gt;
                &lt;/texture&gt;
                &lt;texture&gt;
                    &lt;diffuse&gt;file://media/materials/textures/grass_diffusespecular.png&lt;/diffuse&gt;
                    &lt;normal&gt;file://media/materials/textures/flat_normal.png&lt;/normal&gt;
                    &lt;size&gt;1&lt;/size&gt;
                &lt;/texture&gt;
                &lt;texture&gt;
                    &lt;diffuse&gt;file://media/materials/textures/fungus_diffusespecular.png&lt;/diffuse&gt;
                    &lt;normal&gt;file://media/materials/textures/flat_normal.png&lt;/normal&gt;
                    &lt;size&gt;1&lt;/size&gt;
                &lt;/texture&gt;
                &lt;blend&gt;
                    &lt;min_height&gt;2&lt;/min_height&gt;
                    &lt;fade_dist&gt;5&lt;/fade_dist&gt;
                &lt;/blend&gt;
                &lt;blend&gt;
                    &lt;min_height&gt;4&lt;/min_height&gt;
                    &lt;fade_dist&gt;5&lt;/fade_dist&gt;
                &lt;/blend&gt;
                &lt;uri&gt;/home/shin/sim_ws/src/studying_gazebo/worlds/30.1.1.1282760.dem&lt;/uri&gt;
                &lt;size&gt;75 75 25&lt;/size&gt;
                &lt;pos&gt;0 0 0&lt;/pos&gt;
                &lt;/heightmap&gt;
            &lt;/geometry&gt;
            &lt;/visual&gt;

        &lt;/link&gt;
        &lt;/model&gt;

    &lt;/world&gt;
&lt;/sdf&gt;</code></pre><p>&lt;dem.launch&gt;</p>
<pre><code>&lt;launch&gt;
    &lt;include file=&quot;$(find gazebo_ros)/launch/empty_world.launch&quot;&gt;
        &lt;arg name=&quot;world_name&quot; value=&quot;$(find studying_gazebo)/worlds/dem.world&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>.
.
.</p>
<h2 id="model-population">Model population</h2>
<p>-&gt; object를 무작위로 / 지정된 패턴으로 배치할 수 있다. (하나씩 배치할 필요 없음)
.</p>
<h3 id="population-기본-구문">Population 기본 구문</h3>
<pre><code>&lt;population name=&quot;&quot;&gt; - population 이름 설정
    &lt;model&gt;
        &lt;include&gt; - 다른 리소스나 파일 include
        &lt;/include&gt;
    &lt;/model&gt;
    &lt;pose&gt;&lt;/pose&gt; - 시뮬레이션에서 object 초기 위치 
    &lt;box&gt;&lt;/box&gt; - object가 배치될 영역 설정 (box or cylinder)
    &lt;model_count&gt;&lt;/model_count&gt; - object를 몇개 둘지
    &lt;distribution&gt; - 배치방법 결정
    distribution : random, uniform, grid, linear-x, linear-y, linear-z
        &lt;type&gt;&lt;/type&gt;
    &lt;/distribution&gt;
&lt;/population&gt;</code></pre><p>.</p>
<h3 id="world를-만들어주자">world를 만들어주자</h3>
<pre><code>touch ~/sim_ws/src/studying_gazebo/worlds/population.world
touch ~/sim_ws/src/studying_gazebo/launch/population.launch</code></pre><p>&lt;population.world&gt;</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;!-- Testing the automatic population of objects --&gt;
        &lt;population name=&quot;barriers_population&quot;&gt;
            &lt;model name=&quot;jersey_barrier_1&quot;&gt;
                &lt;include&gt;
                &lt;static&gt;false&lt;/static&gt;
                &lt;uri&gt;model://jersey_barrier&lt;/uri&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;0 0 0.3 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;12 12 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;!-- Testing the automatic population of objects --&gt;
        &lt;population name=&quot;bookshelf_population&quot;&gt;
            &lt;model name=&quot;bookshelf&quot;&gt;
                &lt;include&gt;
                &lt;static&gt;false&lt;/static&gt;
                &lt;uri&gt;model://bookshelf&lt;/uri&gt;
                &lt;/include&gt;
            &lt;/model&gt;
            &lt;pose&gt;0 8 0.3 0 0 0&lt;/pose&gt;
            &lt;box&gt;
                &lt;size&gt;6 6 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;/world&gt;
&lt;/sdf&gt;</code></pre><p>.</p>
<blockquote>
<p>population 태그를 보면 bookshelf에서 model_count가 4, distribution이 linear-y이다.
선형 y축으로 bookshelf가 4개 생성된다는 뜻이다.
.
distribution : random, uniform, grid, linear-x, linear-y, linear-z
.
.</p>
</blockquote>
<p>&lt;population.launch&gt;</p>
<pre><code>&lt;launch&gt;
    &lt;include file=&quot;$(find gazebo_ros)/launch/empty_world.launch&quot;&gt;
        &lt;arg name=&quot;world_name&quot; value=&quot;$(find studying_gazebo)/worlds/population.world&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>.
.</p>
<blockquote>
<p>launch 파일을 실행하면 아래와 같이
y축으로 정렬된 bookshelf와 x축으로 정렬된 barrier가 나온다.
<img src="https://velog.velcdn.com/images/yujin-shim/post/caff5497-9bec-4ba0-9b2c-1b5483414746/image.png" alt=""></p>
</blockquote>
<p>.
.
.</p>
<h3 id="mission3">Mission(3)</h3>
<ul>
<li>40m X 40m의 벽으로 둘러싸기</li>
<li>벽돌안에 constriction_barrel 모델을 무작위로 배치</li>
<li>construction_barrel의 배치는 30mx30m 공간으로 제한</li>
</ul>
<p>&lt;population.world&gt;</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><blockquote>
<p>미션 결과이다!
<img src="https://velog.velcdn.com/images/yujin-shim/post/4017db8a-b94d-4a4f-97cb-abc7c474c653/image.png" alt=""></p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>