<?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="https://teng4.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://teng4.github.io/" rel="alternate" type="text/html" /><updated>2026-10-01T18:41:32-07:00</updated><id>https://teng4.github.io/feed.xml</id><title type="html">Teng’s Website</title><subtitle>personal description</subtitle><author><name>Teng Li</name><email>tengli.li@utoronto.ca</email></author><entry><title type="html">How To export robot URDF file from SolidWorks?</title><link href="https://teng4.github.io/posts/20260122/" rel="alternate" type="text/html" title="How To export robot URDF file from SolidWorks?" /><published>2026-01-22T00:00:00-08:00</published><updated>2026-01-22T00:00:00-08:00</updated><id>https://teng4.github.io/posts/blog-post-10</id><content type="html" xml:base="https://teng4.github.io/posts/20260122/"><![CDATA[<p>(20260122).</p>

<h1 id="how-to-export-robot-urdf-file-from-solidworks">How to export robot URDF file from SolidWorks?</h1>

<p>This post describes the detailed procedures for exporting <code class="language-plaintext highlighter-rouge">URDF</code> files of a robot from SolidWorks.</p>

<p><strong>Test Environment:</strong></p>

<ul>
  <li>SolidWorks 2022, running on a PC with Windows 11 OS.</li>
  <li>The resulting URDFs are tested in a catkin_ws on a PC with Ubuntu 20.0.</li>
  <li>ROS1, noetic, Ubuntu 20.04.6 LTS</li>
</ul>

<p><strong>Main testbed:</strong></p>

<ul>
  <li>Panda urdf export.</li>
  <li>Kuka urdf export.</li>
</ul>

<h2 id="1-install-plugin-tool-sw2urdf-in-solidworks-in-windows-os">1.	Install plugin tool (SW2URDF) in SolidWorks in Windows OS.</h2>

<ul>
  <li>Download “sw2urdfSetup.exe” from “SolidWorks to URDF Exporter (https://wiki.ros.org/sw_urdf_exporter )”</li>
  <li>Follow the instructions there, and install “sw2urdfSetup.exe”.</li>
  <li>If installation is successful, you should be able to find it in SolidWorks. When SolidWorks is open, under “Tools &gt; Add-ins” you will see a <code class="language-plaintext highlighter-rouge">SW2URDF</code> item at the very bottom as shown below.</li>
</ul>

<table>
  <tbody>
    <tr>
      <td><img src="HowToExportURDF/SW2URDF_AddIns.png" width="60%" height="60%" /></td>
    </tr>
  </tbody>
</table>

<h2 id="2-create-a-new-assembly-file-sldasm-in-solidworks-and-setupassemble-the-robot-from-parts">2.	Create a new assembly file (.SLDASM) in SolidWorks and Setup/Assemble the robot from parts.</h2>

<p>In SolidWorks 2022 or other versions, create a new <code class="language-plaintext highlighter-rouge">filename.SLDASM</code> file, and rename it with your own file name, e.g., I rename mine as <code class="language-plaintext highlighter-rouge">“panda.SLDASM”</code>.</p>

<p><em>Note: This name has nothing to do with the names later in URDF files, so you can name it whatever you want. Actually, for any names outside of the URDF configuration interface, they do not affect the names inside the URDF, because you will need to type in the link/joint names manually inside the URDF configuration anyway when you reach there.</em></p>

<p>Before proceeding, you should collect all the robot links’ parts (<code class="language-plaintext highlighter-rouge">.SLDPRT</code> files) into one dedicated folder, because later you will need to import them into the <code class="language-plaintext highlighter-rouge">panda.SLDASM</code> file one by one.</p>

<p>Now in the brand new panda.SLDASM file, import the first part which is the robot’s <code class="language-plaintext highlighter-rouge">“base”</code>, then put it anywhere by a click, and the <code class="language-plaintext highlighter-rouge">“base”</code> link will be fixed there. Again, the name and position of the links do not matter, you can keep the default for easy.</p>

<p>Same procedures, import all other <code class="language-plaintext highlighter-rouge">link.SLDPRT</code> files (e.g., link1, link2, link3,…, link7), put them one by one aside in the <code class="language-plaintext highlighter-rouge">panda.SLDASM</code> environment.
If a specific part needs to be moved or rotated around itself, you can use the <code class="language-plaintext highlighter-rouge">“Move Component”</code> function. Or you can do this right before assembling it when needed.</p>

<p>Starting from the <code class="language-plaintext highlighter-rouge">“base”</code> link, create a mate at the joint for each two adjacent links. For revolute joint, it usually requires two mates, <code class="language-plaintext highlighter-rouge">“Concentric”</code> mate and <code class="language-plaintext highlighter-rouge">“Coincident”</code> mate. The <code class="language-plaintext highlighter-rouge">“Concentric”</code> mate is used to connect to the two links via a co-axis (they can still rotate around the co-axis and move along the co-axis), while the <code class="language-plaintext highlighter-rouge">“Coincident”</code> mate is used to make the two links touch each other exactly on the joint origin point, such that only rotation around the co-axis is allowed.</p>

<p>Before creating the mates, <em>a good practice</em> here is that, in each joint part (right-click the part and open it at the current position), create a new sketch at the plane of each link’s both ends (the plane is the joint plane where the two adjacent links touch each other), put an <code class="language-plaintext highlighter-rouge">octagonal polygon</code> (<em>note, hexagon is not good enough.</em>) on the plane for construction while taking the joint origin as the center of the <code class="language-plaintext highlighter-rouge">octagonal polygon</code>.</p>

<p>Then draw <code class="language-plaintext highlighter-rouge">two lines</code> (the horizontal and vertical lines for construction) by connecting the appropriate points on the octagon vertices. <strong>NOTE: These two lines and the octagons will be the critical references when you want to accurately (in term of visual observation) align the two adjacent links to be at 0 deg.</strong></p>

<p>You may also want to put something extra (e.g., another smaller octagon polygon) as a marker to mark out which end of the link is <em>n</em> and which one is <em>n+1</em> (sometimes, some links are too similar to discriminate (e.g., the Panda’s links), so with this marker you can quickly discriminate which one is which end.)</p>

<p>In each sketch, at the <code class="language-plaintext highlighter-rouge">joint origin</code>, it is better to put a <code class="language-plaintext highlighter-rouge">sketch Point</code> entity there (strongly suggested), it will later help you find this <code class="language-plaintext highlighter-rouge">joint origin</code> quickly and accurately via this <code class="language-plaintext highlighter-rouge">sketch Point</code> entity.</p>

<h2 id="3-add-references-frames-and-axis-for-each-joint">3.	Add references frames and axis for each joint.</h2>

<p>After creating all the mates for joints/links, the robot should be movable when you drag any of the link, it will be able to rotate around the joint axis (<code class="language-plaintext highlighter-rouge">joint origin</code>).</p>

<p><strong>IMPORTANT NOTE HERE: Now it is better to rotate all joints to be at 0 degrees, and then lock the mates at that position which will disable the joint rotation. Aiming for a high level of accuracy for the link alignments (still on the visual observation level), you can take the horizontal &amp; vertical lines inside the octagon as a reference when rotating the joint.</strong></p>

<p>Now you need to add reference coordinates and reference axis on each joint in the panda.SLDASM file. Looking for top menu <code class="language-plaintext highlighter-rouge">“Reference-&gt;Coordinate System”</code> and <code class="language-plaintext highlighter-rouge">“Reference-&gt;Axis”</code> in the Assembly tab.</p>

<p>A <em>good practice</em> for adding <code class="language-plaintext highlighter-rouge">“Coordinate System”</code> and <code class="language-plaintext highlighter-rouge">“Axis”</code> here is to:</p>

<ul>
  <li>add <code class="language-plaintext highlighter-rouge">“joint0_frame”</code> and <code class="language-plaintext highlighter-rouge">“axis0”</code> to the starting side of the <code class="language-plaintext highlighter-rouge">“base”</code>(link0),</li>
  <li>add <code class="language-plaintext highlighter-rouge">“joint1_frame”</code> and <code class="language-plaintext highlighter-rouge">“axis1”</code> to the starting side of the <code class="language-plaintext highlighter-rouge">“link1”</code>,</li>
  <li>add <code class="language-plaintext highlighter-rouge">“joint2_frame”</code> and <code class="language-plaintext highlighter-rouge">“axis2”</code> to the starting side of the <code class="language-plaintext highlighter-rouge">“link2”</code>,</li>
  <li>and so on.</li>
  <li><em>Good practice</em>: set the previous link part as <em>invisible</em> when you work on the current link part, this will allow you to find the sketch Point and octagon correctly and quickly.</li>
  <li><strong>NOTE:</strong> Here it is strongly suggested to rename them as <code class="language-plaintext highlighter-rouge">“joint0_frame”</code> and <code class="language-plaintext highlighter-rouge">“axis0”</code> for <code class="language-plaintext highlighter-rouge">“base”</code> link, <code class="language-plaintext highlighter-rouge">“joint1_frame”</code> and <code class="language-plaintext highlighter-rouge">“axis1”</code> for <code class="language-plaintext highlighter-rouge">“link1”</code> link, and so on. This will provide huge convenience when use them later in URDF configuration.</li>
</ul>

<p><strong>IMPORTANT NOTE: The Reference frames (Coordinate System) xyz directions must be exact same as the one you defined in the DH table and frame attachments, this will avoid you to do point/negative calibration for each joint later because you already correctly defined each frame here. IF you do not have a DH table and frame attachments, then you can obey the basic rules: (1) z-axis is always the joint rotation axis; (2) x-axis can be pointing to the direction of the link axis (assume the link is a cylinder here). To be precisely, the x-axis is pointing to the common perpendicular of the two adjacent joint axes.</strong></p>

<p>The reference Coordinate System is attached to the joint origin Point, and use the earlier created <code class="language-plaintext highlighter-rouge">horizontal &amp; vertical lines in the octagon</code> to align properly with the XYZ axis of the Coordinate System.</p>

<p>The reference Axis is created by going through the joint origin Point and normal to the joint rotating plane.</p>

<p>Make sure that all the created mates, reference frames and reference axis are all in the <code class="language-plaintext highlighter-rouge">panda.SLDASM</code> level, NOT inside the parts as shown below.</p>

<h2 id="4-urdf-configuration-setup">4.	URDF configuration setup.</h2>

<p>Now you can start to configure the <code class="language-plaintext highlighter-rouge">URDF configuration</code>.</p>

<p>Looking for the <code class="language-plaintext highlighter-rouge">“tools/Export as URDF”</code>, and click, it will start to open a <code class="language-plaintext highlighter-rouge">URDF configuration</code> (it will appear in the tree list, and save there).</p>

<p><em>NOTE: Remember to save the URDF configuration frequently during the configuration by clicking the green tick sign (check mark). This is because sometime when you click to select the link body, it may select a wrong component by mistake, you may end up with selected a curve which will automatically get you out of the URDF configuration UNSAVED. Then you need to repeat all the procedures again.</em></p>

<p>In a blank brand new URDF configuration interface, you can start to set up the link/joint connections. See the screenshot below for the details.</p>

<p><img src="HowToExportURDF/URDFconfig_base.png" alt="URDFconfig_base.png" /></p>

<p><img src="HowToExportURDF/URDFconfig_link1.png" alt="URDFconfig_link1.png" /></p>

<p><img src="HowToExportURDF/URDFconfig_link2.png" alt="URDFconfig_link2.png" /></p>

<h2 id="5-urdf-export">5.	URDF export.</h2>

<p>Once all this URDF configuration done, save it first by clicking the green tick sign, this will save all the configurations, otherwise you may need to go through all them again when need it again.</p>

<p>When all URDF configuration done and saved correctly, open it again, and check it though to the last link, make sure all correct, then click <code class="language-plaintext highlighter-rouge">“Preview and Export”</code>. This will open interface to export URDF files, see the screenshots below.</p>

<table>
  <tbody>
    <tr>
      <td><img src="HowToExportURDF/URDFexport_checkpage_joint2.png" width="60%" height="60%" /></td>
    </tr>
  </tbody>
</table>

<table>
  <tbody>
    <tr>
      <td><img src="HowToExportURDF/URDFexport_checkpage_link2.png" width="60%" height="60%" /></td>
    </tr>
  </tbody>
</table>

<p>Now here you just need to check, ensure the joint types are correct as set earlier. Keep all other parameters as default if you do not know them (e.g., inertia, mass, color, etc), no matter whether they are blank, zeros, or auto-generated values, all ok, just use the default is ok.</p>

<p>Then select <code class="language-plaintext highlighter-rouge">“Fine”</code> in <code class="language-plaintext highlighter-rouge">“Mesh Detail”</code>, and click <code class="language-plaintext highlighter-rouge">“Export URDF and Meshes”</code>, re-name the export file name as <code class="language-plaintext highlighter-rouge">“panda1”</code>, or the name you needed in the URDF files. This will save all the URDF files in a folder named <code class="language-plaintext highlighter-rouge">“panda1”</code> (see screenshot below). <strong>NOTE, this name “panda1” is very important, it will be the name used everywhere inside the URDF file.</strong></p>

<table>
  <tbody>
    <tr>
      <td><img src="HowToExportURDF/URDF_panda1_folder.png" width="60%" height="60%" /></td>
    </tr>
  </tbody>
</table>

<ul>
  <li>“C:\Users\tengl\Desktop\20251205_CAD\panda_urdf\panda1\urdf\panda1.urdf”</li>
  <li>“C:\Users\tengl\Desktop\20251205_CAD\panda_urdf\panda1\meshes\link1.STL”</li>
  <li>“panda1\urdf\panda1.urdf”</li>
  <li>“panda1\meshes\link1.STL”</li>
</ul>

<p>In order to test the URDF in RVIZ, In <code class="language-plaintext highlighter-rouge">“panda1.urdf”</code> file, you may need to rename the <code class="language-plaintext highlighter-rouge">link names</code>, <code class="language-plaintext highlighter-rouge">joint names</code>, and the <code class="language-plaintext highlighter-rouge">paths</code>. For example,</p>

<ul>
  <li>“link1” -&gt; “panda1/link1”</li>
  <li>“joint1” -&gt; “panda1/joint1”</li>
  <li>filename=”package://panda1/meshes/base.STL” -&gt; filename=”package://franka_description/panda1/meshes/base.STL”
All done.</li>
</ul>

<h2 id="6-urdf-export-all-done">6.	URDF export all done.</h2>

<p>Now you have done all the procedures for exporting robot’s URDF, and you got the URDF files for a robot. Next you just need to do some tests and see if the URDF works correctly.</p>

<h2 id="7-urdf-test-examples-panda--kuka">7.	URDF Test Examples (Panda &amp; Kuka)</h2>

<p><strong>URDF Test Example 1:</strong> Franka Panda (fr3) robot URDF test passed ok (Left: original URDF from the company; Right: self-exported URDF from SolidWorks.)</p>

<p><strong>Test results:</strong> all joints works correctly and same as the official panda URDF. All positive/negative directions for each joint are correct, and no need to calibrate them.</p>

<table>
  <tbody>
    <tr>
      <td><img src="HowToExportURDF/URDF_panda1_myurdf_good1stver.png" width="60%" height="60%" /></td>
    </tr>
  </tbody>
</table>

<p><strong>URDF Test Example 2:</strong> Kuka (LBR iisy 11 R1300) robot URDF test passed ok (Left: original URDF from GitHub; Right: self-exported URDF from SolidWorks.)</p>

<p><strong>Test results:</strong> all joints works and can rotate around the correct axis. The Reference Coordinate Systems is arbitrarily specified, so they are not correct yet, due to the fact that I do not have a DH table and frame attachment for it when do the URDF export and test.</p>

<table>
  <tbody>
    <tr>
      <td><img src="HowToExportURDF/URDF_kuka1_myurdf_good1stver.png" width="60%" height="60%" /></td>
    </tr>
  </tbody>
</table>

<hr />
<p>Created on 2026-01-22.</p>]]></content><author><name>Teng Li</name><email>tengli.li@utoronto.ca</email></author><category term="URDF" /><category term="SolidWorks" /><category term="SW2URDF" /><category term="Franka Emika" /><category term="Panda" /><category term="rviz" /><summary type="html"><![CDATA[(20260122).]]></summary></entry><entry><title type="html">How To convert [xacro] to [urdf] for Panda robot?</title><link href="https://teng4.github.io/posts/20250416/" rel="alternate" type="text/html" title="How To convert [xacro] to [urdf] for Panda robot?" /><published>2025-04-16T00:00:00-07:00</published><updated>2025-04-16T00:00:00-07:00</updated><id>https://teng4.github.io/posts/blog-post-9</id><content type="html" xml:base="https://teng4.github.io/posts/20250416/"><![CDATA[<p>(20250416).</p>

