Introducing gz_sim_spray_painting_plugin : Simulate spray painting in Gazebo!

While working on a project that involved spray painting using a robotic manipulator, I ran into a gap in Gazebo. There is simply no built-in way to simulate a spray painting application. So, I built one :hammer_and_wrench:

Introducing gz_sim_spray_painting_plugin: A Gazebo Harmonic system plugin that lets you attach a spray nozzle to your robot and visualise paint coverage directly in your simulation.

Repository: GitHub - topguns837/gz_sim_spray_painting_plugin


spray_painting_car_demo


How it works

The plugin implements two independent pipelines that run every simulation step:

  1. Ray-cast paint patches: A cone of rays is fired from the nozzle every simulation step. Wherever a ray hits a surface, a coloured disc is placed flush against it. This works on any geometry: simple boxes and cylinders, complex meshes like the Prius car model in the demo, or anything in between.

  2. Particle spray cloud (optional): A particle emitter at the nozzle gives you the visual mist you would expect from a spray gun. It can be turned off with a single SDF flag (<enable_particle_emitter>false</enable_particle_emitter>) if you do not need the visual effect. That is handy for headless simulations, training ML models on spray-path data, or just keeping the simulation fast.

Triggering spray from ROS 2 the spray ON/OFF signal is a standard gz.msgs.Boolean Gazebo transport topic (default /spray_paint/trigger, configurable via SDF). It can be bridged to a ROS 2 topic with a single ros_gz_bridge node, no plugin changes needed:


Demo worlds included

  • demo_car : UR5e arm + MoveIt 2 + Prius model full autonomous spray demo

  • demo_cube : Standalone nozzle, fast to load good for verifying the plugin

  • demo_cylinder : Cylindrical target

  • demo_ellipsoid : Ellipsoidal target

No local ROS 2 or Gazebo install needed, the whole stack runs in Docker. A single startScript.sh entrypoint handles Docker build, code build, and simulation launch via an interactive menu.


Integrating into your own robot

Drop the plugin block into any URDF or SDF link:


<plugin filename="libSprayPaintPlugin.so" 
  name="gz::sim::systems::SprayPaintPlugin">

<nozzle_link>my_nozzle_link</nozzle_link>

<cone_half_angle_deg>20</cone_half_angle_deg>

<cone_max_range>1.5</cone_max_range>

<spray_color>0.1 0.5 1.0 1.0</spray_color>

<spray_topic>/my_robot/spray</spray_topic>

</plugin>

All parameters are optional with sensible defaults (15° half-angle cone, 1 m range, red paint). Full parameter table in the README.

Built with: ROS 2 Humble · Gazebo Harmonic · C++17


Looking for feedback & contributors

I’d love to hear from anyone working on:

  • Industrial robotics painting, coating, or surface-coverage tasks

  • Gazebo plugin development feedback on the ECS/raycasting approach

  • Anyone who hit the same gap and worked around it differently

Bugs and feature requests are tracked via GitHub Issues. Happy to chat with anyone interested in contributing!

11 Likes

That looks amazing! It would be great if you could submit a PR to add it to Projects using Gazebo — Gazebo jetty documentation

One thing I noticed is that you are creating visual entities into the ECM for the paint disc. This might have performance implications as the number of discs grows. Have you considered using markers (see gz-sim/examples/standalone/marker at 9ed5127b590451cfd1c24a156c0ce99ede525c51 · gazebosim/gz-sim · GitHub )?

Hey @azeey, thanks for the suggestion ! I would love to submit a PR for this.

Before doing so, I wanted to confirm: The plugin is currently built and tested on Gazebo Harmonic. Since the projects_using_gazebo page lists projects using “modern Gazebo” without a specific version requirement, I assume a Harmonic-based plugin would be acceptable. Could you confirm this, or would you prefer I upgrade the plugin to support Jetty first before submitting the PR?

I was not aware of the Marker API in Gazebo. After checking through the example you shared and the gz::msgs::Marker definition, I can see that this might be a good alternative to spawning visual entities using ECM. I will try re-implementing the paint patches using markers.

stands for anything available now under the name Gazebo (or Ignition Gazebo for some time in the past). The other (non-modern) Gazebo is now called Gazebo Classic.

1 Like

This is great as my kids are getting really interested in graffiti, for some reason.

1 Like

It should be fine. That list has things that are still on fortress. Cool (and very practical) plugin though.

1 Like