<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/feed.xml" rel="self" type="application/atom+xml"/><link href="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/" rel="alternate" type="text/html" hreflang="en"/><updated>2026-08-06T12:40:00+00:00</updated><id>https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/feed.xml</id><title type="html">Ashwani Kumar Moudgil: GSoC X JdeRobot 2026</title><subtitle>GSoC 2026 blog — work, weekly updates, and notes from my project on RoboticsAcademy @ JdeRobot. </subtitle><entry><title type="html">Week 12 — One workcell, many palletizing patterns</title><link href="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week12-sku-palletizing-patterns/" rel="alternate" type="text/html" title="Week 12 — One workcell, many palletizing patterns"/><published>2026-08-04T00:00:00+00:00</published><updated>2026-08-04T00:00:00+00:00</updated><id>https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week12-sku-palletizing-patterns</id><content type="html" xml:base="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week12-sku-palletizing-patterns/"><![CDATA[<blockquote> <p><em>GSoC 2026 · Week 12 · Coding period</em></p> </blockquote> <h1 id="week-12-one-workcell-many-palletizing-patterns">Week 12: One workcell, many palletizing patterns</h1> <p>Earlier in the project, the palletizing setup proved an important first point: the UR10 could pick a box from the conveyor and stack it on a pallet.</p> <p>This week, the question changed from <strong>can the robot stack boxes?</strong> to <strong>what pallet layout should it build?</strong></p> <p>The result is a more interesting exercise direction. The same UR10, suction cup, conveyor, and SKU box pipeline can now produce different palletizing outcomes by changing the placement strategy rather than rebuilding the workcell.</p> <hr/> <h2 id="from-a-fixed-stack-to-a-palletizing-decision">From a fixed stack to a palletizing decision</h2> <p>The first stack was intentionally simple. That was useful for proving the complete manipulation loop, but it did not leave much for a student to decide.</p> <p>The updated setup works with named box families, each carrying known dimensions. The planner receives the current box information and the usable pallet area, then decides where that box belongs.</p> <p>In simple terms, the loop is now:</p> <div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>incoming SKU box
      ↓
box dimensions + usable pallet area
      ↓
choose a palletizing pattern and target
      ↓