<h1 id="how-to-convert-xacro-to-urdf-for-franka-emika-panda-robot">How To convert [xacro] to [urdf] for Franka Emika Panda robot?</h1>

<p>This post describes how you can create <code class="language-plaintext highlighter-rouge">.urdf</code> file from <code class="language-plaintext highlighter-rouge">.xacro</code> file for the <strong>Franka Emika</strong> Panda robot, based on the officially provided <a href="https://github.com/frankaemika/franka_description">“franka_description (official)”</a>.</p>

<p>Environment:</p>

<ul>
  <li>ROS1</li>
  <li>rosdistro: noetic</li>
  <li>Ubuntu 20.04.6 LTS</li>
  <li>Source <a href="https://github.com/frankaemika/franka_description">“franka_description (official)”</a></li>
  <li><strong>Improved</strong> <a href="https://github.com/teng4/franka_description">“franka_description (teng4)”</a> with Panda <code class="language-plaintext highlighter-rouge">URDF</code> files inside.</li>
  <li>For application of the <code class="language-plaintext highlighter-rouge">URDF</code> file, please see here for a <a href="https://youtu.be/asSwOhIVADU">demo video</a>.</li>
</ul>

<h2 id="step-0-install-docker">STEP-0: Install Docker</h2>

<p>Docker is required for the process of <code class="language-plaintext highlighter-rouge">xacro</code> -&gt; <code class="language-plaintext highlighter-rouge">URDF</code>. The installation is quick and easy. With the Docker, you can also run Franka’s official code to visualize the Panda robot in rviz.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo apt update
$ sudo apt install docker.io
$ sudo systemctl enable docker
$ sudo systemctl start docker
$ newgrp docker
$ whoami #this will output the USERNAME
$ sudo usermod -aG docker USERNAME
$ groups #should have "docker" as one output
$ sudo reboot #reboot or logout-login the PC is required.
$ docker run hello-world
</code></pre></div></div>

<h2 id="step-1-create-panda-urdf-from-xacro-need-to-do">STEP-1: Create Panda URDF from .xacro, need to do:</h2>

<p><em>(1)</em> Put the original folder <a href="https://github.com/frankaemika/franka_description">“franka_description (official)”</a> into a valid <code class="language-plaintext highlighter-rouge">catkin_ws</code>, ensure the subfolder “scripts” exists, which include the code (<code class="language-plaintext highlighter-rouge">create_urdf.sh</code> <em>etc</em>) will be used to create <code class="language-plaintext highlighter-rouge">URDF</code> files later.</p>

<p><em>(1.1)</em> Commands for generating <code class="language-plaintext highlighter-rouge">URDF</code> from Franka’s <code class="language-plaintext highlighter-rouge">xacro</code>.</p>

<p>NOTE-1: Docker is required.</p>

<p>NOTE-2: It is better to <strong>NOT</strong> use <code class="language-plaintext highlighter-rouge">abs-path</code>, it may have problems when using the <code class="language-plaintext highlighter-rouge">URDF</code> later.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  # Start the generation of the urdf model
$ ./scripts/create_urdf.sh &lt;robot_id&gt; &lt;ee_id&gt;
  # id of the robot model: `fr3`, `fp3`, `fer`, `multi_arm`, `none`
  # id of the robot end effector: `franka_hand` (default), `cobot_pump`
  # Run the following inside *"USERNAME@RT6958U-NESR:~/catkin_ws4/src/franka_description$"*
