<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="devanshdhrafani.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="devanshdhrafani.github.io/" rel="alternate" type="text/html" /><updated>2024-10-10T22:51:46-04:00</updated><id>devanshdhrafani.github.io/feed.xml</id><title type="html">Devansh Dhrafani</title><subtitle>Roboticist
</subtitle><author><name>Devansh Dhrafani</name><email>devansh@cmu.edu</email></author><entry><title type="html">Setup ROS2 in a Docker Container</title><link href="devanshdhrafani.github.io/posts/2021-04-15-dockerros2/" rel="alternate" type="text/html" title="Setup ROS2 in a Docker Container" /><published>2021-04-15T00:00:00-04:00</published><updated>2021-04-15T00:00:00-04:00</updated><id>devanshdhrafani.github.io/posts/dockerros2</id><content type="html" xml:base="devanshdhrafani.github.io/posts/2021-04-15-dockerros2/"><![CDATA[<h2 id="why-docker">Why Docker?</h2>

<p>If you have ever tried building a third party ROS package, you know the time and effort it takes to setup the required dependencies. Often times, the package you are trying to install will be compatible with only specific versions of its dependencies. Say for example you are using a deep learning framework like TensorFlow/PyTorch. These frameworks require specific version of CUDA packages to run correctly. If you already have an incompatible version of CUDA installed, your TensorFlow installation may throw errors. Downgrading/upgrading the CUDA version has its own set of associated problems. On top of this, if you are working on a collaborative project, the version of TensorFlow and CUDA that you have might not be the same as what your colleague is using. This may result in the classic problem of software development: “It works on my machine”.</p>

<p><img src="/assets/images/blog/dockerros2/worksonmymachine.png?style=centerme" alt="It Works on My Machine" class="image--xxl" /></p>

<p>This is where Docker comes in. Docker is like a virtual machine, but smarter. A VM runs an entire OS within an OS, taking up precious computing resources. Docker is much smarter. A Docker VM, called a ‘Container’, only runs a stripped down version of an OS. This bare-bones version of any OS only has the minimum required packages to run whatever task you are trying to accomplish. Docker containers also utilize the full memory and processing power of your system, making them much faster than traditional VMs. The benefits of using Docker containers for software development are endless and require their own separate article to do proper justice. For now, I will explain how using Docker simplifies our ROS2 installation.</p>

<p><img src="/assets/images/blog/dockerros2/dockervsvm.png?style=centerme" alt="Docker vs VM" class="image--xxxl" /></p>

<p>Every ROS version requires a specific Ubuntu version to run. For example, ROS Melodic is made for Ubuntu 18.04. Installing Melodic on Ubuntu 20.04 will undoubtedly cause problems. The current Long-Term-Supported (LTS) version of ROS2 is called Foxy Fitzroy. ROS2 Foxy is made for Ubuntu 20.04. So for installing ROS2 on your system, you will need Ubuntu 20.04. But as Melodic is the current LTS for ROS1, there is a high chance that you, like me, are using Ubuntu 18.04. Even if you are using Ubuntu 20.04, there is a chance that you might have ROS1 Noetic installed on your system. In all these cases, installing ROS2 will cause numerous problems, be it due to incompatible OS version, or simply due to conflicts between ROS1 and ROS2 packages. To avoid this, we can install and run ROS2 in an Ubuntu 20.04 Docker container.</p>

<h2 id="docker-containers-images-and-dockerfiles">Docker Containers, Images and Dockerfiles</h2>

<p>The simplest way to understand Containers and Images is via an Object Oriented Programing (OOP) analogy. A Docker image is like a class and a Docker Container is an object (or instantiation) of the image. A <strong>Docker Image</strong> contains everything needed to run a container: code, libraries, environment variables, configuration files, etc. It serves as a blueprint which can be used to create an instance, ie, a <strong>Docker Container</strong>. Once a Docker Container is created, you can tinker with it as much as you like, and it won’t affect the image from which it was built.</p>

<p><img src="/assets/images/blog/dockerros2/imagecontainerfile.png?style=centerme" alt="Dockerfiles, Images and Containers" class="image--xxxl" /></p>

<p>You can find prebuilt Docker Images for many different applications on the DockerHub<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>, which uses a GitHub like cloud solution where you can pull images to your local computer. These prebuilt images have relevant libraries, environment variables, etc. already setup so you can simply create a Container from the Image and get started on your work.</p>

<p>If you can’t find a suitable image for your use case on DockerHub, you can create your own Docker Image using a Dockerfile. A <strong>Dockerfile</strong> is a set of instructions to build a Docker Image. You can learn more about the syntax and standard practices of writing a Dockerfile from the documentation<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. For the purposes of this guide, I will explain the commands that I used as we go.</p>

<p class="info">The Open Source Robotics Foundation(OSRF) has hosted ROS Docker Images on the DockerHub. While having the knowledge of Dockerfiles and building your own images is helpful, if you are in a hurry, you can pull the ROS2 image from DockerHub <a href="https://hub.docker.com/r/osrf/ros2/" target="_blank">here</a>.</p>

<h2 id="building-the-ros2-image">Building the ROS2 Image</h2>

<p>ROS2 Foxy Fitzroy is made to run on Ubuntu 20.04. So in the first line, we download the Ubuntu 20.04 Docker Image:</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> ubuntu:20.04</span>
</code></pre></div></div>

<p>If you don’t have the image locally, Docker is smart enough to pull it from the official Ubuntu Images on DockerHub.</p>

<p>The instructions that follow are for the ROS2 installation on top of Ubuntu 20.04. My approach to this was modifying the existing set of instructions given in the ROS2 documentation<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>.</p>

<h3 id="set-locale">Set locale</h3>

<p>This can be done easily in Docker Images using:</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">ENV</span><span class="s"> LANG C.UTF-8</span>
<span class="k">ENV</span><span class="s"> LC_ALL C.UTF-8</span>
</code></pre></div></div>

<p>The ENV command is used, as the name suggests, to set the environment variables.</p>

<h3 id="setup-sources">Setup Sources</h3>

<p>Next, we need to setup the sources so that the system knows where to look for ROS2 packages. The RUN command essentially lets us execute terminal commands.</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">RUN </span>apt update <span class="o">&amp;&amp;</span> apt <span class="nb">install</span> <span class="nt">-y</span> curl gnupg2 lsb-release
<span class="k">RUN </span>curl <span class="nt">-sSL</span> https://raw.githubusercontent.com/ros/rosdistro/master/ros.key  <span class="nt">-o</span> /usr/share/keyrings/ros-archive-keyring.gpg
</code></pre></div></div>

<p>Notice that there is no sudo before apt. This is because by default, you are already logged in as root in docker. The -y was added to bypass the prompt by apt, which asks the user if they want to continue the installation or not.</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">RUN </span><span class="nb">echo</span> <span class="s2">"deb [arch=</span><span class="si">$(</span>dpkg <span class="nt">--print-architecture</span><span class="si">)</span><span class="s2"> signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu </span><span class="si">$(</span>lsb_release <span class="nt">-cs</span><span class="si">)</span><span class="s2"> main"</span> | <span class="nb">tee</span> /etc/apt/sources.list.d/ros2.list <span class="o">&gt;</span> /dev/null
</code></pre></div></div>

<h3 id="install-ros2-packages">Install ROS2 packages</h3>

<p>After updating the existing packages, we are ready to install ROS2 foxy. This is as simple as:</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">RUN </span>apt update
<span class="k">RUN </span><span class="nv">DEBIAN_FRONTEND</span><span class="o">=</span>noninteractive apt <span class="nb">install</span> <span class="nt">-y</span> ros-foxy-desktop
</code></pre></div></div>

<p>The ROS installation asks for configuration prompts in between the installation. Especially for selecting the timezone and keyboard language. Using <code class="language-plaintext highlighter-rouge">DEBIAN_FRONTEND=noninteractive</code> allows to bypass this step by letting the installer pick the defaults. And -y as before automatically switches answers to yes when prompted.</p>

<h3 id="setting-up-a-workspace">Setting up a workspace</h3>

<p>While the above steps are enough to build a Docker Image with ROS2, it’s nice to add the following lines for convenience. The first thing that you might end up doing after a ROS installation is setting up a workspace. While this is a fairly easy task, it’s always nice to automate even that, so you can get started right away.</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">WORKDIR</span><span class="s"> /root/dev_ws/src</span>
<span class="k">RUN </span>git clone https://github.com/ros/ros_tutorials.git <span class="nt">-b</span> foxy-devel
<span class="k">WORKDIR</span><span class="s"> /root/dev_ws</span>
</code></pre></div></div>

<p>The above lines make a folder called <code class="language-plaintext highlighter-rouge">dev_ws</code> which will serve as the colcon workspace. And also clone the ROS2 tutorials for beginners. The WORKDIR command, as the name suggests, changes the working directory of the terminal that the Docker Container is running in. Docker is smart enough that if such a directory doesn’t exist, it will make one without prompting the user.</p>

<p>Finally, we install rosdep and colcon. Both are essential packages for ROS2. <strong>Rosdep</strong> helps resolves errors associated with missing dependencies. <strong>Colcon</strong> is the next iteration of catkin_make and other tools that were used for building packages in ROS1.</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">RUN </span>apt-get <span class="nb">install </span>python3-rosdep <span class="nt">-y</span>
<span class="k">RUN </span>rosdep init
<span class="k">RUN </span>rosdep update
<span class="k">RUN </span>rosdep <span class="nb">install</span> <span class="nt">-i</span> <span class="nt">--from-path</span> src <span class="nt">--rosdistro</span> foxy <span class="nt">-y</span>
<span class="k">RUN </span>apt <span class="nb">install </span>python3-colcon-common-extensions <span class="nt">-y</span>
</code></pre></div></div>

<h3 id="entrypoint-for-ros2">Entrypoint for ROS2</h3>

<p>For using ROS commands in any new terminal, we need to source the <code class="language-plaintext highlighter-rouge">setup.bash</code> file. To avoid the hassle of sourcing the file in every terminal, we normally add a script to the <code class="language-plaintext highlighter-rouge">.bashrc</code> file. A similar thing can be done for Docker Containers.</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">COPY</span><span class="s"> ros2_entrypoint.sh /root/.</span>
<span class="k">ENTRYPOINT</span><span class="s"> ["/root/ros2_entrypoint.sh"]</span>
<span class="k">CMD</span><span class="s"> ["bash"]</span>
</code></pre></div></div>

<p>The COPY command copies the entrypoint shell script that I wrote to the root of the Docker Container. This script will source the <code class="language-plaintext highlighter-rouge">setup.bash</code> file. The command ENTRYPOINT allows us to set the script which will run each time a container is started. Finally the CMD  command is used to set bash as the default executable when the container is first started.</p>

<h2 id="conclusion">Conclusion</h2>

<p>This brings us to the end of this little guide. While they might seem tricky to setup, using Docker Containers for software development saves a lot of hassle in the longer run. It gives the developer a peace of mind, knowing that even if he/she messes up the OS, it won’t damage their own system, as the OS is running inside a Container. I hope this guide helped you understand and appreciate how simple it is to setup your own Docker Image/Container. You can check out the code for my Dockerfile. The Open Source Robotics Foundation also has their own ROS Docker Images<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup>, which are regularly updated on the DockerHub. If you want to save some time and are content with the default dependencies that the OSRF Docker Images have, you can always pull the image of your choice from DockerHub.</p>

<hr />

<h4 id="image-sources">Image Sources:</h4>
<ul>
  <li><strong>It works on my computer comic</strong> - <a href="https://donthitsave.com/comic/2016/07/15/it-works-on-my-computer" target="_blank">Jeff Lofvers - Don’t Hit Save</a></li>
  <li><strong>Docker Container &amp; Traditional VM</strong> - <a href="https://codingthesmartway.com/docker-beginners-guide-part-1-images-containers/" target="_blank">Sebastian Eschweiler - Coding The Smart Way</a></li>
  <li><strong>Dockerfile, Image and Container</strong> - <a href="https://medium.com/swlh/understand-dockerfile-dd11746ed183" target="_blank">Rocky Chen - Medium</a></li>
</ul>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p><a href="https://hub.docker.com/" target="_blank">DockerHub</a> <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p><a href="https://docs.docker.com/develop/develop-images/dockerfile_best-practices/" target="_blank">Dockerfile best practices</a> <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p><a href="https://docs.ros.org/en/foxy/Installation/Ubuntu-Install-Debians.html" target="_blank">ROS2 Installation</a> <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p><a href="https://hub.docker.com/r/osrf/ros2/" target="_blank">DockerHub - OSRF ROS2</a> <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Devansh Dhrafani</name></author><category term="ROS2" /><category term="Docker" /><summary type="html"><![CDATA[If you have ever tried building a third party ROS package, you know the time and effort it takes to setup the required dependencies. Often times, the package you are trying to install will be compatible with only specific versions of its dependencies. Running your ROS Installation inside a Docker Container can help solve many of these problems.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devanshdhrafani.github.io/assets/images/blog/dockerros2/imagecontainerfile.png" /><media:content medium="image" url="https://devanshdhrafani.github.io/assets/images/blog/dockerros2/imagecontainerfile.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">SLAM with MIT-Racecar (Gazebo)</title><link href="devanshdhrafani.github.io/posts/2021-02-08-racecar1/" rel="alternate" type="text/html" title="SLAM with MIT-Racecar (Gazebo)" /><published>2021-02-08T00:00:00-05:00</published><updated>2021-02-08T00:00:00-05:00</updated><id>devanshdhrafani.github.io/posts/racecar1</id><content type="html" xml:base="devanshdhrafani.github.io/posts/2021-02-08-racecar1/"><![CDATA[<p>For a recent project, I had to map an environment using the <strong>MIT-Racecar</strong> robot in Gazebo. This is a log of my progress implementing <strong>Simultaneous Localisation and Mapping (SLAM)</strong> on the MIT-Racecar bot. I encountered some interesting problems, and learnt a few things while solving them. In this short post, I will summarise the steps that I used to solve the problems and get <code class="language-plaintext highlighter-rouge">gmapping</code> to run on the <code class="language-plaintext highlighter-rouge">racecar_gazebo</code> simulator.</p>

<p>After cloning the <strong>racecar</strong><sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">1</a></sup> and <strong>racecar_gazebo</strong><sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">2</a></sup> packages to the <code class="language-plaintext highlighter-rouge">/src</code> directory and building them using <code class="language-plaintext highlighter-rouge">catkin_make</code>, I launched the simulation using:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>roslaunch racecar-env racecar_tunnel.launch
</code></pre></div></div>

<p>Of course things never work at the first try. I encountered 2 errors which were fixed by installing the following dependencies:</p>

<ul>
  <li>
    <p><a href="http://wiki.ros.org/ackermann_msgs" target="_blank">ackermann_msgs</a></p>
  </li>
  <li>
    <p><a href="http://wiki.ros.org/effort_controllers" target="_blank">effort_controllers</a></p>
  </li>
</ul>

<p>Now running the same environment launched Gazebo and spawned the racecar in the tunnel map.</p>

<p>To map the environment, I used gmapping. After setting up the launch file <code class="language-plaintext highlighter-rouge">gmapping.launch</code> and running it, rviz showed the warning: <code class="language-plaintext highlighter-rouge">No map received</code>. The terminal showed</p>

<p><code class="language-plaintext highlighter-rouge">[ WARN] [1612766930.932705394, 1106.617000000]: MessageFilter [target=odom ]: Dropped 100.00% of messages so far. Please turn the [ros.gmapping.message_filter] rosconsole logger to DEBUG for more information.</code></p>

<p>Clearly, there was some error with the odometry information. A quick</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>rosrun tf view_frames
</code></pre></div></div>
<p>revealed that the there was no <code class="language-plaintext highlighter-rouge">odom --&gt; base_link</code> transform that is necessary for gmapping to run properly. Refer <strong>REP 105</strong><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">3</a></sup> for the convention.</p>

<p><img src="/assets/images/blog/racecar1/frames1.png" alt="TF" class="border" /></p>

<p>There are 2 ways of fixing this error:</p>

<ol>
  <li>Using the <strong>static transform publisher</strong><sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup>, a transformation can be created from <code class="language-plaintext highlighter-rouge">odom --&gt; base_link</code>. This will work in the case that the robot is fixed with respect to the map. But as I wanted the racecar to explore and map the environment, this approach isn’t viable.</li>
  <li><strong>Dynamically publish</strong> the relationships between <code class="language-plaintext highlighter-rouge">odom</code> and <code class="language-plaintext highlighter-rouge">base_link</code> frame. Generally, this is done using inputs from on-board sensors like encoders and lidar to estimate the robot’s location w.r.t. the map.</li>
</ol>

<p>Point 2 is the ideal approach. But as the robot is running in simulation, a clever shortcut can be used: Instead of using sensor readings, one can use the simulator(gazebo)’s information of the robot pose for odometry. A closer look at the <code class="language-plaintext highlighter-rouge">mit-racecar</code> code revealed that that’s exactly what’s being done in the <code class="language-plaintext highlighter-rouge">gazebo_odometry.py</code> script located in the <code class="language-plaintext highlighter-rouge">racecar_gazebo</code> package.</p>

<p>Executing</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>rosnode info /gazebo_odometry_node
</code></pre></div></div>
<p>revealed that this node is subscribing to the <code class="language-plaintext highlighter-rouge">/gazebo/link_states</code> topic which provides the robot pose information. It’s also publishing to the <code class="language-plaintext highlighter-rouge">/tf</code> topic, which is where the <code class="language-plaintext highlighter-rouge">map--&gt;odom</code> transform is being broadcast. This is in line with what we saw in tf tree above.</p>

<p>So I made a slight modification to the <code class="language-plaintext highlighter-rouge">gazebo_odometry.py</code> to configure the tf tree for gmapping. After looking at the ROS wiki documentation for the <strong>slam_gmapping package</strong><sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup>, I found that for gmapping to run correctly, it needs an <code class="language-plaintext highlighter-rouge">odom--&gt;base_link--&gt;laser</code> transform. The <code class="language-plaintext highlighter-rouge">gazebo_odometry.py</code> node was written, (from what I understood), for navigation purposes. That’s why it transforms <code class="language-plaintext highlighter-rouge">map--&gt;odom</code>, instead of <code class="language-plaintext highlighter-rouge">odom--&gt;base_link</code>. So I simply changed the parent link to <code class="language-plaintext highlighter-rouge">odom</code> and child link to <code class="language-plaintext highlighter-rouge">base_link</code> in the script and voila! We now have working SLAM!</p>

<p><img src="/assets/images/blog/racecar1/gmapping.png" alt="Gmapping" class="border" /></p>

<p>The <code class="language-plaintext highlighter-rouge">map--&gt;odom</code> transform is now provided by the <code class="language-plaintext highlighter-rouge">/slam_gmapping</code> node. We can see the same by looking again at the tf tree:</p>

<p><img src="/assets/images/blog/racecar1/frames2.png" alt="TF" class="border" /></p>

<p class="info">Below is a scribd embed if you want to have a closer look at the final TF Tree.</p>

<iframe class="scribd_iframe_embed" title="MIT-racecar gmapping TF" src="https://www.scribd.com/embeds/493778944/content?start_page=1&amp;view_mode=scroll&amp;access_key=key-pGyvLCwYtc4KjXpIb50o" data-auto-height="true" data-aspect-ratio="2.342857142857143" scrolling="no" id="doc_88947" width="100%" height="600" frameborder="0"></iframe>
<script type="text/javascript">(function() { var scribd = document.createElement("script"); scribd.type = "text/javascript"; scribd.async = true; scribd.src = "https://www.scribd.com/javascripts/embed_code/inject.js"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(scribd, s); })();</script>

<p>Finally, I ran the <code class="language-plaintext highlighter-rouge">keyboard_teleop.py</code> script using</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>rosrun racecar_control keyboard_teleop.py
</code></pre></div></div>

<p>to teleoperate the racecar. Once a good map is obtained, it can be saved using</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>rosrun map_server map_saver <span class="nt">-f</span> mymap
</code></pre></div></div>

<p>With this, we come to the end of this post. The key takeaway from this post is that <strong>TF transforms are an extremely important</strong> part of ROS. It is vital that a ROS Developer be comfortable with interpreting the TF tree. To any beginner ROS users, I would recommend to always run <code class="language-plaintext highlighter-rouge">tf view_frames</code> node and take a look at the TF tree when implementing someone else’s package.</p>

<p>Next I will be implementing the <strong>ROS Navigation Stack</strong><sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">6</a></sup> on the mit-racecar robot. I will post here if I encounter any interesting problems.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:2" role="doc-endnote">
      <p><a href="https://github.com/mit-racecar/racecar" target="_blank">racecar</a> <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p><a href="https://github.com/mit-racecar/racecar" target="_blank">racecar_gazebo</a> <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:1" role="doc-endnote">
      <p><a href="https://www.ros.org/reps/rep-0105.html#relationship-between-frames" target="_blank">REP 105</a> <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p><a href="http://wiki.ros.org/tf#static_transform_publisher" target="_blank">static_transform_publisher</a> <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5" role="doc-endnote">
      <p><a href="http://wiki.ros.org/slam_gmapping" target="_blank">slam_gmapping</a> <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:6" role="doc-endnote">
      <p><a href="http://wiki.ros.org/navigation" target="_blank">ROS Navigation Stack</a> <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Devansh Dhrafani</name></author><category term="ROS" /><category term="Mobile Robot" /><summary type="html"><![CDATA[For a recent project, I had to map an environment using the MIT-Racecar robot in Gazebo. This is a log of my progress implementing Simultaneous Localisation and Mapping (SLAM) on the MIT-Racecar bot. I encountered some interesting problems, and learnt a few things while solving them. In this short post, I will summarise the steps that I used to solve the problems and get gmapping to run on the racecar_gazebo simulator.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devanshdhrafani.github.io/assets/images/blog/racecar1/gmapping.png" /><media:content medium="image" url="https://devanshdhrafani.github.io/assets/images/blog/racecar1/gmapping.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Mobile Robot 1: URDF, Sensors, Gazebo and Rviz</title><link href="devanshdhrafani.github.io/posts/2020-11-01-diffdrive/" rel="alternate" type="text/html" title="Mobile Robot 1: URDF, Sensors, Gazebo and Rviz" /><published>2020-11-01T00:00:00-04:00</published><updated>2020-11-01T00:00:00-04:00</updated><id>devanshdhrafani.github.io/posts/diffdrive</id><content type="html" xml:base="devanshdhrafani.github.io/posts/2020-11-01-diffdrive/"><![CDATA[<p>Software simulation in ROS can help you learn about how to make robots “think.” It is a long way to go from a line following bot to a self-driving car!  This makes software development one of the hardest fields to explore for enthusiasts. This is where the Robot Operating System (ROS) comes in. The highly modular nature of ROS facilitates a developer to focus on their domain and use one of the many open-source ROS packages to fill in the gaps.</p>

<p>In this project, I developed a ROS package that implements SLAM and Autonomous Navigation on a custom 2 wheeled Differential Drive robot in Gazebo. Throughout the development of this project, I learnt several new ROS concepts which are essential to understand for any beginner. In these posts, I will summarise the steps that I followed (along with relevant links) and hopefully, by the end, you would have learnt something new.</p>

<p>Bur first, let’s watch the final result:</p>
<div><div class="extensions extensions--video">
  <iframe src="https://www.youtube.com/embed/jbd2p1llsqA?rel=0&amp;showinfo=0" frameborder="0" scrolling="no" allowfullscreen=""></iframe>
</div>
</div>

<p class="info">To follow along, I would suggest that you install the ROS package using the instructions in the GitHub Repository<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. You can find the same <a href="https://github.com/devanshdhrafani/diff_drive_bot" target="_blank">here</a>.</p>

<h2 id="urdf-and-sensors">URDF and Sensors</h2>

<p>We will be using Gazebo as a simulation environment for our purposes, so, we need to describe the robot model in a way that Gazebo can understand it. The <strong>Unified Robotic Description Format (URDF)</strong> comes to our aid here. URDF describes all the physical properties of a robot, like its basic blocks (links), how links move relative to one another (joints), inertial properties of the links, visual appearance in Gazebo, collision properties, etc.</p>

<p>Apart from that, as we are working in a simulation environment, we will need the robot to get sensor data like LIDAR point clouds, camera feed, etc. We also need a way to move the robot in Gazebo. All these are done using <strong>Gazebo plugins</strong>.<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">2</a></sup></p>

<p>So now, let’s look at the URDF for our robot. There are 4 files located in the <code class="language-plaintext highlighter-rouge">~/urdf/</code> folder:</p>
<ul>
  <li>mybot.xacro</li>
  <li>materials.xacro</li>
  <li>mybot.gazebo</li>
  <li>macros.xacro</li>
</ul>

<p>URDF is written in XML. And as you can see, it gets a bit complicated when dealing with many links and joints. To avoid rewriting certain blocks of code, we can use <strong>Xacro</strong>. Xacro effectively gives us shortcuts to help reduce the overall size of the URDF file which makes it easier to read and maintain. I followed the <strong>ROS Wiki URDF Tutorials</strong><sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">3</a></sup> to learn about URDF and Xacros.</p>

<p>The <code class="language-plaintext highlighter-rouge">mybot.xacro</code> file is our main URDF. It specifies all the joints, links, sensors. You can see that we are importing the other files using:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;xacro:include</span> <span class="na">filename=</span><span class="s">"$(find diff_drive_bot)/urdf/mybot.gazebo"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;xacro:include</span> <span class="na">filename=</span><span class="s">"$(find diff_drive_bot)/urdf/materials.xacro"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;xacro:include</span> <span class="na">filename=</span><span class="s">"$(find diff_drive_bot)/urdf/macros.xacro"</span> <span class="nt">/&gt;</span>
</code></pre></div></div>
<p>Let’s look at a code snippet from <code class="language-plaintext highlighter-rouge">mybot.xacro</code>:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;link</span> <span class="na">name=</span><span class="s">'chassis'</span><span class="nt">&gt;</span>
    <span class="nt">&lt;pose&gt;</span>0 0 0.1 0 0 0<span class="nt">&lt;/pose&gt;</span>

    <span class="nt">&lt;inertial&gt;</span>
      <span class="nt">&lt;mass</span> <span class="na">value=</span><span class="s">"15.0"</span><span class="nt">/&gt;</span>
      <span class="nt">&lt;origin</span> <span class="na">xyz=</span><span class="s">"0.0 0 0.1"</span> <span class="na">rpy=</span><span class="s">" 0 0 0"</span><span class="nt">/&gt;</span>
      <span class="nt">&lt;inertia</span>
          <span class="na">ixx=</span><span class="s">"0.1"</span> <span class="na">ixy=</span><span class="s">"0"</span> <span class="na">ixz=</span><span class="s">"0"</span>
          <span class="na">iyy=</span><span class="s">"0.1"</span> <span class="na">iyz=</span><span class="s">"0"</span>
          <span class="na">izz=</span><span class="s">"0.1"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;/inertial&gt;</span>

    <span class="nt">&lt;collision</span> <span class="na">name=</span><span class="s">'collision'</span><span class="nt">&gt;</span>
      <span class="nt">&lt;geometry&gt;</span>
        <span class="nt">&lt;box</span> <span class="na">size=</span><span class="s">".4 .2 .1"</span><span class="nt">/&gt;</span>
      <span class="nt">&lt;/geometry&gt;</span>
    <span class="nt">&lt;/collision&gt;</span>

    <span class="nt">&lt;visual</span> <span class="na">name=</span><span class="s">'chassis_visual'</span><span class="nt">&gt;</span>
      <span class="nt">&lt;origin</span> <span class="na">xyz=</span><span class="s">"0 0 0"</span> <span class="na">rpy=</span><span class="s">" 0 0 0"</span><span class="nt">/&gt;</span>
      <span class="nt">&lt;geometry&gt;</span>
        <span class="nt">&lt;box</span> <span class="na">size=</span><span class="s">".4 .2 .1"</span><span class="nt">/&gt;</span>
      <span class="nt">&lt;/geometry&gt;</span>
    <span class="nt">&lt;/visual&gt;</span>
<span class="nt">&lt;/link&gt;</span>
</code></pre></div></div>

<p>Here, we are defining a link chassis and specifying its visual, inertial and collision properties. 
To describe the joints between 2 links, we use:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;joint</span> <span class="na">type=</span><span class="s">"continuous"</span> <span class="na">name=</span><span class="s">"left_wheel_hinge"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;origin</span> <span class="na">xyz=</span><span class="s">"0 0.15 0"</span> <span class="na">rpy=</span><span class="s">"0 0 0"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;child</span> <span class="na">link=</span><span class="s">"left_wheel"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;parent</span> <span class="na">link=</span><span class="s">"chassis"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;axis</span> <span class="na">xyz=</span><span class="s">"0 1 0"</span> <span class="na">rpy=</span><span class="s">"0 0 0"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;limit</span> <span class="na">effort=</span><span class="s">"10000"</span> <span class="na">velocity=</span><span class="s">"1000"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;joint_properties</span> <span class="na">damping=</span><span class="s">"1.0"</span> <span class="na">friction=</span><span class="s">"1.0"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/joint&gt;</span>
</code></pre></div></div>

<p>Here, the joint between <code class="language-plaintext highlighter-rouge">left_wheel</code> and <code class="language-plaintext highlighter-rouge">chassis</code> is called <code class="language-plaintext highlighter-rouge">left_wheel_hinge</code> and is a <strong>continuous</strong> type hinge. Meaning it rotates about its axis with no limits. Learn more about all 6 types of URDF joints here.<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup></p>

<p>The <code class="language-plaintext highlighter-rouge">mybot.gazebo</code> file contains the gazebo plugins for the differential drive motion and sensors. The <code class="language-plaintext highlighter-rouge">materials.xacro</code> contains all the colour RGB values that we use in mybot.xacro and mybot.gazebo. Finally, <code class="language-plaintext highlighter-rouge">macros.xacro</code> contains all the macros for mathematical equations used to define the robot.  It’s not being used in this case because we have fixed all the values. But if you were to change the dimensions of your robot, you can use the <code class="language-plaintext highlighter-rouge">macros.xacro</code> file to avoid calculating the other related dimensions.</p>

<p>Again, to learn how to write URDF and use Xacro, the ROS Wiki tutorial<sup id="fnref:2:1" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">3</a></sup> series is a good place to start.</p>

<h2 id="gazebo-and-rviz">Gazebo and Rviz</h2>

<p><strong>Gazebo</strong> is an open-source 3D robotics simulator. This means that it is essentially a replacement for the “<strong>Real World</strong>”. Developers can test their algorithms on Gazebo first, iron out the bugs and then finally build and implement on a real robot. <strong>Rviz</strong> stands for <strong>ROS visualization</strong>. At any instant, several ROS nodes are publishing and subscribing messages on various topics. Some of these messages are difficult to understand by just reading the values. Rviz helps us visualize the message data. E.g., LIDAR point clouds are displayed as dots in Rviz, making it much more intuitive than seeing thousands of numbers in an array.</p>

<p>Let’s spawn our robot in Gazebo:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>roslaunch diff_drive_bot gazebo.launch 
</code></pre></div></div>

<p>The <strong>ROS launch</strong><sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup> file <code class="language-plaintext highlighter-rouge">gazebo.launch</code> starts up the ROS core, Gazebo, and spawns our robot in an empty world with the House model as the robot’s environment.</p>

<p><img src="/assets/images/blog/diffdrive/gazebo.png" alt="Gazebo" /></p>

<p>You can see that LIDAR’s laser bouncing off on the walls and objects of the environment. 
Now let’s start Rviz:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>roslaunch diff_drive_bot rviz.launch
</code></pre></div></div>
<p>This launch file sets the <code class="language-plaintext highlighter-rouge">robot_description</code> parameter to our robot URDF and publishes the <strong>joint and robot states</strong>. These are essential to publish because otherwise, Rviz won’t know how the robot links transform.</p>

<p><img src="/assets/images/blog/diffdrive/rviz.png" alt="Rviz" /></p>

<p>You can see in Rviz that the LIDAR data published on the <code class="language-plaintext highlighter-rouge">scan</code> topic is visualised as red dots, showing the obstacles and walls in the robot’s path.</p>

<h2 id="teleoperation">Teleoperation</h2>

<p>Now that we setup the URDF, Sensors and watched the robot in Gazebo and the sensor data in Rviz, its time to move the robot! For the purpose of this demonstration, I modified the turtlebot3 keyboard teleoperation node to suit our needs. To use that, execute:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>rosrun diff_drive_bot keyboard_teleop.py 
</code></pre></div></div>
<p>You would now be able to move the robot around in Gazebo. Observe how the Rviz laser scan point cloud also changes as the robot moves around in the environment.</p>

<p>With that, this little tutorial/guide is over. I hope that you learnt something new about URDF, Sensors and how to visualise them in Gazebo and Rviz. If you have any questions/suggestions, feel free to drop them below. In the next post, I will share how I implemented SLAM and the ROS Navigation stack on the above robot.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p><a href="https://github.com/devanshdhrafani/diff_drive_bot" target="_blank">GitHub Repo</a> <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p><a href="http://gazebosim.org/tutorials?tut=ros_gzplugins" target="_blank">Gazebo Plugins</a> <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p><a href="http://wiki.ros.org/urdf/Tutorials" target="_blank">URDF Tutorials</a> <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a> <a href="#fnref:2:1" class="reversefootnote" role="doc-backlink">&#8617;<sup>2</sup></a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p><a href="http://wiki.ros.org/urdf/XML/joint" target="_blank">Types of URDF Joints</a> <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5" role="doc-endnote">
      <p><a href="http://wiki.ros.org/roslaunch" target="_blank">ROS Launch Files</a> <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Devansh Dhrafani</name></author><category term="ROS" /><category term="Mobile Robot" /><summary type="html"><![CDATA[I developed a ROS package that implements SLAM and Autonomous Navigation on a custom 2 wheeled Differential Drive robot in Gazebo. Throughout the development of this project, I learnt several new ROS concepts which are essential to understand for any beginner. This post summarises the steps that I followed with relevant links for learning the same.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devanshdhrafani.github.io/assets/images/blog/diffdrive/gazebo.png" /><media:content medium="image" url="https://devanshdhrafani.github.io/assets/images/blog/diffdrive/gazebo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>