robot picks and places the box
</code></pre></div></div> <p>The simulator, conveyor coordination, suction attachment, and robot motion stay behind the exercise helpers. The interesting part for the learner is the placement decision.</p> <hr/> <h2 id="one-setup-several-pallet-patterns">One setup, several pallet patterns</h2> <p>The main achievement this week was turning the same physical workcell into a small pattern gallery.</p> <p>The robot does not need a new arm, a new conveyor, or a new simulator scene for each result. The difference comes from the planner: how it chooses rows, layers, directions, and orientations for the incoming SKU boxes.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/gsoc2026-Ashwani_Kumar/assets/img/w12-row-shelf-sku-480.webp 480w,/gsoc2026-Ashwani_Kumar/assets/img/w12-row-shelf-sku-800.webp 800w,/gsoc2026-Ashwani_Kumar/assets/img/w12-row-shelf-sku-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/gsoc2026-Ashwani_Kumar/assets/img/w12-row-shelf-sku.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <h3 id="row--shelf-packing">Row / shelf packing</h3> <p>The baseline strategy fills the available pallet area row by row. It is the most approachable starting point for SKU palletizing: a longer box consumes more row length, while a smaller one can use the remaining space.</p> <p>This gives students a clear first algorithm without asking them to solve a full industrial optimization problem.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/gsoc2026-Ashwani_Kumar/assets/img/w12-brick-layer-sku-480.webp 480w,/gsoc2026-Ashwani_Kumar/assets/img/w12-brick-layer-sku-800.webp 800w,/gsoc2026-Ashwani_Kumar/assets/img/w12-brick-layer-sku-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/gsoc2026-Ashwani_Kumar/assets/img/w12-brick-layer-sku.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <h3 id="brick-inspired-alternating-layers">Brick-inspired alternating layers</h3> <p>The next strategy changes the direction used to fill each layer. Instead of repeating exactly the same seams above one another, the planner starts the next layer from the opposite side.</p> <p>This is a useful introduction to the idea that pallet patterns are not only about fitting boxes inside a boundary. They also influence how seams, support, and visual structure are distributed through the stack.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/gsoc2026-Ashwani_Kumar/assets/img/w12-rotated-block-sku-480.webp 480w,/gsoc2026-Ashwani_Kumar/assets/img/w12-rotated-block-sku-800.webp 800w,/gsoc2026-Ashwani_Kumar/assets/img/w12-rotated-block-sku-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/gsoc2026-Ashwani_Kumar/assets/img/w12-rotated-block-sku.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <h3 id="rotated-block-layout">Rotated block layout</h3> <p>The rotated-block pattern adds orientation as a planning decision. Some boxes are placed with a rotated footprint, changing the block structure without changing the feeder or robot setup.</p> <p>This is an important step for the exercise because it connects a simple student decision—choose an orientation—to a visibly different pallet result.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/gsoc2026-Ashwani_Kumar/assets/img/w12-medium-pinwheel-full-480.webp 480w,/gsoc2026-Ashwani_Kumar/assets/img/w12-medium-pinwheel-full-800.webp 800w,/gsoc2026-Ashwani_Kumar/assets/img/w12-medium-pinwheel-full-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/gsoc2026-Ashwani_Kumar/assets/img/w12-medium-pinwheel-full.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <h3 id="full-medium-sku-pinwheel">Full MEDIUM-SKU pinwheel</h3> <p>The most visual pattern explored this week is a pinwheel-style layout. Rectangular MEDIUM SKU boxes are arranged around a central opening, alternating horizontal and vertical placement directions to create a full spiral/perimeter layer.</p> <p>The initial small ring was useful for checking the geometry. The fuller MEDIUM layout makes the intended pattern much clearer: a pallet layer can be designed as a deliberate structure, not merely a collection of boxes placed wherever space is available.</p> <hr/> <h2 id="making-physical-placement-match-the-plan">Making physical placement match the plan</h2> <p>Pattern generation only matters if the physical placement matches the intended layout.</p> <p>One of the important improvements this week was making pickup depend on the box’s actual stopped position on the conveyor. Earlier, the robot assumed an ideal pickup point. In practice, simulation physics can leave a box slightly away from that assumed location, which is enough to create placement drift later in the stack.</p> <p>The feeder now observes the active box position before announcing it as ready. The pickup helper then uses that observed pose to bring the suction contact to the top center of the box.</p> <p>This made the pattern experiments much more meaningful: the planner can reason about box centers, and the robot has a better chance of carrying and placing those centers consistently.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/gsoc2026-Ashwani_Kumar/assets/img/w12-sku-pallet-full-480.webp 480w,/gsoc2026-Ashwani_Kumar/assets/img/w12-sku-pallet-full-800.webp 800w,/gsoc2026-Ashwani_Kumar/assets/img/w12-sku-pallet-full-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/gsoc2026-Ashwani_Kumar/assets/img/w12-sku-pallet-full.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <hr/> <h2 id="keeping-the-workcell-reusable">Keeping the workcell reusable</h2> <p>The native launch workflow was also cleaned up so the workcell can be restarted more reliably between pattern experiments.</p> <p>The launcher now brings up the scene, UR10, MoveIt, RViz, conveyor bridge, and SKU feeder together, while cleaning stale processes from earlier native runs. This matters during pattern work: a layout failure should be traceable to the pattern or reach constraints, not to an old bridge or duplicate action server still running in the background.</p> <p>I also started reducing unnecessary background work in the simulation, especially while tracking the active conveyor box. That will matter more as patterns use longer sequences and larger stacks.</p> <hr/> <h2 id="what-i-learned-from-the-pattern-experiments">What I learned from the pattern experiments</h2> <p>A pallet pattern is not just a list of coordinates.</p> <p>The box footprint, approach direction, robot reach, pickup alignment, and available orientation all influence which layouts are practical. Some patterns are visually simple but require more careful geometry than expected. Others, such as row/shelf packing, are easy to describe but become more interesting when box sizes vary.</p> <p>That is exactly why this is a useful RoboticsAcademy exercise direction. Students can begin with a simple placement rule, then improve it by considering orientation, alternating layers, support, and pattern structure.</p> <hr/> <h2 id="whats-next">What’s next</h2> <p>The current work will continue beyond this pattern gallery.</p> <p>The next focus areas are:</p> <ul> <li>validating and refining the larger pinwheel layout at all of its outer positions,</li> <li>making longer pattern runs more predictable under simulation load,</li> <li>improving recovery when a robot movement cannot be completed,</li> <li>and extending the exercise from palletizing toward <strong>depalletizing</strong>: planning how a robot can safely remove boxes from an existing stack.</li> </ul> <p>The workcell is now becoming a palletizing exercise rather than one fixed stacking demo. The next step is to keep turning these layouts into reliable, reusable learning activities.</p> <p>Until next week 👋</p>]]></content><author><name></name></author><category term="updates"/><category term="gsoc"/><category term="roboticsacademy"/><category term="palletizing"/><category term="gazebo-harmonic"/><category term="ros2"/><category term="moveit"/><summary type="html"><![CDATA[This week the palletizing exercise progressed from one fixed stack into an SKU-based pattern playground. The same UR10, conveyor, and box metadata pipeline now support row, brick, rotated-block, and pinwheel-style pallet layouts.]]></summary></entry><entry><title type="html">Week 11 — A slower week and more SKU planning</title><link href="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week11-slow-week-sku-exploration/" rel="alternate" type="text/html" title="Week 11 — A slower week and more SKU planning"/><published>2026-07-28T00:00:00+00:00</published><updated>2026-07-28T00:00:00+00:00</updated><id>https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week11-slow-week-sku-exploration</id><content type="html" xml:base="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week11-slow-week-sku-exploration/"><![CDATA[<blockquote> <p><em>GSoC 2026 · Week 11 · Coding period</em></p> </blockquote> <h1 id="week-11-a-slower-week-and-more-sku-planning">Week 11: A slower week and more SKU planning</h1> <p>This week was not a normal coding week. I was ill, so I could not put in the same amount of implementation time as the previous weeks.</p> <p>Instead of forcing a half-finished feature and pretending it was a milestone, I mostly used the limited time I had to review the SKU-based palletizing direction from last week and think through what should come next.</p> <hr/> <h2 id="revisiting-the-scope">Revisiting the scope</h2> <p>The main question was how ambitious the first SKU version should be.</p> <p>It is tempting to jump straight to a rich palletizing problem: mixed dimensions, rotations, interlocked layers, dynamic collision objects, placed-box tracking, and maybe even batch planning over the full sequence. But that would make the first student version too heavy.</p> <p>The better first step is still the smaller one:</p> <ul> <li>boxes arrive online, one at a time,</li> <li>each box has a known SKU and dimensions,</li> <li>the pallet exposes a safe usable area,</li> <li>all first-version boxes keep the same height and width,</li> <li>the student writes a simple row/shelf placement algorithm,</li> <li>and the backend hides the simulator and planning-scene details.</li> </ul> <p>That keeps the exercise educational rather than overwhelming. A student can understand why <code class="language-plaintext highlighter-rouge">LONG</code> takes more row space than <code class="language-plaintext highlighter-rouge">SMALL</code>, and can improve the placement strategy later.</p> <hr/> <h2 id="keeping-the-hal-boundary-clean">Keeping the HAL boundary clean</h2> <p>I also spent time thinking about the API boundary.</p> <p>The easy but wrong path would be to expose backend mechanics directly: let the student add collision boxes to MoveIt, detach named Gazebo links, or manipulate planning-scene primitives. That would technically work for a demo, but it would teach the wrong layer of abstraction.</p> <p>The exercise should expose palletizing concepts, not simulator plumbing.</p> <p>So the direction remains a simple chain:</p> <div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/box_ready tells us which box arrived
        ↓
GetBoxInfo(name) gives SKU + dimensions
        ↓
GetPalletInfo() gives usable pallet area
        ↓
student computes the placement target
        ↓
HAL handles motion, suction, and feeder coordination
</code></pre></div></div> <p>For example, the student should think in terms of data like this:</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"box_412_0"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"sku"</span><span class="p">:</span><span class="w"> </span><span class="s2">"LONG"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"size"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mf">0.60</span><span class="p">,</span><span class="w"> </span><span class="mf">0.30</span><span class="p">,</span><span class="w"> </span><span class="mf">0.20</span><span class="p">],</span><span class="w">
  </span><span class="nl">"mass"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.5</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div> <p>and pallet limits like this:</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"frame"</span><span class="p">:</span><span class="w"> </span><span class="s2">"base_link"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"usable_size"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mf">1.20</span><span class="p">,</span><span class="w"> </span><span class="mf">0.90</span><span class="p">],</span><span class="w">
  </span><span class="nl">"top_z"</span><span class="p">:</span><span class="w"> </span><span class="mf">-0.60</span><span class="p">,</span><span class="w">
  </span><span class="nl">"max_layers"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div> <p>Then their job is to answer: “where should this box go?”</p> <p>If placed boxes need to become collision objects later, that should happen behind a semantic call such as registering a completed placement, not through a raw <code class="language-plaintext highlighter-rouge">add_collision_box(...)</code> API in the student template.</p> <hr/> <h2 id="what-still-needs-work">What still needs work</h2> <p>The main unresolved part is runtime reliability with SKU boxes.</p> <p>The old fixed-box demo was predictable because every placement was known ahead of time. SKU palletizing changes that. The robot still needs clean approach and retreat motions, but the stack is no longer just a fixed grid of identical cartons. The planning side needs a better story for the carried box and the growing stack.</p> <p>From last week, the attached-collision-object experiment was not stable enough to keep, so the next attempt has to be more careful. I would rather keep the current HAL simple and stable than add a collision feature that causes more planning failures.</p> <p>The next practical steps are:</p> <ol> <li>Rebuild and retest the Infrastructure feeder split in the launch flow.</li> <li>Confirm the <code class="language-plaintext highlighter-rouge">/box_info</code> and <code class="language-plaintext highlighter-rouge">/pallet_info</code> metadata arrive correctly in Academy.</li> <li>Run the row/shelf reference solution end to end.</li> <li>Decide whether the first SKU demo should use conservative motion and a simplified usable area, or wait for a stronger internal collision manager.</li> </ol> <hr/> <h2 id="whats-next">What’s next</h2> <p>Next week I want to get back to implementation and testing: stabilize the SKU-based runtime, make the row/shelf placement path reliable enough for a demo, and keep the student-facing code focused on the actual palletizing algorithm.</p> <p>Until next week 👋</p>]]></content><author><name></name></author><category term="updates"/><category term="gsoc"/><category term="roboticsacademy"/><category term="gazebo-harmonic"/><category term="ros2"/><category term="moveit"/><summary type="html"><![CDATA[This was a lighter week because I was ill, so there was no major implementation milestone. I used the time I had to review the SKU-based palletizing direction, think through the student-facing API boundary, and plan the next collision and placement steps.]]></summary></entry><entry><title type="html">Week 10 — Starting SKU-based palletizing</title><link href="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week10-sku-based-palletizing/" rel="alternate" type="text/html" title="Week 10 — Starting SKU-based palletizing"/><published>2026-07-21T00:00:00+00:00</published><updated>2026-07-21T00:00:00+00:00</updated><id>https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week10-sku-based-palletizing</id><content type="html" xml:base="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week10-sku-based-palletizing/"><![CDATA[<blockquote> <p><em>GSoC 2026 · Week 10 · Coding period</em></p> </blockquote> <h1 id="week-10-starting-sku-based-palletizing">Week 10: Starting SKU-based palletizing</h1> <p>After the upstream sync, the next question was: what should the exercise actually teach beyond a fixed pick-and-place loop?</p> <p>The first version stacked eight identical boxes in a neat 2×2×2 grid. That was useful because it proved the conveyor, UR10, suction, MoveIt, and HAL all worked together. But palletizing becomes interesting when the placement is not just “next hardcoded coordinate”. Real palletizing is about deciding where a box should go based on its size, the available space, the current stack, and the pattern you want to build.</p> <p>So this week I started shifting the exercise toward <strong>SKU-based palletizing</strong>.</p> <hr/> <h2 id="from-fixed-boxes-to-sku-families">From fixed boxes to SKU families</h2> <p>Instead of jumping straight to arbitrary random dimensions, I started with a constrained setup: a few named box families, or SKUs.</p> <p>The first planned set is:</p> <table> <thead> <tr> <th>SKU</th> <th style="text-align: right">Size <code class="language-plaintext highlighter-rouge">[length, width, height]</code></th> <th style="text-align: right">Mass</th> </tr> </thead> <tbody> <tr> <td><code class="language-plaintext highlighter-rouge">SMALL</code></td> <td style="text-align: right"><code class="language-plaintext highlighter-rouge">[0.30, 0.30, 0.20]</code> m</td> <td style="text-align: right"><code class="language-plaintext highlighter-rouge">1.5</code> kg</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">MEDIUM</code></td> <td style="text-align: right"><code class="language-plaintext highlighter-rouge">[0.40, 0.30, 0.20]</code> m</td> <td style="text-align: right"><code class="language-plaintext highlighter-rouge">2.0</code> kg</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">LONG</code></td> <td style="text-align: right"><code class="language-plaintext highlighter-rouge">[0.60, 0.30, 0.20]</code> m</td> <td style="text-align: right"><code class="language-plaintext highlighter-rouge">2.5</code> kg</td> </tr> </tbody> </table> <p>The important simplification is that all boxes have the same height and width for now. Only the length changes.</p> <p>That keeps the first student algorithm approachable. Instead of solving a full 3D packing problem, the student can think in rows: choose a layer, choose a row, and find the first free slot where the current box fits. Rotation, interlocked layers, occupancy grids, and batch optimization can come later. The first version should teach the shape of the problem without turning into an industrial optimizer.</p> <p>The physical pallet is still <code class="language-plaintext highlighter-rouge">1.60 × 1.30 × 0.10</code> m, but the first SKU task exposes a smaller <code class="language-plaintext highlighter-rouge">1.20 × 0.90</code> m usable area inside it. That gives safety margin around the edges and keeps the targets inside a comfortable reach envelope for the UR10.</p> <hr/> <h2 id="the-task-now-lives-in-yaml">The task now lives in YAML</h2> <p>The old demo had a lot of geometry baked into the solution: box dimensions, target spacing, layer height, and the assumption that every object was the same.</p> <p>For SKU-based palletizing, that has to move out of the student solution. The task should describe what is arriving, and the student’s code should decide where to place it.</p> <p>So I added a task configuration file:</p> <div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>CustomRobots/palletizing/config/palletizing_task.yaml
</code></pre></div></div> <p>It is the single place that describes the first SKU task:</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">pallet</span><span class="pi">:</span>
  <span class="na">physical_size</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">1.60</span><span class="pi">,</span> <span class="nv">1.30</span><span class="pi">,</span> <span class="nv">0.10</span><span class="pi">]</span>
  <span class="na">usable_size</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">1.20</span><span class="pi">,</span> <span class="nv">0.90</span><span class="pi">]</span>
  <span class="na">max_layers</span><span class="pi">:</span> <span class="m">2</span>

<span class="na">boxes</span><span class="pi">:</span>
  <span class="na">skus</span><span class="pi">:</span>
    <span class="na">SMALL</span><span class="pi">:</span>
      <span class="na">size</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">0.30</span><span class="pi">,</span> <span class="nv">0.30</span><span class="pi">,</span> <span class="nv">0.20</span><span class="pi">]</span>
      <span class="na">mass</span><span class="pi">:</span> <span class="m">1.5</span>
    <span class="na">MEDIUM</span><span class="pi">:</span>
      <span class="na">size</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">0.40</span><span class="pi">,</span> <span class="nv">0.30</span><span class="pi">,</span> <span class="nv">0.20</span><span class="pi">]</span>
      <span class="na">mass</span><span class="pi">:</span> <span class="m">2.0</span>
    <span class="na">LONG</span><span class="pi">:</span>
      <span class="na">size</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">0.60</span><span class="pi">,</span> <span class="nv">0.30</span><span class="pi">,</span> <span class="nv">0.20</span><span class="pi">]</span>
      <span class="na">mass</span><span class="pi">:</span> <span class="m">2.5</span>

  <span class="na">sequence</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">LONG</span>
    <span class="pi">-</span> <span class="s">MEDIUM</span>
    <span class="pi">-</span> <span class="s">LONG</span>
    <span class="pi">-</span> <span class="s">SMALL</span>
    <span class="pi">-</span> <span class="s">LONG</span>
    <span class="pi">-</span> <span class="s">MEDIUM</span>
    <span class="pi">-</span> <span class="s">LONG</span>
    <span class="pi">-</span> <span class="s">SMALL</span>
</code></pre></div></div> <p>That sequence is important: this is <strong>online palletizing</strong>. The student does not receive the whole batch and optimize it ahead of time. One box arrives, the student places it, and then the next one arrives.</p> <p>Changing the exercise is now much easier. If I want to try a different SKU mix or pallet usable area, I can edit the YAML instead of rewriting the feeder logic.</p> <hr/> <h2 id="what-the-feeder-publishes">What the feeder publishes</h2> <p>The feeder now exposes simple JSON messages for the current box and the pallet.</p> <p>When a box reaches the pickup point, the feeder keeps publishing its name on <code class="language-plaintext highlighter-rouge">/box_ready</code>, same as before. Along with that, it publishes box metadata on <code class="language-plaintext highlighter-rouge">/box_info</code>.</p> <p>Example <code class="language-plaintext highlighter-rouge">/box_info</code> message:</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"box_412_0"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"sku"</span><span class="p">:</span><span class="w"> </span><span class="s2">"LONG"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"size"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mf">0.60</span><span class="p">,</span><span class="w"> </span><span class="mf">0.30</span><span class="p">,</span><span class="w"> </span><span class="mf">0.20</span><span class="p">],</span><span class="w">
  </span><span class="nl">"mass"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.5</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div> <p>The <code class="language-plaintext highlighter-rouge">name</code> is the actual Gazebo model name for this run. The <code class="language-plaintext highlighter-rouge">sku</code>, <code class="language-plaintext highlighter-rouge">size</code>, and <code class="language-plaintext highlighter-rouge">mass</code> are the semantic task data the student cares about.</p> <p>The pallet data is published separately on <code class="language-plaintext highlighter-rouge">/pallet_info</code>:</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"frame"</span><span class="p">:</span><span class="w"> </span><span class="s2">"base_link"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"size"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mf">1.60</span><span class="p">,</span><span class="w"> </span><span class="mf">1.30</span><span class="p">,</span><span class="w"> </span><span class="mf">0.10</span><span class="p">],</span><span class="w">
  </span><span class="nl">"usable_size"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mf">1.20</span><span class="p">,</span><span class="w"> </span><span class="mf">0.90</span><span class="p">],</span><span class="w">
  </span><span class="nl">"center"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="mf">-0.88</span><span class="p">,</span><span class="w"> </span><span class="mf">-0.90</span><span class="p">],</span><span class="w">
  </span><span class="nl">"top_z"</span><span class="p">:</span><span class="w"> </span><span class="mf">-0.60</span><span class="p">,</span><span class="w">
  </span><span class="nl">"max_layers"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div> <p>The key detail here is <code class="language-plaintext highlighter-rouge">frame: "base_link"</code>. The student solution and MoveIt commands work in the robot base frame, so the pallet metadata is translated into that same frame. The student should not have to know the Gazebo world offset of the robot base.</p> <p>In plain English, the messages mean:</p> <table> <thead> <tr> <th>Topic</th> <th>Meaning</th> </tr> </thead> <tbody> <tr> <td><code class="language-plaintext highlighter-rouge">/box_ready</code></td> <td>“This box is stopped at the pickup point.”</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/box_info</code></td> <td>“Here is the SKU, size, and mass of that box.”</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/pallet_info</code></td> <td>“Here is the pallet area you are allowed to fill.”</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/box_done</code></td> <td>“The robot has taken this box, feed the next one.”</td> </tr> </tbody> </table> <p>That is the data contract for the first SKU version.</p> <hr/> <h2 id="how-the-ros-flow-works">How the ROS flow works</h2> <p>The runtime loop is still intentionally simple:</p> <ol> <li><code class="language-plaintext highlighter-rouge">box_spawner</code> reads <code class="language-plaintext highlighter-rouge">palletizing_task.yaml</code>.</li> <li>It creates the next box from the SKU sequence.</li> <li>It writes a temporary SDF model with the correct size, mass, inertia, and color.</li> <li>It spawns that model in Gazebo using <code class="language-plaintext highlighter-rouge">ros_gz_sim create</code>.</li> <li>It starts the conveyor by publishing to <code class="language-plaintext highlighter-rouge">/conveyor/speed</code>.</li> <li>After the estimated travel time, it stops the conveyor at the pickup point.</li> <li>It publishes <code class="language-plaintext highlighter-rouge">/box_ready</code> and <code class="language-plaintext highlighter-rouge">/box_info</code> repeatedly.</li> <li>The Academy HAL receives the ready signal and metadata.</li> <li>The student solution computes a target pose and picks the box.</li> <li>The solution calls <code class="language-plaintext highlighter-rouge">HAL.BoxDone(name)</code>.</li> <li>The feeder checks that the name matches the expected box, then starts the next one.</li> </ol> <p>The name check matters. If a stale or wrong <code class="language-plaintext highlighter-rouge">/box_done</code> message arrives, the feeder ignores it instead of advancing the sequence incorrectly.</p> <p>Here is the same flow as a compact diagram:</p> <div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>YAML task
   ↓
box_spawner ROS node
   ↓ spawn SDF box
Gazebo + conveyor
   ↓ box reaches pickup
/box_ready + /box_info + /pallet_info
   ↓
Academy HAL
   ↓
student solution chooses target
   ↓
/box_done
   ↓
next box
</code></pre></div></div> <p>This keeps the simulator side and the student side separate. The feeder owns the world mechanics; the student owns the placement decision.</p> <hr/> <h2 id="files-and-responsibilities">Files and responsibilities</h2> <p>The feeder had become too much of a single large script, so I split the SKU work into smaller files with clearer jobs.</p> <table> <thead> <tr> <th>File</th> <th>Responsibility</th> </tr> </thead> <tbody> <tr> <td><code class="language-plaintext highlighter-rouge">CustomRobots/palletizing/config/palletizing_task.yaml</code></td> <td>Defines the pallet, conveyor settings, SKU sizes/masses/colors, and the box sequence.</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">CustomRobots/palletizing/launch/box_spawner.launch.py</code></td> <td>Starts the feeder node and passes the installed YAML path as <code class="language-plaintext highlighter-rouge">task_config</code>.</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">CustomRobots/palletizing/scripts/box_spawner.py</code></td> <td>The ROS node. Owns publishers, subscribers, timers, conveyor speed commands, Gazebo spawn calls, and <code class="language-plaintext highlighter-rouge">/box_ready</code>/<code class="language-plaintext highlighter-rouge">/box_done</code> coordination.</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">CustomRobots/palletizing/scripts/feeder/task_loader.py</code></td> <td>Loads the YAML and normalizes it into box metadata, pallet metadata, and spawn heights.</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">CustomRobots/palletizing/scripts/feeder/box_model_generator.py</code></td> <td>Generates temporary Gazebo SDF files for SKU boxes, including size, visual color, mass, and cuboid inertia.</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">CustomRobots/palletizing/scripts/feeder/state_machine.py</code></td> <td>Tracks the feeder state without ROS dependencies: idle, moving to pickup, settling, ready, done.</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">RoboticsAcademy/exercises/palletizing/python_template/HAL_Harmonic.py</code></td> <td>Subscribes to <code class="language-plaintext highlighter-rouge">/box_info</code> and <code class="language-plaintext highlighter-rouge">/pallet_info</code>, stores the latest metadata, and exposes clean HAL functions.</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">RoboticsAcademy/exercises/palletizing/python_template/solution.py</code></td> <td>Uses the HAL metadata and a first-fit row/shelf planner to choose target poses.</td> </tr> </tbody> </table> <p>This split makes the code easier to explain too. If a student asks “where do box dimensions come from?”, the answer is the YAML and <code class="language-plaintext highlighter-rouge">task_loader.py</code>. If they ask “who moves the belt?”, the answer is <code class="language-plaintext highlighter-rouge">box_spawner.py</code>. If they ask “where is the algorithm?”, the answer is the reference <code class="language-plaintext highlighter-rouge">solution.py</code>.</p> <hr/> <h2 id="extending-the-hal">Extending the HAL</h2> <p>On the RoboticsAcademy side, the HAL now listens for the metadata topics and exposes them through two calls:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">box</span> <span class="o">=</span> <span class="n">HAL</span><span class="p">.</span><span class="nc">GetBoxInfo</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
<span class="n">pallet</span> <span class="o">=</span> <span class="n">HAL</span><span class="p">.</span><span class="nc">GetPalletInfo</span><span class="p">()</span>
</code></pre></div></div> <p>So the student solution can stay high-level:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">name</span> <span class="o">=</span> <span class="n">HAL</span><span class="p">.</span><span class="nc">WaitForBox</span><span class="p">()</span>
<span class="n">box</span> <span class="o">=</span> <span class="n">HAL</span><span class="p">.</span><span class="nc">GetBoxInfo</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
<span class="n">target</span> <span class="o">=</span> <span class="n">planner</span><span class="p">.</span><span class="nf">next_pose</span><span class="p">(</span><span class="n">box</span><span class="p">)</span>

<span class="nf">pick</span><span class="p">()</span>
<span class="n">HAL</span><span class="p">.</span><span class="nc">BoxDone</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
<span class="nf">place</span><span class="p">(</span><span class="n">box</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
</code></pre></div></div> <p>That is a much better exercise boundary than asking the student to subscribe to ROS topics directly.</p> <p>The reference planner is intentionally small. It keeps a <code class="language-plaintext highlighter-rouge">used_length</code> value for each <code class="language-plaintext highlighter-rouge">(layer, row)</code> pair. For each incoming box, it tries layer 0 row 0, then the next row, then the next layer, and places the box in the first row where its length fits.</p> <p>Conceptually:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">layer</span> <span class="ow">in</span> <span class="n">layers</span><span class="p">:</span>
    <span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">rows</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">used_length</span><span class="p">[</span><span class="n">row</span><span class="p">]</span> <span class="o">+</span> <span class="n">box</span><span class="p">.</span><span class="n">length</span> <span class="o">&lt;=</span> <span class="n">usable_length</span><span class="p">:</span>
            <span class="n">place</span> <span class="n">box</span> <span class="n">here</span>
</code></pre></div></div> <p>It is not an advanced packing algorithm, but that is the point. It gives students a clean baseline they can understand and improve.</p> <hr/> <h2 id="the-hard-part-collision-and-reliability">The hard part: collision and reliability</h2> <p>The SKU metadata path is now in place, but the full runtime is not fully solved yet.</p> <p>Different box sizes make the stack more dynamic. The planner needs to avoid the conveyor and pallet as before, but eventually it also needs to understand the growing stack and possibly the carried box. I tried an experiment with MoveIt attached collision objects for the carried box, but it made planning less reliable, so I reverted it instead of leaving a half-working mechanism in the HAL.</p> <p>That is an important design line: students should not have to call low-level collision APIs like “add this box to MoveIt”. If dynamic collision objects are needed, they should be managed internally by the exercise backend, probably through a semantic placement registration step later.</p> <p>Current status is honest but incomplete:</p> <ul> <li>the SKU task data path exists,</li> <li>the feeder can describe incoming SKU boxes,</li> <li>the HAL can expose box and pallet metadata,</li> <li>the reference solution has a first row/shelf planner,</li> <li>syntax checks passed after the helper split and HAL changes,</li> <li>but full Academy runtime still needs more work for robust motion and collision behavior with mixed box sizes.</li> </ul> <p>That is still progress. The exercise is no longer conceptually locked to eight identical boxes.</p> <hr/> <h2 id="whats-next">What’s next</h2> <p>Next I need to stabilize the SKU runtime path: rebuild and retest the feeder split, check that installed helper imports work in the ROS launch flow, and decide how much collision handling belongs in the first SKU demo.</p> <p>The target is a clean student-facing task: boxes arrive one at a time, the HAL reports the SKU and pallet data, and the student writes a placement algorithm without touching Gazebo, MoveIt, or ROS topics directly.</p> <p>Until next week 👋</p>]]></content><author><name></name></author><category term="updates"/><category term="gsoc"/><category term="roboticsacademy"/><category term="gazebo-harmonic"/><category term="ros2"/><category term="moveit"/><summary type="html"><![CDATA[The exercise started moving from one fixed box size and hardcoded 2×2×2 targets toward SKU-based online palletizing, with task metadata in YAML, dynamic box generation, box and pallet info topics, HAL accessors, and a first row/shelf placement planner.]]></summary></entry><entry><title type="html">Week 9 — Syncing the palletizing exercise with upstream</title><link href="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week9-upstream-sync/" rel="alternate" type="text/html" title="Week 9 — Syncing the palletizing exercise with upstream"/><published>2026-07-14T00:00:00+00:00</published><updated>2026-07-14T00:00:00+00:00</updated><id>https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week9-upstream-sync</id><content type="html" xml:base="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week9-upstream-sync/"><![CDATA[<blockquote> <p><em>GSoC 2026 · Week 9 · Coding period</em></p> </blockquote> <h1 id="week-9-syncing-the-palletizing-exercise-with-upstream">Week 9: Syncing the palletizing exercise with upstream</h1> <p>After Week 8, the palletizing demo was in a good place visually: the UR10 picked boxes from the conveyor, carried them cleanly, and stacked them on the pallet. But there was a less visible problem underneath it — the feature branches had drifted from upstream JdeRobot.</p> <p>This week was about paying that debt down. Less shiny demo work, more integration work: bringing the branches back onto the current upstream structure, resolving schema changes, and making sure the palletizing exercise still has a clean identity inside the platform.</p> <hr/> <h2 id="why-the-sync-mattered">Why the sync mattered</h2> <p>The palletizing work had been moving quickly on top of an older base. Meanwhile, upstream kept changing around it: new robots, new launchers, database changes, and a terminology cleanup that renamed the old universe/world layout.</p> <p>Letting that gap grow would make every future test less representative. The exercise might work in my local branch, but not in the current RoboticsAcademy + RoboticsInfrastructure stack that it eventually has to live in. So the goal for the week was simple: catch up now, while the conflict set was still understandable.</p> <p>The sync was done carefully instead of directly on the active branch. I kept a backup at the pre-merge state, performed the merge in an isolated worktree, reviewed the conflicts there, and only then fast-forwarded the main <code class="language-plaintext highlighter-rouge">palletizing-exercise</code> branch. That way, the working branch stayed recoverable during the whole process.</p> <hr/> <h2 id="worlds-became-scenes">Worlds became Scenes</h2> <p>One of the biggest upstream changes was the database and launcher terminology.</p> <p>Earlier, the project used <code class="language-plaintext highlighter-rouge">universes.sql</code> and a <code class="language-plaintext highlighter-rouge">Worlds/</code> directory. Upstream moved to the newer structure:</p> <ul> <li><code class="language-plaintext highlighter-rouge">Worlds/</code> became <code class="language-plaintext highlighter-rouge">Scenes/</code>.</li> <li><code class="language-plaintext highlighter-rouge">database/universes.sql</code> became <code class="language-plaintext highlighter-rouge">database/worlds.sql</code>.</li> <li>The old universe/world naming split changed into world/scene records.</li> </ul> <p>So the palletizing launcher and database rows had to move with that structure. The world file now lives under <code class="language-plaintext highlighter-rouge">Scenes/</code>, and the launcher points there instead of the stale <code class="language-plaintext highlighter-rouge">Worlds/</code> path.</p> <p>This was not just a rename. Upstream had also claimed the IDs I had used earlier, so the palletizing entries had to be renumbered.</p> <p>The current mapping is:</p> <ul> <li>exercise: <code class="language-plaintext highlighter-rouge">30</code></li> <li>world: <code class="language-plaintext highlighter-rouge">73</code></li> <li>scene: <code class="language-plaintext highlighter-rouge">73</code></li> <li>robot: <code class="language-plaintext highlighter-rouge">31</code></li> </ul> <p>That is the set the Academy side now points to as well: the palletizing exercise is exercise <code class="language-plaintext highlighter-rouge">30</code>, linked to world <code class="language-plaintext highlighter-rouge">73</code>, which launches the palletizing scene and the UR10 suction robot.</p> <p>A small note for future-me: some older notes still mention exercise <code class="language-plaintext highlighter-rouge">27</code> or universe/world <code class="language-plaintext highlighter-rouge">71</code>. Those are historical now. The post-sync exercise identity is <code class="language-plaintext highlighter-rouge">30 → 73</code>, with scene <code class="language-plaintext highlighter-rouge">73</code> and robot <code class="language-plaintext highlighter-rouge">31</code>.</p> <hr/> <h2 id="converging-with-upstreams-moveit-group-parameter">Converging with upstream’s MoveIt group parameter</h2> <p>Another useful outcome of the sync was around the MoveIt planning group.</p> <p>Earlier in the project, the IFRA execution servers had a hardcoded planning group for the UR5. That broke the UR10 suction setup, because the palletizing robot uses the <code class="language-plaintext highlighter-rouge">ur10_arm</code> group. I had solved that locally by parameterizing the group name.</p> <p>During the sync, I found that upstream had solved the same class of problem too, but with a different parameter name. Instead of keeping my local version and creating a long-term divergence, I switched the palletizing launcher to upstream’s final shape: <code class="language-plaintext highlighter-rouge">ROB_GROUP</code>.</p> <p>So the UR10 suction launch path now follows the same convention as the current upstream robot launchers. The palletizing launcher passes <code class="language-plaintext highlighter-rouge">ur10_arm</code>, and the shared execution code stays aligned with upstream instead of carrying a forked version of the same idea.</p> <p>That is the kind of sync result I like: not just “conflicts resolved”, but one more custom patch removed.</p> <hr/> <h2 id="rebuilding-the-academy-side-cleanly">Rebuilding the Academy side cleanly</h2> <p>The Infrastructure sync was only half the story. RoboticsAcademy also needed to point to the new world and scene IDs, and the exercise needed to sit cleanly on top of current upstream.</p> <p>So the Academy side was rebuilt as the standalone palletizing exercise on the current base, instead of carrying forward old experimental history. The important part is that the public exercise identity is now clear:</p> <ul> <li>Palletizing is its own exercise, not a hidden variant of Pick and Place.</li> <li>The database entry points to world <code class="language-plaintext highlighter-rouge">73</code>.</li> <li>The exercise remains marked as a prototype while integration work continues.</li> </ul> <p>This also keeps the project easier to review. The palletizing changes are focused around the exercise, its templates, and its database link, instead of being mixed with unrelated churn.</p> <hr/> <h2 id="what-changed-from-the-students-point-of-view">What changed from the student’s point of view?</h2> <p>Ideally, not much.</p> <p>The student still sees the same idea: a UR10 with a suction cup waits for a box, picks it from the conveyor, and stacks it on the pallet through the HAL. The sync was mostly platform plumbing — paths, IDs, launch records, and robot configuration — but that plumbing matters because it decides whether the exercise launches in the real RoboticsAcademy flow.</p> <p>The student-facing API stays centered around calls like:</p> <ul> <li><code class="language-plaintext highlighter-rouge">WaitForBox()</code></li> <li><code class="language-plaintext highlighter-rouge">MoveJoint(...)</code></li> <li><code class="language-plaintext highlighter-rouge">MoveLinear(...)</code></li> <li><code class="language-plaintext highlighter-rouge">SuctionSet(...)</code></li> <li><code class="language-plaintext highlighter-rouge">BoxDone(name)</code></li> </ul> <p>That separation is the point of the HAL. The branch can survive a database rename or a launcher refactor without forcing students to learn about those internals.</p> <hr/> <h2 id="whats-next">What’s next</h2> <p>With the branches synced, the next step is to move the exercise beyond identical boxes and a hardcoded 2×2×2 target grid. The mentors’ direction is to make palletizing more algorithmic: different box dimensions, SKU families, and placement patterns.</p> <p>So next week I will start turning the fixed-box demo into an SKU-based palletizing exercise.</p> <p>Until next week 👋</p>]]></content><author><name></name></author><category term="updates"/><category term="gsoc"/><category term="roboticsacademy"/><category term="gazebo-harmonic"/><category term="ros2"/><category term="moveit"/><summary type="html"><![CDATA[The palletizing branches caught up with JdeRobot upstream — Worlds became Scenes, universe records moved to the new world/scene schema, IDs changed to exercise 30 and world 73, and the UR10 MoveIt group setup converged with upstream's ROB_GROUP parameter.]]></summary></entry><entry><title type="html">Week 8 — Polishing on mentors’ feedback</title><link href="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week8-mentor-polish/" rel="alternate" type="text/html" title="Week 8 — Polishing on mentors’ feedback"/><published>2026-07-07T00:00:00+00:00</published><updated>2026-07-07T00:00:00+00:00</updated><id>https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week8-mentor-polish</id><content type="html" xml:base="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week8-mentor-polish/"><![CDATA[<blockquote> <p><em>GSoC 2026 · Week 8 · Coding period</em></p> </blockquote> <h1 id="week-8-polishing-on-mentors-feedback">Week 8: Polishing on mentors’ feedback</h1> <p>Last week the pick-and-place loop closed — boxes got stacked. But “it works” and “it works <em>the way an exercise should</em>” are different bars. The mentors went through the exercise and sent back a list of pointers, and this week was almost entirely about working through that list: the layout, the motion, the scene, the look. Less new machinery, more turning a rough-but-working demo into something a student would actually want to open.</p> <hr/> <h2 id="demo">Demo</h2> <p>The whole thing running after the polish pass — feed, pick, carry, stack, repeat:</p> <video controls="" width="100%" style="max-width: 720px;"> <source src="/gsoc2026-Ashwani_Kumar/assets/video/palletizing-v1.mp4" type="video/mp4"/> Your browser does not support the video tag. </video> <hr/> <h2 id="relaying-the-world">Relaying the world</h2> <p>The first note was about the geometry of the workcell. Originally the belt ran across the front of the robot and the pallet sat behind it, which made the pick and place fight for the same bit of space and gave the arm awkward reaches.</p> <p>So I rebuilt the layout around one idea: the robot works <strong>one end</strong> of the belt.</p> <ul> <li>The conveyor now runs straight out along world +X, with the robot at the near end. Boxes feed from the far end and ride toward the arm, stopping at a fixed pickup point right in front of it.</li> <li>The pallet moved to the robot’s right (−Y). So every cycle is the same shape: pick in front, swing right, place. A repeatable ~90° clockwise turn instead of a reach-over-the-shoulder contortion.</li> </ul> <p>Getting the pallet distance right took a couple of tries. Too far out and the far row of the stack fell outside the UR10’s ~1.3 m reach — MoveIt would just report the plan as infeasible, the arm would stall at the belt, and the <em>next</em> move would drag the box across the conveyor. Pulling the pallet centre in to −0.88 m puts the whole grid inside the reach envelope while still keeping the deck clear of the robot base and visibly separate from the belt.</p> <p>I also replaced the old table mesh with a plain wooden-pallet-on-a-stand built from boxes — cheaper collision geometry for the planner, and it reads more like an actual palletizing station.</p> <hr/> <h2 id="making-the-box-stand-out">Making the box stand out</h2> <p>A small one, but it mattered visually: the boxes and the pallet were both the same brown, so the stack blended into the platform and you couldn’t tell what had been placed.</p> <p>I warmed and lightened the box colour to a kraft-cardboard tone and kept the pallet a darker wood. Now the stack reads clearly against the deck — obvious in the demo above.</p> <hr/> <h2 id="the-joint-limits-limit-4-5-6">The joint limits: “limit 4, 5, 6”</h2> <p>The main technical note was about the wrist joints. The arm was still doing ugly things — spinning a wrist most of a full turn, twisting the box around on its way to the pallet — and the ask was to clamp joints 4, 5, and 6 down.</p> <p>Two things came out of digging into this.</p> <p>First, a correction to my own assumption. I went in expecting to clamp all three wrists hard, but watching the motion, only <strong>joint 6</strong> (wrist_3) was the culprit doing the visible box “swirl” — spinning the carton about the cup axis on the way over. Wrists 1 and 2 actually needed their range to reach the far row on the descent. So the fix wasn’t a blanket clamp: wrist_3 narrowed to ±120° to kill the swirl, wrists 1 and 2 left wide.</p> <p>Second — and this is the bit I’m happier about — <em>where</em> the limits live. Last week I’d narrowed the shared UR10 limits to fix the 360° spins. But those limits are shared with the Pick &amp; Place exercise’s UR5-family config, and clamping the common file to make palletizing tidy is exactly the kind of change that quietly breaks the neighbour. So I reverted the shared file back to stock (±360°) and added a <strong>palletizing-only</strong> joint-limits file that the suction launcher loads instead. Same effect for this exercise, zero blast radius on anything else.</p> <p>That isolated file is where the “predictable” behaviour is actually encoded:</p> <ul> <li><strong>shoulder_pan <code class="language-plaintext highlighter-rouge">[−150°, +30°]</code></strong> — deliberately asymmetric. A symmetric range lets the planner pick either the clockwise swing or its counter-clockwise mirror; capping the positive side at +30° makes the counter-clockwise twin illegal, so the arm <em>always</em> turns the way the mentors wanted.</li> <li><strong>elbow <code class="language-plaintext highlighter-rouge">[0°, +180°]</code></strong> — elbow-up only, so the forearm never droops down toward the cells.</li> <li><strong>wrist_3 <code class="language-plaintext highlighter-rouge">±120°</code></strong> — the swirl clamp.</li> </ul> <p>None of these limit what the task needs to reach — they just delete the ugly redundant solutions from the search space so the planner is forced onto the tidy one. Same lesson as last week’s ±180° fix, applied with a finer brush.</p> <hr/> <h2 id="feeding-the-workcell-to-moveit">Feeding the workcell to MoveIt</h2> <p>The other structural improvement: the planner didn’t actually know the conveyor or the pallet existed.</p> <p>Up to now, the arm avoided them only because the <em>hand-picked waypoints</em> kept it clear — a single cruise height it lifted to, crossed at, and descended from. That works, but it’s fragile and it pushes obstacle-avoidance into the student’s solution code, which is backwards.</p> <p>So I added a small behind-the-scenes node, <code class="language-plaintext highlighter-rouge">palletizing_scene</code>, that injects the conveyor and the pallet into MoveIt’s planning scene as collision objects when the stack starts. Now the planner routes around them natively — it <em>knows</em> the belt and pallet are solid. It publishes the objects as a planning-scene diff, in the same <code class="language-plaintext highlighter-rouge">base_link</code> frame the solution’s targets use, and re-publishes a few times so a late-starting <code class="language-plaintext highlighter-rouge">move_group</code> still picks them up.</p> <p>The student solution and the HAL never touch the planning scene — it’s pure setup that happens for them. One nuance I had to handle: the pallet collision box is trimmed a few centimetres below the real deck top, so a box placed <em>flush</em> on the deck doesn’t get rejected as a touching-contact collision.</p> <hr/> <h2 id="making-the-motion-predictable">Making the motion predictable</h2> <p>Threaded through all of the above was the theme the mentors kept coming back to: the arm should move <em>predictably</em>. A student watching it should see the same clean cycle every time, not a planner improvising a different path on each box.</p> <p>The joint limits do most of that work. The rest was in the solution’s motion shape:</p> <ul> <li><strong>One cruise height.</strong> Every carry lifts straight up to a single transit height, crosses at that height, and descends straight down. No diagonal swoops that clip the belt.</li> <li><strong>Dropped the return-to-home between boxes.</strong> It was a wasted sweep; the arm now goes pick → place → straight to waiting for the next box.</li> <li><strong>Bumped the speed up.</strong> With the paths clean and predictable, I could raise the motion speed a good deal without anything flinging — the cautious crawl from earlier weeks wasn’t needed anymore.</li> <li><strong>Cup centring.</strong> The suction cup doesn’t sit dead-centre under the tool flange, so I aim the flange a few centimetres short and the cup lands on the box centre — applied identically at pick and place.</li> </ul> <p>The solution file itself got noticeably shorter and simpler in the process — a good sign that the complexity moved to where it belongs (the scene, the limits, the HAL) instead of living in the code the student reads.</p> <hr/> <h2 id="whats-next">What’s next</h2> <p>The layout, motion, and scene are where the mentors wanted them. Next is the vision side I flagged last week — a camera in the world and a detection node to replace the synthetic “box ready” signal — plus a proper standby pose over the belt so the arm waits in a sensible spot between boxes.</p> <p>Until next week 👋</p>]]></content><author><name></name></author><category term="updates"/><category term="gsoc"/><category term="roboticsacademy"/><category term="gazebo-harmonic"/><category term="ros2"/><category term="moveit"/><summary type="html"><![CDATA[A full pass over the mentors' review notes — the world relaid so the robot works one end of the belt, palletizing-only joint limits that force a tidy clockwise swing, the conveyor and pallet fed to MoveIt as collision objects, and a UR10 that finally moves predictably.]]></summary></entry><entry><title type="html">Week 7 — Stacking boxes and getting the paths clean</title><link href="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week7-stacking-and-clean-paths/" rel="alternate" type="text/html" title="Week 7 — Stacking boxes and getting the paths clean"/><published>2026-06-30T00:00:00+00:00</published><updated>2026-06-30T00:00:00+00:00</updated><id>https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week7-stacking-and-clean-paths</id><content type="html" xml:base="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week7-stacking-and-clean-paths/"><![CDATA[<blockquote> <p><em>GSoC 2026 · Week 7 · Coding period</em></p> </blockquote> <h1 id="week-7-stacking-boxes-and-getting-the-paths-clean">Week 7: Stacking boxes and getting the paths clean</h1> <p>Week 6 ended with the arm reaching the box but not yet picking and stacking reliably. This week the loop closes: a box rides in on the belt, the arm picks it, places it on the pallet in an ordered grid, and signals for the next one. Most of the week went into two problems that weren’t about “does it work” but “does it work <em>sanely</em>” — the arm taking absurd paths, and the student-facing code still talking raw ROS.</p> <hr/> <h2 id="demo">Demo</h2> <p>The full cycle — pick from the belt, carry across, place on the pallet:</p> <video controls="" width="100%" style="max-width: 720px;"> <source src="/gsoc2026-Ashwani_Kumar/assets/video/ur10-pick-place.mp4" type="video/mp4"/> Your browser does not support the video tag. </video> <hr/> <h2 id="writing-the-placement-logic">Writing the placement logic</h2> <p>Up to now the box spawner cheated: when a box reached the pickup point it just teleported it onto the pallet to fake the result. This week the robot actually does the work.</p> <p>The placement is a grid: 2 columns × 2 rows × 2 layers, eight boxes total. The solution walks the grid index by index — fill a layer row by row, then start the next layer on top. Each cell maps to a world coordinate, and the arm goes there. The motion for each box is the same shape:</p> <ol> <li>PTP move to a clearance height above the pickup point.</li> <li>Straight-line descent onto the box.</li> <li>Suction on.</li> <li>Lift straight up.</li> <li>Carry across to the target grid cell at transit height.</li> <li>Straight-line descent onto the stack.</li> <li>Suction off.</li> <li>Retreat up.</li> </ol> <p>The pick and place heights are tunable constants, because the offset between the tool flange (what MoveIt positions) and the suction cup face isn’t a clean number — the cup is mounted sideways with a compound offset. One calibration pass in sim sets the height where the cup just touches the box top, and everything else is derived from that.</p> <hr/> <h2 id="the-arm-kept-taking-the-long-way-around">The arm kept taking the long way around</h2> <p>The annoying problem this week: planning would succeed, but the path was ridiculous. From the home pose to the pickup pose, the arm would swing a wrist joint a full turn — 310 degrees the wrong way — sweeping through the table on the way. Valid plan, stupid motion.</p> <p>I spent a while chasing this in the wrong place. First I tried seeding the planner with a known-good pose before each move, which worked but was a band-aid. Then I tried giving the IK solver more time and more attempts — that helped it <em>find</em> solutions from far away, but it didn’t change <em>which</em> solution it picked, so the spin stayed. I even tried swapping the IK solver out entirely for one that picks the closest solution to the current pose. That one isn’t packaged for our ROS version, and an alternative added a heavier dependency, so I reverted both.</p> <p>The actual fix was much smaller, and it’s about the joint limits.</p> <p>A robot wrist at +52° and at −310° (which is +52° minus a full turn) are the <em>same physical pose</em>. The UR10 shipped with position limits of ±360° on every joint, so both solutions are legal — and the planner had no reason to prefer the short one. It just returned whichever it found first.</p> <p>Narrowing every joint’s position limit to ±180° removes the long-way-round twin from the search space entirely. The arm can still reach every pose the task needs — you never need more than ±180° on a single joint here — but the redundant full-turn solutions are now illegal, so the planner is forced onto the short path.</p> <p>That also explained why RViz always looked fine while my script spun: RViz seeds the solver from the live arm pose, which is near the short solution, so it rarely wandered. The scripted motion request had no such seed and hit the long-way solution every time — until the limits made it impossible for both.</p> <p>The lesson I’m taking from this: when the planner gives you a valid-but-ugly path, look at the limits before you start swapping out solvers. Tightening the joint range was one line of config; the solver swap was an afternoon I’m not getting back.</p> <p>The final setup is the stock IK solver with a longer timeout, and ±180° joint limits. No exotic plugins.</p> <hr/> <h2 id="moving-the-conveyor-handshake-into-the-hal">Moving the conveyor handshake into the HAL</h2> <p>The other cleanup was about where the line sits between the student and the plumbing.</p> <p>The whole point of the HAL (Hardware Abstraction Layer) is that a student writing the exercise only calls simple functions — move here, grip, release — and never touches ROS topics directly. The motion and suction calls were already like that. But the conveyor coordination wasn’t: the solution code still had a class that subscribed to <code class="language-plaintext highlighter-rouge">/box_ready</code> and published to <code class="language-plaintext highlighter-rouge">/box_done</code> by hand. Raw ROS, sitting in the file the student is supposed to fill in.</p> <p>So I moved it. The HAL now exposes two functions:</p> <ul> <li><code class="language-plaintext highlighter-rouge">WaitForBox()</code> — blocks until a box is stopped at the pickup point, returns its name.</li> <li><code class="language-plaintext highlighter-rouge">BoxDone(name)</code> — tells the feeder the box is stacked, which releases the next one.</li> </ul> <p>The topic subscription, the publisher, and the bookkeeping that ignores duplicate announcements all live inside the HAL now. The solution file dropped its ROS imports and the whole coordination class — it just calls <code class="language-plaintext highlighter-rouge">HAL.WaitForBox()</code> and <code class="language-plaintext highlighter-rouge">HAL.BoxDone(name)</code> in the loop. Pure API, no plumbing.</p> <p>One thing worth being honest about: <code class="language-plaintext highlighter-rouge">WaitForBox()</code> is not perception. The signal comes from the spawner node, which already knows where the box is because it put it there. It’s the right abstraction for getting the loop working, but it’s a stand-in for a real sensor.</p> <p>That’s deliberate. Because the coordination is wrapped behind those two function calls, the synthetic topic can later be replaced with a camera near the conveyor that actually detects the box arriving — and the student’s exercise code doesn’t change at all. The function signature stays; only what’s behind it gets smarter. That’s the direction I want to take it: a static camera, real detection, and a reported box pose instead of a hardcoded pickup point, which would also let boxes arrive at varying positions instead of always stopping at the same spot.</p> <hr/> <h2 id="whats-next">What’s next</h2> <p>Tune the stack so the boxes sit flush on each other, then start on the vision side — a camera in the world and a detection node to replace the synthetic <code class="language-plaintext highlighter-rouge">box_ready</code> signal.</p> <p>Until next week 👋</p>]]></content><author><name></name></author><category term="updates"/><category term="gsoc"/><category term="roboticsacademy"/><category term="gazebo-harmonic"/><category term="ros2"/><category term="moveit"/><summary type="html"><![CDATA[The pick-and-place loop now runs end to end — boxes get stacked on the pallet, the arm stops taking 360° detours after a joint-limit fix, and the conveyor handshake moved into the HAL so the student code is pure API.]]></summary></entry><entry><title type="html">Week 6 — The full loop: belt, box, arm</title><link href="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week6-stack-up-calibration/" rel="alternate" type="text/html" title="Week 6 — The full loop: belt, box, arm"/><published>2026-06-23T00:00:00+00:00</published><updated>2026-06-23T00:00:00+00:00</updated><id>https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week6-stack-up-calibration</id><content type="html" xml:base="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week6-stack-up-calibration/"><![CDATA[<blockquote> <p><em>GSoC 2026 · Week 6 · Coding period</em></p> </blockquote> <h1 id="week-6-the-full-loop--belt-box-arm">Week 6: The full loop — belt, box, arm</h1> <p>Last week ended with a UR10 that existed on disk and launched in a terminal window. This week I found out how many things between “it launches” and “it actually works” were quietly broken — and then got the full pick-and-place loop running for the first time: box spawns, belt carries it, conveyor stops, arm reaches in and picks it up.</p> <hr/> <h2 id="four-things-broke-on-first-launch">Four things broke on first launch</h2> <p>The first time I ran the full palletizing stack, four things failed before the arm moved a millimeter.</p> <p><strong>The controller never started.</strong> The first error was <code class="language-plaintext highlighter-rouge">symbol GzPluginHook missing</code> — cryptic, but it traced back to the <code class="language-plaintext highlighter-rouge">gz_ros2_control</code> plugin (the bridge between ROS controllers and Gazebo) having been compiled against an older Gazebo version. On a ROS Humble system, the apt package for this plugin links to Gazebo Fortress, not Gazebo Harmonic — and Gazebo Harmonic refused to load it. Fix: rebuild the plugin from source with the right flag, tell the launcher to prefer it over the system one.</p> <p><strong>The move server crashed immediately.</strong> After that, the action server that handles motion commands exited with a file-not-found error — it was looking for a joint config file for the suction gripper that doesn’t exist (suction cups have no joints to configure). A one-line fix: tell it there’s no end-effector.</p> <p><strong>MoveIt refused to plan anything.</strong> Every motion request failed in about 2 milliseconds — fast enough to know it wasn’t even trying. The URDF said the robot was called <code class="language-plaintext highlighter-rouge">ur5</code>, the SRDF said <code class="language-plaintext highlighter-rouge">ur10</code>. MoveIt sees two different robots and refuses to proceed.</p> <p><strong>The suction cup’s contact sensor was on the wrong link.</strong> Gazebo merges fixed joints during world loading — so the suction cup’s sensor ended up reported as being on the wrist link, not the cup link. The attachment system that listens for “something is touching the cup” never fired. One line in the URDF tells Gazebo to preserve that joint and keep the links separate.</p> <p>After all four: clean launch, arm holding its home pose, MoveIt ready to plan.</p> <hr/> <h2 id="how-the-conveyor-and-arm-talk-to-each-other">How the conveyor and arm talk to each other</h2> <p>Week 4 set up the conveyor belt and a <code class="language-plaintext highlighter-rouge">box_spawner</code> node that controls the box lifecycle. This week I wired up the handshake between the belt and the arm — the protocol that lets the robot know when to move.</p> <p>The sequence goes like this:</p> <ol> <li>The <code class="language-plaintext highlighter-rouge">box_spawner</code> node spawns a box at the feed end of the belt.</li> <li>The belt carries it forward. The node watches the box position.</li> <li>When the box reaches the pickup point — directly in front of the arm — the belt stops and the node publishes the box’s name on a <code class="language-plaintext highlighter-rouge">/box_ready</code> topic.</li> <li>The robot’s solution code is subscribed to <code class="language-plaintext highlighter-rouge">/box_ready</code>. When the message arrives, the arm moves in, picks the box, and places it on the pallet.</li> <li>The solution code then publishes on <code class="language-plaintext highlighter-rouge">/box_done</code> with the same box name.</li> <li>The spawner sees <code class="language-plaintext highlighter-rouge">/box_done</code>, releases the belt, and starts the next box.</li> </ol> <p>This keeps things clean on the student side — the exercise template gives you a simple <code class="language-plaintext highlighter-rouge">wait_for_box()</code> call that blocks until a box is ready, and a <code class="language-plaintext highlighter-rouge">box_done()</code> call to signal you’re finished. The student writes pick-and-place logic, not conveyor management.</p> <p>It also means the exercise is self-pacing: the belt doesn’t send the next box until the robot says it’s done with the current one. No timing hacks, no hardcoded delays.</p> <hr/> <h2 id="fixing-the-suction-hal">Fixing the suction HAL</h2> <p>The student-facing HAL was still a copy of the Pick &amp; Place gripper HAL. It had three problems:</p> <ul> <li>At startup, it waited for a gripper controller that doesn’t exist on the suction robot — hanging forever.</li> <li><code class="language-plaintext highlighter-rouge">GripperSet()</code> tried to move finger joints that also don’t exist.</li> <li>The list of objects the suction cup is allowed to grab was hardcoded to the Pick &amp; Place props (<code class="language-plaintext highlighter-rouge">blue_ball</code>, <code class="language-plaintext highlighter-rouge">yellow_box</code>, etc.) — none of which match the dynamically-named <code class="language-plaintext highlighter-rouge">box_&lt;id&gt;_&lt;count&gt;</code> boxes the spawner creates.</li> </ul> <p>The replacement is much simpler. <code class="language-plaintext highlighter-rouge">SuctionSet(True)</code> sends an on/off signal to the vacuum system. When vacuum is on and something graspable is in contact with the cup, the Gazebo plugin attaches it. When vacuum is off, it releases. That’s the whole API. One publisher, one message, no joint trajectory nonsense.</p> <p>The graspable allow-list is now just <code class="language-plaintext highlighter-rouge">"box"</code> — a substring match that catches any box regardless of its generated name.</p> <hr/> <h2 id="why-the-arm-wasnt-planning-anything">Why the arm wasn’t planning anything</h2> <p>Even with the stack clean, every arm motion request failed instantly. Buried in the output was this:</p> <blockquote> <p><em>No kinematics plugins defined. Fill and load kinematics.yaml!</em></p> </blockquote> <p><code class="language-plaintext highlighter-rouge">kinematics.yaml</code> tells MoveIt’s planner how to solve “given a target position, which joint angles get me there?” The file had entries for the UR5 pick-and-place exercise (it’s shared), but nothing for the UR10 palletizing group. Without it, every pose goal fails before planning even starts.</p> <p>Adding the entry — and giving it more attempts and a longer timeout, because the UR10’s longer arm has more configurations to search through — made pose goals start working immediately.</p> <hr/> <h2 id="which-way-does-the-cup-face">Which way does the cup face?</h2> <p>With planning working, the next question was orientation: which joint angles make the suction cup point straight down at the box?</p> <p>My first guess was wrong. I assumed “cup down” meant pointing the tool’s Z-axis downward — the natural assumption. But the suction cup is mounted sideways on the tool flange, rotated 90°. So “cup down” actually means pointing the tool’s X-axis downward, which is a completely different set of joint angles.</p> <p>Once I had the right orientation, I could start testing whether the arm could actually reach the box from above.</p> <div class="row mt-3"> <div class="col-sm mt-3 mt-md-0"> <figure> <picture> <source class="responsive-img-srcset" srcset="/gsoc2026-Ashwani_Kumar/assets/img/w6-angles-480.webp 480w,/gsoc2026-Ashwani_Kumar/assets/img/w6-angles-800.webp 800w,/gsoc2026-Ashwani_Kumar/assets/img/w6-angles-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/gsoc2026-Ashwani_Kumar/assets/img/w6-angles.jpg" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div> <div class="caption"> Planning in RViz — the orange arm is the goal state with the suction cup pointing down toward the pickup point. The grey arm is where the robot currently is. </div> <hr/> <h2 id="straight-line-descent-kept-failing">Straight-line descent kept failing</h2> <p>With the arm hovering above the box, I tried commanding it to descend straight down. The planner for straight-line moves (Pilz LIN, same as Pick &amp; Place) kept refusing it.</p> <p>The reason: at the cup-down orientation, one of the wrist joints sits close to zero degrees — a singular configuration where the planner gives up because small target changes would require wild joint swings. This is a known quirk of UR wrists at certain orientations.</p> <p>The fix was switching planners for the descent step. Instead of Pilz LIN, I used <code class="language-plaintext highlighter-rouge">computeCartesianPath</code> — a different approach that stitches together tiny joint-space steps along the desired line. It handles the near-singular wrist just fine. Six consecutive 5 cm descent steps all went through cleanly.</p> <hr/> <h2 id="the-arm-next-to-the-box">The arm next to the box</h2> <p>By the end of the week the arm was reliably reaching a position close to the box on the belt with the cup pointing down — and the belt had already stopped and published <code class="language-plaintext highlighter-rouge">/box_ready</code> by the time the arm moved.</p> <div class="row mt-3"> <div class="col-sm mt-3 mt-md-0"> <figure> <picture> <source class="responsive-img-srcset" srcset="/gsoc2026-Ashwani_Kumar/assets/img/w6-bot-on-box-480.webp 480w,/gsoc2026-Ashwani_Kumar/assets/img/w6-bot-on-box-800.webp 800w,/gsoc2026-Ashwani_Kumar/assets/img/w6-bot-on-box-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/gsoc2026-Ashwani_Kumar/assets/img/w6-bot-on-box.jpg" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div> <div class="caption"> The UR10 in Gazebo Harmonic — arm extended toward the pickup position, box waiting on the stopped belt. </div> <p>The remaining work is dialling in the exact height where the cup makes contact with the box top, and confirming the suction attach fires reliably. Once that’s calibrated, the full pick-place-repeat loop can run on its own.</p> <hr/> <h2 id="whats-next">What’s next</h2> <p>Finish the pick height calibration, confirm suction works end-to-end, then write the pallet placement logic so boxes actually stack.</p> <p>Until next week 👋</p>]]></content><author><name></name></author><category term="updates"/><category term="gsoc"/><category term="roboticsacademy"/><category term="gazebo-harmonic"/><category term="ros2"/><category term="moveit"/><summary type="html"><![CDATA[The palletizing stack ran end-to-end for the first time — four launch bugs cleared, the conveyor handshake wired up, the suction HAL fixed, and the arm finally reaching the box with the cup pointing down.]]></summary></entry><entry><title type="html">Week 5 — Swapping in the UR10 and cleaning up the framework</title><link href="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week5-ur10-arm/" rel="alternate" type="text/html" title="Week 5 — Swapping in the UR10 and cleaning up the framework"/><published>2026-06-17T00:00:00+00:00</published><updated>2026-06-17T00:00:00+00:00</updated><id>https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week5-ur10-arm</id><content type="html" xml:base="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week5-ur10-arm/"><![CDATA[<blockquote> <p><em>GSoC 2026 · Week 5 · Coding period</em></p> </blockquote> <h1 id="week-5-swapping-in-the-ur10-and-cleaning-up-the-framework">Week 5: Swapping in the UR10 and cleaning up the framework</h1> <p>My mentors suggested upgrading the palletizing arm from a UR5 to a UR10 — the UR10 has a 1.3 m reach (vs 0.85 m) and a 10 kg payload, which is a much better match for a real palletizing scenario. That sounded like a one-word change (<code class="language-plaintext highlighter-rouge">ur_type: ur10</code>), but the rabbit hole went a bit deeper than expected.</p> <hr/> <h2 id="the-hardcoded-group-name-problem">The hardcoded group name problem</h2> <p>The three shared C++ action servers that drive arm motion — <code class="language-plaintext highlighter-rouge">move</code>, <code class="language-plaintext highlighter-rouge">robmove</code>, <code class="language-plaintext highlighter-rouge">robpose</code> — all had this:</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">group_name</span> <span class="o">=</span> <span class="s">"ur5_manipulator"</span><span class="p">;</span>   <span class="c1">// hardcoded in 5 places</span>
<span class="n">move_group_interface_ROB</span> <span class="o">=</span> <span class="n">MoveGroupInterface</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">group_name</span><span class="p">);</span>
</code></pre></div></div> <p>MoveIt uses this string to look up the planning group in the SRDF. If it doesn’t match exactly, every motion call fails silently with “unknown group.” So a UR10 that declares its group <code class="language-plaintext highlighter-rouge">ur10_arm</code> in the SRDF would never plan anything — not because the geometry is wrong, but because a single string literal points somewhere else.</p> <p>The fix I went with (matching an approach mentor Jesus used on a parallel branch) was to add a <strong><code class="language-plaintext highlighter-rouge">MOVE_GROUP</code> ROS parameter</strong> with a default of <code class="language-plaintext highlighter-rouge">"ur5_manipulator"</code>. The existing Pick &amp; Place launcher passes nothing → gets the default → unchanged behaviour. The new UR10 launcher explicitly sets <code class="language-plaintext highlighter-rouge">{"MOVE_GROUP": "ur10_arm"}</code> on all three nodes. Five hardcoded literals replaced, one rebuild, zero regressions.</p> <hr/> <h2 id="the-arm-swap-itself">The arm swap itself</h2> <p>With the framework unblocked, the swap was mostly renaming and config edits:</p> <ul> <li><code class="language-plaintext highlighter-rouge">ur_type</code>, <code class="language-plaintext highlighter-rouge">ROB_PARAM</code> → <code class="language-plaintext highlighter-rouge">ur10</code> everywhere</li> <li>SRDF group <code class="language-plaintext highlighter-rouge">ur5_manipulator</code> → <code class="language-plaintext highlighter-rouge">ur10_arm</code></li> <li>Joint limits and Pilz Cartesian limits swapped to the <code class="language-plaintext highlighter-rouge">ur10/config/</code> paths</li> <li>The Gazebo spawn entity name and the world’s <code class="language-plaintext highlighter-rouge">&lt;robot_model_name&gt;</code> both updated to <code class="language-plaintext highlighter-rouge">ur10_suction</code> — these two must change together because <code class="language-plaintext highlighter-rouge">gz_link_attacher</code> looks up the robot by that name to know where to attach the suction grip</li> </ul> <hr/> <h2 id="naming-things-properly">Naming things properly</h2> <p>At one point during the work I noticed we had a file called <code class="language-plaintext highlighter-rouge">ur5_suction.urdf.xacro</code> that was already describing a UR10 arm. That’s exactly the kind of confusion that causes real bugs — a future me would open that file assuming UR5 geometry and get UR10 instead.</p> <p>So I renamed the whole family: <code class="language-plaintext highlighter-rouge">ur5_suction.*</code> → <code class="language-plaintext highlighter-rouge">ur10_suction.*</code>, and moved the URDF from <code class="language-plaintext highlighter-rouge">models/ur5/</code> into a proper <code class="language-plaintext highlighter-rouge">models/ur10/</code> folder. The shared generic xacro includes (<code class="language-plaintext highlighter-rouge">ur.urdf.xacro</code>, <code class="language-plaintext highlighter-rouge">ur.ros2_control.xacro</code>) stayed in <code class="language-plaintext highlighter-rouge">models/ur5/</code> — those are arm-agnostic and Pick &amp; Place relies on them — but the suction-specific file now lives where it should.</p> <hr/> <h2 id="the-ghost-package-bug">The ghost package bug</h2> <p>While validating the xacro I ran into a crash: <code class="language-plaintext highlighter-rouge">Package 'ur5_gripper_description' not found</code>. It turned out <code class="language-plaintext highlighter-rouge">config/ur10/visual_parameters.yaml</code> was imported from IFRA (the upstream framework) with all 14 mesh references pointing at that package — which isn’t installed in the RA image. The UR10 meshes actually live inside <code class="language-plaintext highlighter-rouge">ros2srrc_robots/ur10/meshes/</code>, mirroring the convention the UR5 yaml already uses correctly.</p> <p>One bulk replace later, the xacro parsed cleanly to 575 lines. The arm would have loaded without meshes otherwise — a silent rendering failure that would have been genuinely annoying to track down.</p> <hr/> <h2 id="demo">Demo</h2> <p>Here’s the current state — the UR10 loaded in Gazebo Harmonic alongside the palletizing scene, with the arm skeleton showing in RViz:</p> <div class="row mt-3"> <div class="col-sm mt-3 mt-md-0"> <figure> <picture> <source class="responsive-img-srcset" srcset="/gsoc2026-Ashwani_Kumar/assets/img/palletizing-ur10-sim-480.webp 480w,/gsoc2026-Ashwani_Kumar/assets/img/palletizing-ur10-sim-800.webp 800w,/gsoc2026-Ashwani_Kumar/assets/img/palletizing-ur10-sim-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/gsoc2026-Ashwani_Kumar/assets/img/palletizing-ur10-sim.jpeg" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div> <div class="caption"> UR10 in the Palletizing Harmonic World — Gazebo (top) and RViz skeleton (bottom). </div> <hr/> <h2 id="also-this-week-rectangular-boxes">Also this week: rectangular boxes</h2> <p>One detail I also got in: the palletizing boxes are now proper rectangular cuboids instead of cubes. Real-world palletizing almost never involves perfect cubes, and the rectangular shape makes placement orientation meaningful — which matters once the motion planning is written.</p> <hr/> <h2 id="whats-next">What’s next</h2> <p>The arm is in, named correctly, and plans against the right group. Next is getting the HAL’s <code class="language-plaintext highlighter-rouge">SuctionSet</code> wired up — replacing the <code class="language-plaintext highlighter-rouge">GripperSet</code> stub copied from Pick &amp; Place with a proper vacuum on/off toggle — and then writing the actual pick-and-place motion logic for the palletizing cycle.</p> <p>Until next week.</p>]]></content><author><name></name></author><category term="updates"/><category term="gsoc"/><category term="roboticsacademy"/><category term="gazebo-harmonic"/><category term="ros2"/><category term="moveit"/><summary type="html"><![CDATA[Upgrading the palletizing arm from UR5 to UR10, parameterizing a hardcoded group name buried in shared C++, renaming files to mean what they say, and fixing a ghost mesh package along the way.]]></summary></entry><entry><title type="html">Week 4 — A moving belt and a vacuum grip</title><link href="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week4-conveyor-suction/" rel="alternate" type="text/html" title="Week 4 — A moving belt and a vacuum grip"/><published>2026-06-14T00:00:00+00:00</published><updated>2026-06-14T00:00:00+00:00</updated><id>https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week4-conveyor-suction</id><content type="html" xml:base="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week4-conveyor-suction/"><![CDATA[<blockquote> <p><em>GSoC 2026 · Week 4 · Coding period</em></p> </blockquote> <h1 id="week-4-a-moving-belt-and-a-vacuum-grip">Week 4: A moving belt and a vacuum grip</h1> <p>Last week the Palletizing scene loaded but nothing in it moved — the conveyor was a static mesh, the boxes sat where I placed them, and the arm shared its gripper with Pick &amp; Place. This week I made all three of those things untrue. Palletizing now has its own branches, its own exercise row, a conveyor that actually carries boxes, and a UR5 that wears a suction cup instead of fingers.</p> <hr/> <h2 id="giving-palletizing-a-home-of-its-own">Giving Palletizing a home of its own</h2> <p>Up to now Palletizing had been riding inside Pick &amp; Place’s exercise — sharing its card, its robot, its everything. That was the right call for standing the scene up quickly, but the moment the two exercises start to differ (and a suction gripper is exactly that moment), they need to be separable.</p> <p>So I cut <strong><code class="language-plaintext highlighter-rouge">palletizing-exercise</code> branches on both RoboticsAcademy and RoboticsInfrastructure</strong>, wired the exercise into the database as its own entry — <strong>exercise 27, universe 71</strong> — and rebuilt the Gazebo world under that identity. The HAL templates I scaffolded from <code class="language-plaintext highlighter-rouge">pick_place</code> as a starting point; they’re stubs for now, but they give the exercise a place to grow its own student-facing API.</p> <p><a href="https://github.com/JdeRobot/RoboticsInfrastructure/commit/7d409c4819826ed039f34bf85a62eeed16121b75"><code class="language-plaintext highlighter-rouge">RoboticsInfrastructure@7d409c4</code></a></p> <hr/> <h2 id="the-conveyor-belt">The conveyor belt</h2> <p>A pallet exercise where boxes don’t arrive isn’t much of an exercise. The Pick &amp; Place conveyor <em>looks</em> like a belt but is a static mesh — so I replaced it with a real moving one using <strong>Gazebo Harmonic’s built-in <code class="language-plaintext highlighter-rouge">TrackController</code> system</strong>, the same machinery Gazebo uses to drive tank treads. The contact surface of the belt link gets a commanded velocity, and anything resting on it gets carried along.</p> <p>On top of the belt I wrote a <strong><code class="language-plaintext highlighter-rouge">box_spawner.py</code> node</strong> that owns the full box lifecycle:</p> <ol> <li>A box spawns at the feed end of the belt.</li> <li>It rides the belt to the centre.</li> <li>The belt stops.</li> <li>The box is placed on the pallet table.</li> <li>The cycle restarts.</li> </ol> <p>The belt speed is also controllable from ROS — there’s a bridge exposing it as a topic, so the speed isn’t baked into the world and can be driven from a node (or, eventually, from the exercise logic).</p> <p><a href="https://github.com/JdeRobot/RoboticsInfrastructure/commit/af25affc0d48176ca4512b98c97d7fab1c69d2e0"><code class="language-plaintext highlighter-rouge">RoboticsInfrastructure@af25aff</code></a></p> <hr/> <h2 id="the-suction-gripper">The suction gripper</h2> <p>A palletizing robot doesn’t pinch boxes between two fingers — it grabs them by the face with a vacuum cup. So the UR5 needed a new end effector, and that ripples out further than you’d expect:</p> <ul> <li>A new <strong>URDF</strong> for the suction-cup variant of the arm.</li> <li>A new <strong>SRDF</strong> — and this one is genuinely different, not a copy. It’s <em>arm-only</em>: no finger group, because there are no fingers to plan for.</li> <li>A matching <strong>controllers</strong> config and a <strong>MoveIt launcher</strong> for the new robot.</li> <li>A <strong><code class="language-plaintext highlighter-rouge">palletizing_harmonic.launch.py</code></strong> for RViz.</li> </ul> <p>I registered the suction arm as a <strong>new robot in the database</strong> and pointed the Palletizing universe at it instead of the Robotiq-gripper UR5.</p> <p>The last piece was the grab itself. RoboticsInfrastructure uses a <code class="language-plaintext highlighter-rouge">gz_link_attacher</code> plugin to fake the grasp — when the gripper closes, it rigidly attaches the target object to the gripper link. That plugin had the robot model name and gripper link <strong>hardcoded to the Robotiq finger tips</strong>. I patched it to take both as <strong>SDF parameters</strong>, so Palletizing can point it at the suction cup face instead. Pick &amp; Place keeps its old behaviour by passing the old values; Palletizing passes its own.</p> <p><a href="https://github.com/JdeRobot/RoboticsInfrastructure/commit/1623d8f6e626e616d1555dec9a5b94b18bf1ed67"><code class="language-plaintext highlighter-rouge">RoboticsInfrastructure@1623d8f</code></a></p> <hr/> <h2 id="demo">Demo</h2> <p>The conveyor carrying boxes, and the suction gripper picking them off:</p> <video controls="" width="100%" style="max-width: 720px;"> <source src="/gsoc2026-Ashwani_Kumar/assets/video/conveyer-gripper.mp4" type="video/mp4"/> Your browser does not support the video tag. </video> <hr/> <h2 id="whats-next">What’s next</h2> <p>Next up is the HAL — replacing <code class="language-plaintext highlighter-rouge">GripperSet</code> with a <code class="language-plaintext highlighter-rouge">SuctionSet</code> that just toggles the vacuum on/off. The motion API carries over from Pick &amp; Place; it’s only the end-effector verb that changes, finger pinch → vacuum on.</p> <p>Until next week 👋</p>]]></content><author><name></name></author><category term="updates"/><category term="gsoc"/><category term="roboticsacademy"/><category term="gazebo-harmonic"/><category term="ros2"/><category term="moveit"/><summary type="html"><![CDATA[Giving Palletizing its own branches and exercise, a real moving conveyor belt with a box-lifecycle node, and a suction-gripper variant of the UR5 — URDF, SRDF, controllers, and a patched link attacher.]]></summary></entry><entry><title type="html">Week 3 — Setting up the Palletizing World</title><link href="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week3-prs-shipped/" rel="alternate" type="text/html" title="Week 3 — Setting up the Palletizing World"/><published>2026-06-07T00:00:00+00:00</published><updated>2026-06-07T00:00:00+00:00</updated><id>https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week3-prs-shipped</id><content type="html" xml:base="https://theroboticsclub.github.io/gsoc2026-Ashwani_Kumar/blog/2026/week3-prs-shipped/"><![CDATA[<blockquote> <p><em>GSoC 2026 · Week 3 · Coding period</em></p> </blockquote> <h1 id="week-3-setting-up-the-palletizing-world">Week 3: Setting up the Palletizing World</h1> <p>This week I set up a Palletizing Harmonic universe inside RoboticsAcademy. Most of the work is reused from Pick &amp; Place — same arm (UR5 + Robotiq 85), same MoveIt config, same controllers. What’s new is the scene: six boxes on a conveyor belt and a pallet platform to stack them on.</p> <p>The changes are on my fork for now.</p> <hr/> <h2 id="what-changed">What changed</h2> <p>Four files in RoboticsInfrastructure, one SQL row in RoboticsAcademy.</p> <h3 id="the-world-file">The world file</h3> <p><code class="language-plaintext highlighter-rouge">Industrial/robotiq_description/world/palletizing_arm_harmonic.world</code> — 317 lines. Most of it is copied from <code class="language-plaintext highlighter-rouge">warehouse_arm_harmonic.world</code>. The only new part is the six pickable boxes laid out in a 2×3 grid on the conveyor at <code class="language-plaintext highlighter-rouge">z = 1.04</code>.</p> <p>The arm, conveyor, and table positions are unchanged so all the MoveIt frames and controller config from Pick &amp; Place carry over as-is.</p> <h3 id="the-world_file-patch">The <code class="language-plaintext highlighter-rouge">world_file</code> patch</h3> <p><code class="language-plaintext highlighter-rouge">spawn_robot_warehouse.launch.py</code> had the world path hardcoded. I turned it into a launch argument with the original world as the default:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">DeclareLaunchArgument</span><span class="p">(</span>
    <span class="sh">"</span><span class="s">world_file</span><span class="sh">"</span><span class="p">,</span>
    <span class="n">default_value</span><span class="o">=</span><span class="n">default_world_file</span><span class="p">,</span>
    <span class="n">description</span><span class="o">=</span><span class="sh">"</span><span class="s">Absolute path to the .world file to load in gz sim</span><span class="sh">"</span><span class="p">,</span>
<span class="p">)</span>
</code></pre></div></div> <p>Pick &amp; Place is unaffected. Palletizing passes its own path. 13 lines changed.</p> <h3 id="the-launchers">The launchers</h3> <p><code class="language-plaintext highlighter-rouge">Launchers/palletizing_harmonic.launch.py</code> — sets <code class="language-plaintext highlighter-rouge">GZ_SIM_RESOURCE_PATH</code> and includes <code class="language-plaintext highlighter-rouge">spawn_robot_warehouse.launch.py</code> with <code class="language-plaintext highlighter-rouge">world_file</code> overridden and <code class="language-plaintext highlighter-rouge">launch_rviz=false</code> (RAM starts RViz separately).</p> <p><code class="language-plaintext highlighter-rouge">Launchers/rviz/palletizing_harmonic.launch.py</code> — a near copy of the Pick &amp; Place RViz launcher. Same arm xacro, same MoveIt config, same RViz layout.</p> <h3 id="the-sql-row">The SQL row</h3> <p>One row in <code class="language-plaintext highlighter-rouge">database/exercises/db.sql</code>:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>71	17	68	False
</code></pre></div></div> <p>Exercise 17 (Pick &amp; Place), universe 68 (Palletizing Harmonic), not the default. This makes the new universe show up in the dropdown without any frontend changes.</p> <p>There’s still a row needed in the <code class="language-plaintext highlighter-rouge">database</code> branch of RoboticsInfrastructure (<code class="language-plaintext highlighter-rouge">universes.sql</code>) to wire RAM to the launcher path — that’s the remaining piece.</p> <hr/> <h2 id="demo">Demo</h2> <p><strong>Palletizing universe</strong> — Gazebo Harmonic with the UR5, gripper, and six boxes:</p> <video controls="" width="100%" style="max-width: 720px;"> <source src="/gsoc2026-Ashwani_Kumar/assets/video/palletizing_universe.mp4" type="video/mp4"/> Your browser does not support the video tag. </video> <p><strong>Conveyor belt:</strong></p> <p>The conveyor in the existing Pick &amp; Place world is a static mesh — it looks like a belt but doesn’t move. For palletizing, boxes need to actually travel to the arm’s pickup point, so I wired up Gazebo Harmonic’s native <code class="language-plaintext highlighter-rouge">TrackController</code> plugin on the same model. It moves the contact surface velocity of the belt link. Combined with high anisotropic friction (<code class="language-plaintext highlighter-rouge">mu2=800</code>, <code class="language-plaintext highlighter-rouge">fdir1</code> along the travel direction), boxes get dragged along rather than sliding. The belt speed is controlled by publishing a <code class="language-plaintext highlighter-rouge">gz.msgs.Double</code> to the plugin’s command topic.</p> <p>On top of that I wrote a feeder script that spawns boxes one at a time from behind the belt, waits for each one to reach the arm pickup point, then spawns the next.</p> <video controls="" width="100%" style="max-width: 720px;"> <source src="/gsoc2026-Ashwani_Kumar/assets/video/conveyer.mp4" type="video/mp4"/> Your browser does not support the video tag. </video> <p>The arm doesn’t move yet — there’s no motion plan. The scene is just set up at this point.</p> <hr/> <h2 id="whats-next">What’s next</h2> <ul> <li>Get push access to the main repos and submit upstream PRs.</li> <li>Add the <code class="language-plaintext highlighter-rouge">universes.sql</code> row to the <code class="language-plaintext highlighter-rouge">database</code> branch.</li> <li>Start working on the palletizing motion plan — pick a box, place it on the pallet, repeat for all six.</li> </ul>]]></content><author><name></name></author><category term="updates"/><category term="gsoc"/><category term="roboticsacademy"/><category term="gazebo-harmonic"/><category term="ros2"/><category term="moveit"/><summary type="html"><![CDATA[Added a Palletizing Harmonic universe by reusing most of the Pick & Place stack — a new SDF world, two launchers, and a SQL row to wire it up.]]></summary></entry></feed>