$ cd ~/catkin_ws4
$ source devel/setup.bash #required
$ cd ~/catkin_ws4/src/franka_description
  # NOTE: It is better to **NOT** use `abs-path`, it may have problems when using the `URDF` later.
$ ./scripts/create_urdf.sh fr3 --robot-ee franka_hand --with-sc --abs-path --no-prefix 
$ ./scripts/create_urdf.sh fr3 --robot-ee franka_hand --with-sc --no-prefix
$ ./scripts/create_urdf.sh fp3 --robot-ee franka_hand --with-sc --no-prefix
$ ./scripts/create_urdf.sh fer --robot-ee franka_hand --with-sc --no-prefix
$ ./scripts/create_urdf.sh multi_arm --robot-ee franka_hand --with-sc --no-prefix
</code></pre></div></div>

<p><em>(1.2)</em> If cmd in <em>(1.1)</em> does not work, run the following inside <em>“USERNAME@RT6958U-NESR:~/catkin_ws4/src/franka_description$”</em>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ python3 ./scripts/create_urdf.py fr3 --robot-ee franka_hand --with-sc --no-prefix
$ python3 ./scripts/create_urdf.py fp3 --robot-ee franka_hand --with-sc --no-prefix
$ python3 ./scripts/create_urdf.py fer --robot-ee franka_hand --with-sc --no-prefix
$ python3 ./scripts/create_urdf.py multi_arm --robot-ee franka_hand --with-sc --no-prefix
$ python3 ./scripts/create_urdf.py fr3 --robot-ee cobot_pump --with-sc --no-prefix
$ python3 ./scripts/create_urdf.py fr3 --robot-ee cobot_pump --with-sc --no-prefix
</code></pre></div></div>

<p><em>(2)</em> In the newly generated <code class="language-plaintext highlighter-rouge">URDF</code> file, need to do:</p>

<ul>
  <li>
    <p>Need to correct one conversion mistake for <code class="language-plaintext highlighter-rouge">"fr3_link8"</code> -&gt; <code class="language-plaintext highlighter-rouge">"link8"</code>.</p>
  </li>
  <li>
    <p>The last two fingers <code class="language-plaintext highlighter-rouge">"prismatic"</code> -&gt; <code class="language-plaintext highlighter-rouge">"fixed"</code>, otherwise it will have few related errors in rviz.</p>
  </li>
</ul>

<h2 id="step-2-panda-in-rviz-file-needs-to-do">STEP-2: Panda in .rviz file, needs to do:</h2>

<p><em>(1)</em> Link names do not matter, and you can leave them alone.</p>

<p><em>(2)</em> Robot Description: <code class="language-plaintext highlighter-rouge">/panda1/robot_description</code> (if your publish topic name is <code class="language-plaintext highlighter-rouge">/panda1/joint_states</code>)</p>

<p><em>(3)</em> Fixed Frame: <code class="language-plaintext highlighter-rouge">link0</code></p>

<h2 id="step-3-panda-in-cpp-file-need-to-do">STEP-3: Panda in .cpp file, need to do:</h2>

<p><em>(1)</em> Ensure the joint names are exactly the same as the ones in the <code class="language-plaintext highlighter-rouge">URDF</code> file.</p>

<p><em>(2)</em> The publish topic name will be <code class="language-plaintext highlighter-rouge">/panda1/joint_states</code>.</p>

<h2 id="step-4-if-all-ok-double-check-with">STEP-4: if all ok, double check with:</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ rosrun rqt_tf_tree rqt_tf_tree
</code></pre></div></div>

<h2 id="optional-step-5-visualize-panda-via-frankas-official-code">(optional) STEP-5: Visualize Panda via Franka’s official code:</h2>

<p>It would be interesting to try the officially provided code for visualizing the Panda robot in <code class="language-plaintext highlighter-rouge">rviz</code>, it will have an independent small panel to control each joint angle via a slider.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  # Run the following inside "USERNAME@RT6958U-NESR:~/catkin_ws4/src/franka_description$"
$ ./scripts/visualize_franka.sh arm_id:=&lt;robot_id&gt; 
$ ./scripts/visualize_franka.sh arm_id:=fr3 #it will show a realistic white franka robot in rviz.
$ ./scripts/visualize_franka.sh arm_id:=fp3 #it will show a realistic black franka robot in rviz.
$ ./scripts/visualize_franka.sh arm_id:=fer #it will show a realistic white franka robot in rviz.
</code></pre></div></div>

<hr />
<p>Created on 2025-04-16.</p>]]></content><author><name>Teng Li</name><email>tengli.li@utoronto.ca</email></author><category term="Panda" /><category term="URDF" /><category term="Franka Emika" /><category term="xacro" /><category term="rviz" /><summary type="html"><![CDATA[(20250416).]]></summary></entry><entry><title type="html">How To generate a triangular mesh plane in Blender for Unity?</title><link href="https://teng4.github.io/posts/20250129/" rel="alternate" type="text/html" title="How To generate a triangular mesh plane in Blender for Unity?" /><published>2025-01-29T00:00:00-08:00</published><updated>2025-01-29T00:00:00-08:00</updated><id>https://teng4.github.io/posts/blog-post-8</id><content type="html" xml:base="https://teng4.github.io/posts/20250129/"><![CDATA[<p>(20250129).</p>

<h1 id="how-to-generate-a-triangular-mesh-plane-in-blender-for-unity">How To generate a triangular mesh plane in Blender for Unity?</h1>

<p>In Unity, the default plane mesh is a 10x10 mesh (to be precisely 11x11 points). Sometimes you may need a plane mesh with more dense points, <em>e.g.</em>, 50x50, 100x100, or even more, such that you can render a cloth plane with more details.</p>

<p>Here below are the steps for generating a 100x100 mesh plane in Blender and exporting it as an OBJ file which can be used in Unity.</p>

<h2 id="steps-to-generate-obj-file-from-blender">Steps to generate OBJ file from blender.</h2>

<ol>
  <li>Open Blender in Ubuntu: <code class="language-plaintext highlighter-rouge">cd blender-3.0.0-linux-x64/</code> -&gt; <code class="language-plaintext highlighter-rouge">./blender</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">A</code> to select all.</li>
  <li><code class="language-plaintext highlighter-rouge">Shift+A</code> to select <code class="language-plaintext highlighter-rouge">Mesh</code> -&gt; <code class="language-plaintext highlighter-rouge">Plane</code>. <strong>Note</strong>, the default size of plane is 2m in Blender.</li>
  <li>Rotate the plane if needed.</li>
  <li><code class="language-plaintext highlighter-rouge">TAB</code> to Edit Mode.</li>
  <li>Select the plane, right-click, select <code class="language-plaintext highlighter-rouge">Subdivide</code>, then at the bottom left corner set the <code class="language-plaintext highlighter-rouge">cut numbers</code> as 99 (for obtaining a plane with 100x100 small squares). <strong>Note</strong>, limits to Max 100 cuts in Blender.</li>
  <li>Select all vertices, then go to tab <code class="language-plaintext highlighter-rouge">Face</code> -&gt; <code class="language-plaintext highlighter-rouge">Triangulate Faces</code>, this will generate triangular meshes on the plane. <strong>Note</strong>, Unity requires triangular mesh instead of quadrilateral mesh.</li>
  <li>HOWEVER, the triangular mesh’s edges are not always in the same orientation. To obtain mesh edges all with the same orientation, DO NOT use the <code class="language-plaintext highlighter-rouge">Face</code> -&gt; <code class="language-plaintext highlighter-rouge">Triangulate Faces</code>. <strong>Instead</strong>, select the plane object, go to the <code class="language-plaintext highlighter-rouge">modifier</code> (a wrench icon), add a <code class="language-plaintext highlighter-rouge">Triangulate Modifier</code>, Set <em>Quad Method</em> as <code class="language-plaintext highlighter-rouge">Fixed</code>, set <em>N-gon Method</em> as <code class="language-plaintext highlighter-rouge">Beauty</code>. Click <code class="language-plaintext highlighter-rouge">Apply</code>. <code class="language-plaintext highlighter-rouge">TAB</code> to check the result, and now all edges of the triangular mesh should be with the same orientation.</li>
  <li><code class="language-plaintext highlighter-rouge">TAB</code> to <em>Object Mode</em>, <code class="language-plaintext highlighter-rouge">Ctrl+A</code> to select and apply <code class="language-plaintext highlighter-rouge">All Transforms</code>.</li>
  <li>Export to OBJ files: check <code class="language-plaintext highlighter-rouge">SectionOnly</code>, check <code class="language-plaintext highlighter-rouge">Geometry/Triangulate Faces</code>, (you can also try <code class="language-plaintext highlighter-rouge">Keep Vertex Order</code>).</li>
  <li>Then click Export.</li>
  <li>Done.</li>
</ol>

<h2 id="import-obj-file-into-unity">Import OBJ file into Unity:</h2>
<ul>
  <li>Remember to check <code class="language-plaintext highlighter-rouge">Read/Write</code>, and <code class="language-plaintext highlighter-rouge">Apply</code>. (<strong>Important! Although no Error msgs if not in Unity.</strong>).</li>
  <li><code class="language-plaintext highlighter-rouge">Import New Assets</code>, then browse to find the OBJ file.</li>
  <li>Directly putting the OBJ file into the target folder in Unity seems also ok and has the same result as via <code class="language-plaintext highlighter-rouge">Import New Assets</code>.</li>
</ul>

<hr />
<p>Created on 2025-01-29.</p>]]></content><author><name>Teng Li</name><email>tengli.li@utoronto.ca</email></author><category term="Blender" /><category term="Plane Mesh" /><category term="Triangular Mesh" /><category term="OBJ" /><category term="Unity" /><summary type="html"><![CDATA[(20250129).]]></summary></entry><entry><title type="html">How To display 2 robots in the same RViz environment?</title><link href="https://teng4.github.io/posts/20241122/" rel="alternate" type="text/html" title="How To display 2 robots in the same RViz environment?" /><published>2024-11-22T00:00:00-08:00</published><updated>2024-11-22T00:00:00-08:00</updated><id>https://teng4.github.io/posts/blog-post-7</id><content type="html" xml:base="https://teng4.github.io/posts/20241122/"><![CDATA[<p>(20241122).</p>

<h1 id="how-to-display-2-or-more-robots-in-the-same-rviz-environment">How To display 2 or more robots in the same RViz environment?</h1>

<h2 id="steps-1">Steps-1</h2>

<p>Make sure two publishers are in separate namespace: <code class="language-plaintext highlighter-rouge">"/robot1/joint_states"</code>, <code class="language-plaintext highlighter-rouge">"/robot2/joint_states"</code>. <strong>Importantly</strong>, when constructing the msg, the joint names should be exactly the same as the ones used in the corresponding URDF files, robot2 joint names could be <code class="language-plaintext highlighter-rouge">"robot2/waist"</code>, <code class="language-plaintext highlighter-rouge">"robot2/shoulder"</code>, <code class="language-plaintext highlighter-rouge">"robot2/elbow"</code>, etc.</p>

<h2 id="steps-2">Steps-2</h2>

<p>In the URDF file of robot2, add namespace for all links and joints, e.g., <code class="language-plaintext highlighter-rouge">"robot2/waist"</code>, <code class="language-plaintext highlighter-rouge">"robot2/shoulder"</code>, <code class="language-plaintext highlighter-rouge">"robot2/elbow"</code>, etc. Note that, for robot1, you can add and use namespace as well like what we did for robot2, but for simplicity, the robot1 URDF and its joint state publisher can remain unchanged which means it is not necessary to add namespace for robot1 URDF and its joint state publisher.</p>

<h2 id="steps-3">Steps-3</h2>

<p>To move the base location of robot2 in RViz environment, use <code class="language-plaintext highlighter-rouge">&lt;node pkg="tf2_ros" type="static_transform_publisher" name="robot2_base_transform" args="0.1 0 0 0 0 0 world robot2/base" /&gt;</code> in the lauch file, which will move the robot2 base to <code class="language-plaintext highlighter-rouge">x=0.1m</code>. Note that, <strong>(a)</strong> the <code class="language-plaintext highlighter-rouge">"robot2/base"</code> is according to the name you defined in the URDF file. <strong>(b)</strong> this static base transform requires to set the <code class="language-plaintext highlighter-rouge">"Fixed Frame"</code> in rviz file to be <code class="language-plaintext highlighter-rouge">"world"</code> instead of <code class="language-plaintext highlighter-rouge">"base"</code> (<code class="language-plaintext highlighter-rouge">"base"</code> in the base name used in URDF file).</p>

<h2 id="steps-4">Steps-4</h2>

<p>In the <code class="language-plaintext highlighter-rouge">*.rviz</code> file, add or modify as the following,</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Displays:
    - Alpha: 0.5
      Cell Size: 1
      Class: rviz/Grid
      Color: 160; 160; 164
      Enabled: true
      Line Style:
        Line Width: 0.03
        Value: Lines
      Name: Grid
      Normal Cell Count: 0
      Offset:
        X: 0
        Y: 0
        Z: 0
      Plane: XY
      Plane Cell Count: 10
      Reference Frame: &lt;Fixed Frame&gt;
      Value: true
    - Alpha: 1
      Class: rviz/RobotModel
      Collision Enabled: false
      Enabled: true
      Links:
        All Links Enabled: true
        Expand Joint Details: false
        Expand Link Details: false
        Expand Tree: false
        Link Tree Style: Links in Alphabetic Order
        base:   #(Note: make sure these link names are exactly same as you used in the URDF file.)
          Alpha: 1
          Show Axes: true   #(Note: you can set it true to show the base frame axis in rviz.)
          Show Trail: false
          Value: true
        lower_arm:
          Alpha: 1
          Show Axes: false
          Show Trail: false
          Value: true
        stylus:
          Alpha: 1
          Show Axes: true    #(Note: you can set it true to show the EE frame axis in rviz.)
          Show Trail: false
          Value: true
        tip:
          Alpha: 1
          Show Axes: false
          Show Trail: false
          Value: true
        torso:
          Alpha: 1
          Show Axes: false
          Show Trail: false
          Value: true
        upper_arm:
          Alpha: 1
          Show Axes: false
          Show Trail: false
          Value: true
        wrist:
          Alpha: 1
          Show Axes: false
          Show Trail: false
          Value: true
      Name: RobotModel1  #(Note: set a unique name for this robot.)
      Robot Description: /robot1/robot_description  #(Note: modify here.)
      TF Prefix: ""  #(Note: DO NOT modify here.)
      Update Interval: 0
      Value: true
      Visual Enabled: true
    - Alpha: 1
      Class: rviz/RobotModel
      Collision Enabled: false
      Enabled: true
      Links:
        All Links Enabled: true
        Expand Joint Details: false
        Expand Link Details: false
        Expand Tree: false
        Link Tree Style: Links in Alphabetic Order
        base:   #(Note: make sure these link names are exactly same as you used in the URDF file.)
          Alpha: 1
          Show Axes: true    #(Note: you can set it true to show the base frame axis in rviz.)
          Show Trail: false
          Value: true
        lower_arm:
          Alpha: 1
          Show Axes: false
          Show Trail: false
          Value: true
        stylus:
          Alpha: 1
          Show Axes: true    #(Note: you can set it true to show the EE frame axis in rviz.)
          Show Trail: false
          Value: true
        tip:
          Alpha: 1
          Show Axes: false
          Show Trail: false
          Value: true
        torso:
          Alpha: 1
          Show Axes: false
          Show Trail: false
          Value: true
        upper_arm:
          Alpha: 1
          Show Axes: false
          Show Trail: false
          Value: true
        wrist:
          Alpha: 1
          Show Axes: false
          Show Trail: false
          Value: true
      Name: RobotModel2  #(Note: set a unique name for this robot.)
      Robot Description: /robot2/robot_description  #(Note: modify here.)
      TF Prefix: "" #(Note: DO NOT modify here.)
      Update Interval: 0
      Value: true
      Visual Enabled: true
  Enabled: true
  Global Options:
    Background Color: 48; 48; 48
    Fixed Frame: world  #(Note: modify here.)
    Frame Rate: 30
</code></pre></div></div>

<h2 id="steps-5">Steps-5</h2>

<p>Adjust the pose of the two robots in rviz using a mouse, and save the rviz file (This will auto correct some minor mistakes you may have made when editing the rviz file, e.g., the robot2 links should be <code class="language-plaintext highlighter-rouge">"robot2/base:"</code>, <code class="language-plaintext highlighter-rouge">"robot2/lower_arm:"</code>, etc.).</p>

<h2 id="steps-6">Steps-6</h2>

<p>Finally, in the launch file, add the following code to launch the two robots with different namespaces.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;launch&gt;
  &lt;!-- Launch Robot 1 --&gt;
  &lt;group ns="robot1"&gt;
    &lt;param name="robot_description" command="cat $(find robot_description)/urdf/robot1.urdf" /&gt;
    &lt;node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" /&gt;
    &lt;node pkg="tf2_ros" type="static_transform_publisher" name="robot1_base_transform"
      args="-0.5 0 0 0 0 0 world base" /&gt; 
      &lt;!-- note, static base transform requires to set the "Fixed Frame" in rviz file to be "world" instead of "base". --&gt;
  &lt;/group&gt;

  &lt;!-- Launch Robot 2 --&gt;
  &lt;group ns="robot2"&gt;
    &lt;param name="robot_description" command="cat $(find robot_description)/urdf/robot2.urdf" /&gt;    
    &lt;node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" /&gt;
    &lt;node pkg="tf2_ros" type="static_transform_publisher" name="robot2_base_transform"
      args="0.1 0 0 0 0 0 world robot2/base" /&gt;
      &lt;!-- note, static base transform requires to set the "Fixed Frame" in rviz file to be "world" instead of "base". --&gt;
  &lt;/group&gt;
  &lt;node name="rviz2" pkg="rviz" type="rviz" args="-d $(find robot_common)/launch/robotx2.rviz" required="true" /&gt;
&lt;/launch&gt;
</code></pre></div></div>

<h2 id="steps-7">Steps-7</h2>

<p>Now you can run the launch file, if it can correctly display two robots in the same RViz, then you can command <code class="language-plaintext highlighter-rouge">"$ rosrun rqt_tf_tree rqt_tf_tree"</code> to check the tf tree.</p>

<h1 id="notes">Notes</h1>

<ul>
  <li>Note, it is better to NOT use <code class="language-plaintext highlighter-rouge">&lt;remap from="/tf" to="robot2/tf" /&gt;</code> and <code class="language-plaintext highlighter-rouge">&lt;remap from="/tf_static" to="robot2/tf_static" /&gt;</code> in the launch file. Because it will make the command <code class="language-plaintext highlighter-rouge">"$ rosrun rqt_tf_tree rqt_tf_tree"</code> be invalid when you want to check the tf tree. <code class="language-plaintext highlighter-rouge">/tf</code> itself can discriminate the joint states based on namespace, so no need to remap <code class="language-plaintext highlighter-rouge">/tf</code>.</li>
</ul>

<hr />
<p>Created on 2024-11-22.</p>]]></content><author><name>Teng Li</name><email>tengli.li@utoronto.ca</email></author><category term="RViz" /><category term="multiple robots" /><category term="ROS" /><summary type="html"><![CDATA[(20241122).]]></summary></entry><entry><title type="html">How To use the variable [List] in Unity?</title><link href="https://teng4.github.io/posts/20241004/" rel="alternate" type="text/html" title="How To use the variable [List] in Unity?" /><published>2024-10-04T00:00:00-07:00</published><updated>2024-10-04T00:00:00-07:00</updated><id>https://teng4.github.io/posts/blog-post-6</id><content type="html" xml:base="https://teng4.github.io/posts/20241004/"><![CDATA[<p>(20241004).</p>

<h1 id="how-to-use-the-variable-list-in-unity">How to use the variable <code class="language-plaintext highlighter-rouge">List</code> in Unity?</h1>

<h2 id="steps-for-test-a-list">Steps for test a <code class="language-plaintext highlighter-rouge">List</code></h2>

<ol>
  <li>declare a <code class="language-plaintext highlighter-rouge">List</code> variable <code class="language-plaintext highlighter-rouge">dh = new List&lt;float[]&gt;();</code>.</li>
  <li>define a row vector <code class="language-plaintext highlighter-rouge">float[] row1 = { 11.0f, 12.0f, 13.0f, 14.0f };</code>.</li>
  <li>add the row vector to list <code class="language-plaintext highlighter-rouge">dh.Add(row1);</code>.</li>
  <li>print out messages <code class="language-plaintext highlighter-rouge">print("dh.Count = " + dh.Count)</code>.</li>
</ol>

<h2 id="example-c-code-in-unity">Example c# code in Unity</h2>

<p>Example c# code in Unity.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dh = new List&lt;float[]&gt;();
// Create rows as float arrays and add them to the list directly
float[] row1 = { 11.0f, 12.0f, 13.0f, 14.0f };
float[] row2 = { 21.0f, 22.0f, 23.0f, 24.0f };
float[] row3 = { 31.0f, 32.0f, 33.0f, 34.0f };
float[] row4 = { 41.0f, 42.0f, 43.0f, 44.0f };
// Use Add() method to add rows to the list
dh.Add(row1);
dh.Add(row2);
dh.Add(row3);
dh.Add(row4);
print("teng4test, mdh is defined as: dh.Count = " + dh.Count); //=4
print("teng4test, mdh is defined as: dh[1][1] = " + dh[1][1]); //=22
print("teng4test, mdh is defined as: dh[2][2] = " + dh[2][2]); //=33
print("teng4test, mdh is defined as: dh[3][3] = " + dh[3][3]); //=44
// Print the list values using Debug.Log
print("dh.GetType() = " + dh.GetType()); //= System.Collections.Generic.List`1[System.Single[]]
print("dh[1].GetType() = " + dh[1].GetType()); //= System.Single[].
print("dh[1][1].GetType() = " + dh[1][1].GetType()); //= System.Single
print("dh.Capacity = " + dh.Capacity); //= 4
//print("dh[1].Capacity = " + dh[1].Capacity); //will error: float no capacity.
//print("dh[1][1].Capacity = " + dh[1][1].Capacity); //will error: float no capacity.
foreach (float[] row in dh)
{
    string rowValues = string.Join(", ", row);
    Debug.Log("Row: " + rowValues);
}

//try to define another list2.
var list2 = new List&lt;double&gt; {2014.0, 2015.0, 2016.0, 2017.0};
foreach (double row in list2)
{
    string rowValues = string.Join(", ", row);
    Debug.Log("list2 Row: " + rowValues);
}
</code></pre></div></div>

<hr />
<p>Created on 2024-10-04.</p>]]></content><author><name>Teng Li</name><email>tengli.li@utoronto.ca</email></author><category term="Unity" /><category term="List" /><summary type="html"><![CDATA[(20241004).]]></summary></entry><entry><title type="html">How To create a customized ROS msg in MATLAB?</title><link href="https://teng4.github.io/posts/20241001/" rel="alternate" type="text/html" title="How To create a customized ROS msg in MATLAB?" /><published>2024-10-01T00:00:00-07:00</published><updated>2024-10-01T00:00:00-07:00</updated><id>https://teng4.github.io/posts/blog-post-3</id><content type="html" xml:base="https://teng4.github.io/posts/20241001/"><![CDATA[<p>(20241001).</p>

<h1 id="how-to-create-a-customized-ros-msg-in-matlab">How To create a customized ROS msg in MATLAB?</h1>

<p><a href="https://www.mathworks.com/help/ros/ug/create-custom-messages-from-ros-package.html">MATLAB Help</a></p>

<h2 id="step-1-place-your-custom-messages-in-a-location-and-note-the-folder-path">step-1. Place your custom messages in a location and note the folder path.</h2>

<p>It is recommended to put all your custom message definitions in a single packages folder.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% folderpath = 'c:\MATLAB\custom_msgs\packages';
folderpath = '/home/teng/MATLAB/custom_msgs/packages';
</code></pre></div></div>

<p>The above cmd no need to run, but you need to find your custom msg package folder in <code class="language-plaintext highlighter-rouge">catkin_ws</code>, and better to copy it to a single package folder in MATLAB. For example, my custom msg is located in <code class="language-plaintext highlighter-rouge">/home/teng/catkin_ws2/src/phantom_omni/teng4pkg_msgs</code>, then I copy the package folder <code class="language-plaintext highlighter-rouge">teng4pkg_msgs</code> into the MATLAB path <code class="language-plaintext highlighter-rouge">/home/teng/MATLAB/custom_msgs/</code> where all the custom ROS msg definitions to be used in MATLAB will be collected here in the future.</p>

<h2 id="step-2-in-matlab-run-the-command-to-create-custom-msg-for-matlab">step-2. In MATLAB, run the command to create custom msg for MATLAB,</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% rosgenmsg('c:\MATLAB\custom_msgs')
rosgenmsg('/home/teng/MATLAB/custom_msgs/')
</code></pre></div></div>

<p>Matlab will run and show the following info if successful,</p>

<blockquote>
  <p>rosgenmsg(‘/home/teng/MATLAB/custom_msgs/’)
Identifying message files in folder ‘/home/teng/MATLAB/custom_msgs’..Done.
Validating message files in folder ‘/home/teng/MATLAB/custom_msgs’..Done.
[1/1] Generating MATLAB interfaces for custom message packages… Done.
Running catkin build in folder ‘/home/teng/MATLAB/custom_msgs/matlab_msg_gen_ros1/glnxa64’.
Build in progress. This may take several minutes…
Build succeeded.build_log.</p>
</blockquote>

<p>To use the custom messages in Matlab, follow these steps:</p>

<p><strong>1. Add the custom message folder to the MATLAB path by executing:</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>addpath('/home/teng/MATLAB/custom_msgs/matlab_msg_gen_ros1/glnxa64/install/m')
savepath
</code></pre></div></div>

<p><strong>Note</strong>, the above cmd may need to run each time you restart MATLAB (not tested), But it is ok.</p>

<p><strong>2. Refresh all message class definitions, which requires clearing the workspace, by executing:</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clear classes
rehash toolboxcache
</code></pre></div></div>

<p><strong>3. Verify that you can use the custom messages.</strong></p>

<p>Enter “rosmsg list” and ensure that the output contains the generated custom message types.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rosmsg list
</code></pre></div></div>

<p>It will show a long list including your custom msg types. For example, mine is,</p>

<blockquote>
  <p>teng4pkg_msgs/Arm_State                                      <br />
teng4pkg_msgs/PoseCartesian6DOF                              <br />
teng4pkg_msgs/Vector7DOF</p>
</blockquote>

<p><strong>4. Check the details of a specific msg type</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% custommsg = rosmessage('B/Standalone')
custommsg = rosmessage('teng4pkg_msgs/PoseCartesian6DOF')
</code></pre></div></div>

<blockquote>
  <p>custommsg = 
  ROS PoseCartesian6DOF message with properties:
    MessageType: ‘teng4pkg_msgs/PoseCartesian6DOF’
              X: 0
              Y: 0
              Z: 0
              A: 0
              B: 0
              G: 0
  Use showdetails to show the contents of the message</p>
</blockquote>

<p>Bingo. All done. Now you can use the custom msg in Matlab.</p>

<h2 id="debugging">Debugging.</h2>

<p>Matlab may need to configure ROS first. If so, first run this cmd in a Ubuntu terminal.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>whereis python #Find the location of python in your pc.
sudo apt install python3.8-venv
</code></pre></div></div>
<p>Then, in MATLAB go to find <code class="language-plaintext highlighter-rouge">Home-&gt;Preferences-&gt;ROS Toolbox</code>, click button <code class="language-plaintext highlighter-rouge">Open ROS Toolbox Preferences</code>. put the path (e.g., mine is <code class="language-plaintext highlighter-rouge">/usr/bin/python3.8</code>) in the ‘browse’ line, and click <code class="language-plaintext highlighter-rouge">Recreate Python Environment</code>. Done.</p>

<hr />
<p>Created on 2024-10-01.</p>]]></content><author><name>Teng Li</name><email>tengli.li@utoronto.ca</email></author><category term="ROS" /><category term="msg" /><category term="Matlab" /><summary type="html"><![CDATA[(20241001).]]></summary></entry><entry><title type="html">How To convert [ROBOT.urdf.xacro] to [ROBOT.urdf]?</title><link href="https://teng4.github.io/posts/20240923/" rel="alternate" type="text/html" title="How To convert [ROBOT.urdf.xacro] to [ROBOT.urdf]?" /><published>2024-09-23T00:00:00-07:00</published><updated>2024-09-23T00:00:00-07:00</updated><id>https://teng4.github.io/posts/blog-post-4</id><content type="html" xml:base="https://teng4.github.io/posts/20240923/"><![CDATA[<p>(20240923).</p>

<h1 id="how-to-convert-roboturdfxacro-to-roboturdf">How To convert <code class="language-plaintext highlighter-rouge">ROBOT.urdf.xacro</code> to <code class="language-plaintext highlighter-rouge">ROBOT.urdf</code>?</h1>

<p>Terminal command to convert the <code class="language-plaintext highlighter-rouge">ROBOT.urdf.xacro</code> to <code class="language-plaintext highlighter-rouge">ROBOT.urdf</code> (open a terminal inside its folder).</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rosrun xacro xacro --inorder -o ./niryo_one.urdf ./niryo_one.urdf.xacro
</code></pre></div></div>

<p>The above command is used to convert a <code class="language-plaintext highlighter-rouge">.xacro</code> file (<code class="language-plaintext highlighter-rouge">niryo_one.urdf.xacro</code>) into a <code class="language-plaintext highlighter-rouge">URDF</code> file (<code class="language-plaintext highlighter-rouge">niryo_one.urdf</code>), using the <code class="language-plaintext highlighter-rouge">--inorder</code> option.</p>

<p>Command Breakdown:</p>

<ul>
  <li>
    <p><code class="language-plaintext highlighter-rouge">rosrun xacro xacro</code>: Invokes the <code class="language-plaintext highlighter-rouge">xacro</code> command using <code class="language-plaintext highlighter-rouge">rosrun</code>, which is part of the <code class="language-plaintext highlighter-rouge">xacro</code> package in ROS.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">--inorder</code>: Ensures that the processing of macros follows the order in which they appear in the Xacro file. This is recommended for newer Xacro versions and is sometimes necessary to ensure macros are processed correctly, especially in more complex models.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">-o ./niryo_one.urdf</code>: The <code class="language-plaintext highlighter-rouge">-o</code> flag specifies the <strong>output</strong> file (<code class="language-plaintext highlighter-rouge">niryo_one.urdf</code>), which will be generated by converting the <code class="language-plaintext highlighter-rouge">.xacro</code> file.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">./niryo_one.urdf.xacro</code>: The input <code class="language-plaintext highlighter-rouge">.xacro</code> file, which contains the robot description with macros.</p>
  </li>
</ul>

<h2 id="how-to-generate-urdf-form-xacro">How to generate urdf form xacro</h2>
<p>(from dvrk code, dvrk_model/Readme.md, seems no difference with the above command.)</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rosrun xacro xacro mtm_right_only.urdf.xacro &gt; mtm_right_only.urdf
rosrun xacro xacro &lt;input.xacro&gt; -o &lt;output.urdf&gt; //(Syntax from ChatGPT)
</code></pre></div></div>

<p>Command Breakdown:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">rosrun xacro xacro</code>: Runs the <code class="language-plaintext highlighter-rouge">xacro</code> command using <code class="language-plaintext highlighter-rouge">rosrun</code> to process the <code class="language-plaintext highlighter-rouge">.xacro</code> file.</li>
  <li><code class="language-plaintext highlighter-rouge">mtm_right_only.urdf.xacro</code>: The <strong>input</strong> <code class="language-plaintext highlighter-rouge">.xacro</code> file that defines the robot description.</li>
  <li><code class="language-plaintext highlighter-rouge">&gt;</code>: Redirects the <strong>output</strong> of the command to a file.</li>
  <li><code class="language-plaintext highlighter-rouge">mtm_right_only.urdf</code>: The <strong>output</strong> file where the <code class="language-plaintext highlighter-rouge">URDF</code> is stored.</li>
</ul>

<h2 id="note">Note</h2>

<p>An example of robot URDF source files can be found here <a href="https://github.com/NiryoRobotics/niryo_one_ros">niryo_one_ros</a>.</p>

<hr />
<p>Created on 2024-09-23.</p>]]></content><author><name>Teng Li</name><email>tengli.li@utoronto.ca</email></author><category term="urdf" /><category term="xacro" /><summary type="html"><![CDATA[(20240923).]]></summary></entry><entry><title type="html">How To configure Unity to receive ROS data and import URDF robot model?</title><link href="https://teng4.github.io/posts/20240923b/" rel="alternate" type="text/html" title="How To configure Unity to receive ROS data and import URDF robot model?" /><published>2024-09-23T00:00:00-07:00</published><updated>2024-09-23T00:00:00-07:00</updated><id>https://teng4.github.io/posts/blog-post-5</id><content type="html" xml:base="https://teng4.github.io/posts/20240923b/"><![CDATA[<p>(20240923).</p>

<h1 id="how-to-configure-unity-to-receive-ros-data-and-import-urdf-robot-model">How to configure Unity to receive ROS data and import URDF robot model?</h1>

<p>Suppose, you have already had a well-configured <code class="language-plaintext highlighter-rouge">catkin_ws</code> for publishing data to a ROS node (which will be subscribed by Unity later).</p>
<ul>
  <li>Require a <code class="language-plaintext highlighter-rouge">ROS-TCP-Endpoint</code> package in your <code class="language-plaintext highlighter-rouge">catkin_ws</code>.</li>
  <li>Require a <code class="language-plaintext highlighter-rouge">msg</code> folder where you defined your own message type, it should be in a specific package in your <code class="language-plaintext highlighter-rouge">catkin_ws</code>.</li>
</ul>

<h2 id="step-1-unity-add-packages-ros-tcp-visualizations">Step-1: Unity add packages (ROS-TCP; Visualizations)</h2>
<p>(<a href="https://github.com/Unity-Technologies/ROS-TCP-Connector">Source</a>)</p>

<p>Using Unity 2020.2 or later, open the Package Manager from <code class="language-plaintext highlighter-rouge">Window</code> –&gt; <code class="language-plaintext highlighter-rouge">Package Manager</code>.</p>

<p>In the <code class="language-plaintext highlighter-rouge">Package Manager</code> window, find and click the <code class="language-plaintext highlighter-rouge">+</code> button in the upper lefthand corner of the window. Select <code class="language-plaintext highlighter-rouge">Add package from git URL....</code>
Enter the git URL for the desired package. Note: you can append a version tag to the end of the git url, like <code class="language-plaintext highlighter-rouge">#v0.4.0</code> or <code class="language-plaintext highlighter-rouge">#v0.5.0</code>, to declare a specific package version, or exclude the tag to get the latest from the package’s main branch.</p>

<ul>
  <li>
    <p>For the ROS-TCP-Connector, enter <code class="language-plaintext highlighter-rouge">https://github.com/Unity-Technologies/ROS-TCP-Connector.git?path=/com.unity.robotics.ros-tcp-connector</code>.</p>
  </li>
  <li>
    <p>For Visualizations, enter <code class="language-plaintext highlighter-rouge">https://github.com/Unity-Technologies/ROS-TCP-Connector.git?path=/com.unity.robotics.visualizations</code>. Note that visualization is for visualizing the ROS connection etc (e.g., an icon displays the ROS connection status).</p>
  </li>
  <li>
    <p>Click <code class="language-plaintext highlighter-rouge">Add</code>.</p>
  </li>
</ul>

<h2 id="step-2-unity-add-packages-urdf-importer">Step-2: Unity add packages (URDF-Importer)</h2>
<p>(<a href="https://github.com/Unity-Technologies/URDF-Importer">Source</a>)</p>

<p>Enter the git URL for the URDF Importer with the latest version tag (currently v0.5.2) <code class="language-plaintext highlighter-rouge">https://github.com/Unity-Technologies/URDF-Importer.git?path=/com.unity.robotics.urdf-importer#v0.5.2</code> in the text box and press <code class="language-plaintext highlighter-rouge">Enter</code>.</p>

<h2 id="step-3-generate-corresponding-msg-in-unity">Step-3: Generate corresponding msg in Unity.</h2>

<p>In Unity top menu, find <code class="language-plaintext highlighter-rouge">Robotics</code> –&gt; <code class="language-plaintext highlighter-rouge">Generate ROS Message</code>.</p>

<ul>
  <li>
    <p>For <code class="language-plaintext highlighter-rouge">ROS message path</code>, click <code class="language-plaintext highlighter-rouge">Browse</code> to find the msg definitions folder in your specific <code class="language-plaintext highlighter-rouge">catkin_ws</code>, where the <code class="language-plaintext highlighter-rouge">ROS-TCP-Endpoint</code> package is also installed. Either use the <code class="language-plaintext highlighter-rouge">msg</code> folder or its package folder in <code class="language-plaintext highlighter-rouge">catkin_ws</code> both OK, it does not matter, will get the same results.</p>
  </li>
  <li>
    <p>For <code class="language-plaintext highlighter-rouge">Built message path</code>, click <code class="language-plaintext highlighter-rouge">Browse</code> to find the folder where you want to contain the build msg results. By default, it will be using ‘unityROSproject_test1/Assets/RosMessages/Mynewpkg/msg/Arm_StateMsg.cs’</p>
  </li>
</ul>

<h2 id="step-4-unity-add-a-script-to-receive-ros-data">Step-4: Unity add a script to receive ROS data.</h2>

<ul>
  <li>Add a <code class="language-plaintext highlighter-rouge">Scripts</code> folder inside folder <code class="language-plaintext highlighter-rouge">Assets</code>.</li>
  <li>Inside <code class="language-plaintext highlighter-rouge">Scripts</code> folder, create a new cs script (say, <code class="language-plaintext highlighter-rouge">ROSDataReceiver.cs</code>) to receive ROS data msg.</li>
  <li>Create an empty object in the scene, rename it as <code class="language-plaintext highlighter-rouge">ROSDataReceiver</code>, and attach the <code class="language-plaintext highlighter-rouge">ROSDataReceiver.cs</code> to it.</li>
  <li>Note, the received ROS data can be observed here when doing the testing later.</li>
</ul>

<p>Copy-paste the following code into <code class="language-plaintext highlighter-rouge">ROSDataReceiver.cs</code> for receiving data from ROS. (Note, this code allows Unity to receive ROS data without importing any URDF robot model.)</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using System;
using RosMessageTypes.Geometry;
using RosMessageTypes.Teng4pkg; //teng4note, Change to your own package folder name here! (find it in Assets/RosMessages/)
using Unity.Robotics;
using Unity.Robotics.ROSTCPConnector;
using Unity.Robotics.ROSTCPConnector.ROSGeometry;
using Unity.Robotics.UrdfImporter;
using Unity.Robotics.UrdfImporter.Control;

public class ROSDataReceiver : MonoBehaviour
{
    //Unity.Robotics.UrdfImporter.Control.Controller controller; //note, this "controller" is the "Controller.cs" attached to the robot object.
    
    // Required for ROS communication
    [SerializeField]
    string m_SubscriberName = "/arm_target1"; //note, '/arm_target1' is ROS topic message in customized 'Arm_State.msg' type. [joint_1_angle,.., joint_2_angle].
    GameObject myRobot; //teng4note, this will be defined by selecting the robot object as its 'value'.
    public float data_received_a1 = 0.0f; //to display the received ROS data.
    public float data_received_a2 = 0.0f; //to display the received ROS data.
    public float data_received_a3 = 0.0f; //to display the received ROS data.
    public float data_received_a4 = 0.0f; //to display the received ROS data.
    public float data_received_a5 = 0.0f; //to display the received ROS data.
    public float data_received_a6 = 0.0f; //to display the received ROS data.
   
    // ROS Connector
    ROSConnection m_Ros;


    // Start is called before the first frame update
    void Start()
    {
        //Get ROS connection static instance
        m_Ros = ROSConnection.GetOrCreateInstance();
        
        //Get the Controller.cs component from robot object
        //controller = myRobot.GetComponent&lt;Controller&gt;(); //this retrieves the Controller.cs attached to the robot object.        
        //print("test, controller.name=: " + controller.name);
    }

    // Update is called once per frame
    void Update()
    {
        //Receiving data from ROS node.
        m_Ros.Subscribe&lt;Arm_StateMsg&gt;(m_SubscriberName, MsgDataProcessing);
    }

    void MsgDataProcessing(Arm_StateMsg dataMsg){ 
        data_received_a1 = (float)dataMsg.joint_1_angle; //to display the received ROS data.
        data_received_a2 = (float)dataMsg.joint_2_angle; //to display the received ROS data.
        data_received_a3 = (float)dataMsg.joint_3_angle; //to display the received ROS data.
        data_received_a4 = (float)dataMsg.joint_4_angle; //to display the received ROS data.
        data_received_a5 = (float)dataMsg.joint_5_angle; //to display the received ROS data.
        data_received_a6 = (float)dataMsg.joint_6_angle; //to display the received ROS data.   
        
        //controller.joint_1_target = (float)dataMsg.joint_1_angle; //to display the received ROS data.
        //controller.joint_2_target = (float)dataMsg.joint_2_angle; //to display the received ROS data.
        //controller.joint_3_target = (float)dataMsg.joint_3_angle; //to display the received ROS data.
        //controller.joint_4_target = (float)dataMsg.joint_4_angle; //to display the received ROS data.
        //controller.joint_5_target = (float)dataMsg.joint_5_angle; //to display the received ROS data.
        //controller.joint_6_target = (float)dataMsg.joint_6_angle; //to display the received ROS data.           
    }
}
</code></pre></div></div>

<h2 id="test-unity-receives-ros-data">Test Unity receives ROS data.</h2>

<p>In Terminal(s), do the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ roscore

$ echo $ROS_PACKAGE_PATH
$ cd catkin_ws2
$ source devel/setup.bash
$ echo $ROS_PACKAGE_PATH
$ roslaunch ros_tcp_endpoint endpoint.launch tcp_ip:=127.0.0.1 tcp_port:=10000 //this will launch the bridge between ROS and Unity.

$ echo $ROS_PACKAGE_PATH
$ cd catkin_ws2
$ source devel/setup.bash
$ echo $ROS_PACKAGE_PATH
$ roslaunch omni_common omni_state4.launch //this will publish data to ROS. And now the received data in Unity can be observed in “ROSDataReceiver” object.
</code></pre></div></div>

<h2 id="error-debugging">Error Debugging.</h2>

<p>There may have the following warning message when you try to modify the <code class="language-plaintext highlighter-rouge">Controller.cs</code>.</p>

<blockquote>
  <p>The following asset(s) located in immutable packages were unexpectedly altered. Making changes to immutable packages isn’t recommended because the changes can be lost without any warning during Package Manager operations.
 Packages/com.unity.robotics.urdf-importer/Runtime/Controller/Controller.cs</p>
</blockquote>

<p><strong>Soulution</strong></p>
<ol>
  <li>Copy-paste the <code class="language-plaintext highlighter-rouge">com.unity.robotics.urdf-importer@90f353e435</code> from <code class="language-plaintext highlighter-rouge">youUnityProj/Library/PackageCache/</code> to folder <code class="language-plaintext highlighter-rouge">youUnityProj/Packages</code>.</li>
  <li>Or, Create a new script, say <code class="language-plaintext highlighter-rouge">CustomController.cs</code> to inherit the original Controller class in <code class="language-plaintext highlighter-rouge">Controller.cs</code>, then modify in the new script.</li>
</ol>

<p><strong>Example of the <code class="language-plaintext highlighter-rouge">CustomController.cs</code> (not tested).</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>using Unity.Robotics.UrdfImporter; // Import the original package namespace
using UnityEngine;

// Inherit from the existing Controller class
public class CustomController : Controller
{
    // Example of overriding a method from the original Controller class
    protected override void Start()
    {
        // Call the base Start method from Controller
        base.Start();
        
        // Add custom functionality here
        Debug.Log("Custom controller initialization");
    }

    // Add any new methods or override other methods from Controller.cs here
    public void CustomMethod()
    {
        Debug.Log("This is a custom method in the custom controller.");
    }
}
</code></pre></div></div>

<h2 id="notes">Notes.</h2>

<ul>
  <li>Originally, <code class="language-plaintext highlighter-rouge">com.unity.robotics.urdf-importer@90f353e435</code> is located in path <code class="language-plaintext highlighter-rouge">youUnityProj/Library/PackageCache/</code>. You can copy-paste it from <code class="language-plaintext highlighter-rouge">Library/PackageCache</code> to folder <code class="language-plaintext highlighter-rouge">Packages</code> to avoid system warning message. (Note that, in it, a <code class="language-plaintext highlighter-rouge">Controller</code> folder with several controller scripts need to be modified for your own use.)</li>
  <li>When importing a URDF tool/robot, a “Controller.cs” will be attached to the robot object automatically.</li>
</ul>

<hr />
<p>Created on 2024-09-23.</p>]]></content><author><name>Teng Li</name><email>tengli.li@utoronto.ca</email></author><category term="ROS" /><category term="Unity" /><category term="URDF" /><summary type="html"><![CDATA[(20240923).]]></summary></entry><entry><title type="html">How To get rotation matrix from RPY angles?</title><link href="https://teng4.github.io/posts/2024/09/blog-post-20240912-1/" rel="alternate" type="text/html" title="How To get rotation matrix from RPY angles?" /><published>2024-09-12T00:00:00-07:00</published><updated>2024-09-12T00:00:00-07:00</updated><id>https://teng4.github.io/posts/2024/09/blog-post-1</id><content type="html" xml:base="https://teng4.github.io/posts/2024/09/blog-post-20240912-1/"><![CDATA[<p>(20240912).</p>

<h1 id="how-to-get-rotation-matrix-from-rpy-angles">How To get rotation matrix from RPY angles</h1>

<h2 id="summary">Summary</h2>

<ul>
  <li>(1) The resulted matrix <code class="language-plaintext highlighter-rouge">R_A_B</code> is (new {B} expressed in old {A} frame), which means it may need <strong>transpose</strong> <code class="language-plaintext highlighter-rouge">R_A_B</code> first in order to calculate the position xyz in new {B} frame (e.g., <code class="language-plaintext highlighter-rouge">P_B = R_B_A * P_A</code>).</li>
  <li>(2) Roll-X, Pitch-Y, Yaw-Z, always true.</li>
  <li>(3) Positive/negative rotation angles are based on right-hand rule, correct.</li>
  <li>(4) ZYX-euler angles are first rotate around Z, then Y, then X. (Remember, ZYX each time always rotate the updated frame.)’)</li>
  <li>(5) ZYX-euler angles are SAME as the RPY where Z(yaw)Y(pitch)X(roll).</li>
</ul>

<h2 id="code-matlab">Code (MATLAB)</h2>

<p>The following MATLAB code help you calculate rotation matrix (3x3) from RPY angles.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>%teng4, this file calculate robot rotation matrix (3x3) based on the
%ZYX-Euler angles.
% from ECE464 ppt 4.2

% ZYX Euler angles are defined as
% (1) First,rotate reference frame {A.old} 'alpha' around Z-axis;
% (2) then, rotate the rotated frame 'beta' around its Y-axis;
% (3) then, rotate the latest frame 'gamma' around its X-axis, results is new frame {B};
% frame{A} is old, and frame{B} is new.
%R_A_B = Rzprime(alpha) * Ryprime(beta) * Rxprime(gamma).
% = [ca, -sa, 0;
%    sa,  ca, 0;
%     0,   0, 1] *
% [cb, 0, sb;
%   0, 1,  0;
% -sb, 0, cb] *
% [1,  0,   0;
%  0, cg, -sg;
%  0, sg,  cg]
%= [ ca.cb, ca.sb.sg-sa.cg, ca.sb.cg+sa.sg;
%    sa.cb, sa.sb.sg+ca.cg, sa.sb.cg-ca.sg;
%      -sb,          cb.sg,          cb.cg]

% Note that, this ZYX-Euler transformation is exactly same as that obtained
% for the same three rotations in the opposite order about the fixed
% angles (i.e., XYZ fixed angles)!
% Rzprime*yprime*xprime(alpha,beta,gamma) = Rxyz(gamma, beta, alpha)
% = Rzprime(alpha) * Ryprime(beta) * Rxprime(gamma)
% = Rz(alpha) * Ry(beta) * Rx(gamma)

roll_x  = -pi/2; %angles around x-axis.
pitch_y = 0;     %angles around y-axis.
yaw_z   = 0;     %angles around z-axis.

%based on ZYX-Euler angles above, we have,
gamma = roll_x;
beta  = pitch_y;
alpha = yaw_z;

g = gamma;
b = beta;
a = alpha;

R_A_B = [ cos(a)*cos(b), cos(a)*sin(b)*sin(g)-sin(a)*cos(g), cos(a)*sin(b)*cos(g)+sin(a)*sin(g);
          sin(a)*cos(b), sin(a)*sin(b)*sin(g)+cos(a)*cos(g), sin(a)*sin(b)*cos(g)-cos(a)*sin(g);
                -sin(b),                      cos(b)*sin(g),                     cos(b)*cos(g)];
%

disp('ZYX-Euler angles -   yaw(z),pitch(y); roll(x)')
disp('ZYX-Euler angles - alpha(z), beta(y);gamma(x)')
disp([alpha, beta, gamma])
disp('R_A_B =: ')
disp(R_A_B)

disp('----------')
disp('teng4 important note: (2024-09-12)')
disp('(1) The resulted matrix R_A_B is (new {B} expressed in old {A} frame),')
disp('      means it may need transpose R_A_B to calculate xyz in new frame.')
disp('(2) Roll-X, Pitch-Y, Yaw-Z, always true.')
disp('(3) Positive/negative rotation angles are based on right-hand rule, correct.')
disp('(4) ZYX-euler angles are first rotate around Z, then Y, then X.')
disp('      (Remember, ZYX each time always rotate around the updated frame.)')
disp('(5) ZYX-euler angles are SAME as the RPY where Z(yaw)Y(pitch)X(roll).')
</code></pre></div></div>

<hr />
<p>Created on 2024-09-12.</p>]]></content><author><name>Teng Li</name><email>tengli.li@utoronto.ca</email></author><category term="rpy" /><category term="rotation" /><category term="zyx" /><summary type="html"><![CDATA[(20240912).]]></summary></entry><entry><title type="html">How To create a customized ROS msg in a [catkin_ws]?</title><link href="https://teng4.github.io/posts/20240911/" rel="alternate" type="text/html" title="How To create a customized ROS msg in a [catkin_ws]?" /><published>2024-09-11T00:00:00-07:00</published><updated>2024-09-11T00:00:00-07:00</updated><id>https://teng4.github.io/posts/blog-post-2</id><content type="html" xml:base="https://teng4.github.io/posts/20240911/"><![CDATA[<p>(20240911).</p>

<h1 id="how-to-create-a-customized-ros-msg-in-a-catkin_ws">How To create a customized ROS msg in a <code class="language-plaintext highlighter-rouge">catkin_ws</code>?</h1>

<p>In ROS, custom message types are defined using <code class="language-plaintext highlighter-rouge">.msg</code> files, which are stored in a package’s msg folder. These message types allow you to define custom data structures that can be used for inter-node communication. Below is a guide on how to create and use custom message types in a <code class="language-plaintext highlighter-rouge">catkin_ws</code> workspace.</p>

<p>Steps to Customize a msg Folder and Files in a <code class="language-plaintext highlighter-rouge">catkin_ws</code>.</p>

<h2 id="1-create-a-ros-package">1. Create a ROS Package</h2>

<p>First, create a package in your <code class="language-plaintext highlighter-rouge">catkin_ws</code> workspace. This package will hold your custom messages.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd ~/catkin_ws/src
catkin_create_pkg teng4pkg_msgs std_msgs rospy roscpp
</code></pre></div></div>

<p>Here, <code class="language-plaintext highlighter-rouge">teng4pkg_msgs</code> is the package name, and it depends on <code class="language-plaintext highlighter-rouge">std_msgs</code>, <code class="language-plaintext highlighter-rouge">rospy</code>, and <code class="language-plaintext highlighter-rouge">roscpp</code>.</p>

<h2 id="2-create-a-msg-directory">2. Create a <code class="language-plaintext highlighter-rouge">msg</code> Directory</h2>

<p>Inside your package, create a <code class="language-plaintext highlighter-rouge">msg</code> folder where your custom message types will be defined.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd ~/catkin_ws/src/teng4pkg_msgs
mkdir msg
</code></pre></div></div>

<h2 id="3-create-a-msg-file">3. Create a ‘.msg’ File</h2>

<p>Inside the <code class="language-plaintext highlighter-rouge">msg</code> folder, create a file with a <code class="language-plaintext highlighter-rouge">.msg</code> extension. This file defines your custom message type.</p>

<p>Example: <code class="language-plaintext highlighter-rouge">PoseCartesian6DOF.msg</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gedit msg/PoseCartesian6DOF.msg
</code></pre></div></div>

<p>Inside <code class="language-plaintext highlighter-rouge">PoseCartesian6DOF.msg</code>, define the structure of the message. For example, for a 6D cartesian pose:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>float64 x
float64 y
float64 z
float64 a
float64 b
float64 g
</code></pre></div></div>

<p>Each line defines a field in the message, with the type followed by the field name.</p>

<h2 id="4-modify-cmakeliststxt">4. Modify <code class="language-plaintext highlighter-rouge">CMakeLists.txt</code></h2>

<p>You need to modify your package’s <code class="language-plaintext highlighter-rouge">CMakeLists.txt</code> file to include the message generation.</p>

<ul>
  <li>Find and uncomment (or add) <code class="language-plaintext highlighter-rouge">message_generation</code> as a required component in <code class="language-plaintext highlighter-rouge">find_package</code>:</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)
</code></pre></div></div>

<ul>
  <li>Add the custom message files under <code class="language-plaintext highlighter-rouge">add_message_files</code>:</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>add_message_files(
  FILES
  PoseCartesian6DOF.msg
)
</code></pre></div></div>

<ul>
  <li>Add the following after <code class="language-plaintext highlighter-rouge">add_message_files()</code> to actually generate the messages. Add <code class="language-plaintext highlighter-rouge">generate_messages()</code> and ensure <code class="language-plaintext highlighter-rouge">std_msgs</code> and other dependencies are listed:</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>generate_messages(
  DEPENDENCIES
  std_msgs
)
</code></pre></div></div>

<ul>
  <li>Ensure the <code class="language-plaintext highlighter-rouge">catkin_package</code> includes <code class="language-plaintext highlighter-rouge">message_runtime</code>:</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>catkin_package(
  CATKIN_DEPENDS message_runtime std_msgs
)
</code></pre></div></div>

<h2 id="5-modify-packagexml">5. Modify <code class="language-plaintext highlighter-rouge">package.xml</code></h2>

<p>In your package’s <code class="language-plaintext highlighter-rouge">package.xml</code>, ensure you have the following dependencies for message generation:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;build_depend&gt;message_generation&lt;/build_depend&gt;
&lt;exec_depend&gt;message_runtime&lt;/exec_depend&gt;
</code></pre></div></div>

<h2 id="6-build-the-package">6. Build the Package</h2>

<p>After modifying the files, go back to the root of your <code class="language-plaintext highlighter-rouge">catkin_ws</code> workspace and build the workspace.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd ~/catkin_ws
catkin_make
</code></pre></div></div>

<p>If everything is set up correctly, the custom message types will be generated and ready for use.</p>

<h2 id="7-using-the-custom-message">7. Using the Custom Message</h2>

<p>Now, you can use your custom message type in your ROS nodes.</p>

<p><strong>C++ Example:</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// teng4note, the Position.h may not be easy to find or even not exist. But it does not matter.
#include "teng4pkg_msgs/PoseCartesian6DOF.h"

// Publishing a custom message
teng4pkg_msgs::PoseCartesian6DOF test1_msg;
test1_msg.x = 1.11;
test1_msg.y = 1.11;
test1_msg.z = 1.11;
test1_msg.a = 1.11;
test1_msg.b = 1.11;
test1_msg.g = 1.11;
pub_test1_pose6dof.publish(test1_msg);
</code></pre></div></div>

<p><strong>Python Example:</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>from teng4pkg_msgs.msg import PoseCartesian6DOF

# Publishing a custom message
test1_msg = Position()
test1_msg.x = 1.11;
test1_msg.y = 1.11;
test1_msg.z = 1.11;
test1_msg.a = 1.11;
test1_msg.b = 1.11;
test1_msg.g = 1.11;
</code></pre></div></div>

<h2 id="8-verify-the-custom-message">8. Verify the Custom Message</h2>

<p>Now, we can verify the custom message in a terminal. First you need to run your file.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>echo $ROS_PACKAGE_PATH

// May need to source the Workspace Again
source devel/setup.bash

// note, the Terminal needs `cd` to the correct catkin_ws, in order to be able to echo the customized msg.
cd ~/catkin_ws
rostopic list
rostopic list -v

// Check Message Availability, suppose msg route is /teng4pkg_msgs/PoseCartesian6DOF
rosmsg show teng4pkg_msgs/PoseCartesian6DOF

// echo the rostopic, suppose the publisher is /teng4/test1_pose6dof
rostopic echo /teng4/test1_pose6dof

// some other cmd for retrieving the ROS param.
rosparam list
rosparam get /PSM1/rate
cd catkin_ws
echo $ROS_PACKAGE_PATH
source devel/setup.bash
</code></pre></div></div>

<h2 id="9-error-debugging">9. Error Debugging.</h2>

<p>You may encounter some errors when <code class="language-plaintext highlighter-rouge">rostopic echo /your_topic</code> the customized ROS msg. For example,</p>

<blockquote>
  <p>$ rostopic echo /teng4/test1_pose6dof</p>

  <p>ERROR: Cannot load message class for [teng4pkg_msgs/PoseCartesian6DOF]. Are your messages built?</p>

  <p><strong>SOLUTION:</strong>
This error is caused by wrong catkin_ws in terminal. You need to get into the correct catkin_ws2 ($cd catkin_ws2; $cd ~/catkin_ws2), and then “rostopic echo /teng4/test1_pose6dof “ will work.</p>

  <p>$ cd ~/catkin_ws2</p>

  <p>$ source devel/setup.bash</p>

  <p>$ echo $ROS_PACKAGE_PATH</p>

  <p>$ rostopic list</p>

  <p>$ rostopic echo /teng4/test1_pose6dof</p>
</blockquote>

<h2 id="summary">Summary</h2>

<ul>
  <li>Create a ROS package in <code class="language-plaintext highlighter-rouge">catkin_ws/src</code>.</li>
  <li>Create a <code class="language-plaintext highlighter-rouge">msg</code> directory and define your custom <code class="language-plaintext highlighter-rouge">.msg</code> file.</li>
  <li>Update the <code class="language-plaintext highlighter-rouge">CMakeLists.txt</code> and <code class="language-plaintext highlighter-rouge">package.xml</code> to include message generation.</li>
  <li>Build your workspace with <code class="language-plaintext highlighter-rouge">catkin_make</code>.</li>
  <li>Use the custom message in your ROS nodes for inter-node communication.</li>
</ul>

<p>This setup allows you to define custom data structures for your ROS applications.</p>

<hr />
<p>Created on 2024-09-11.</p>]]></content><author><name>Teng Li</name><email>tengli.li@utoronto.ca</email></author><category term="ROS" /><category term="msg" /><category term="catkin" /><summary type="html"><![CDATA[(20240911).]]></summary></entry></feed>