PointCloud Publisher

This example demonstrates how to create a static point cloud using 3D Slicer’s built-in features and publish it over a ROS 2 topic to be visualized in RViz.

Creating and Publishing the Point Cloud in 3D Slicer

You can run the publisher script in one of two ways:

Option A – run the script directly from a terminal using the slicer launcher’s --python-script argument (the script executes automatically after Slicer starts):

ros2 run slicer_ros2_module slicer --python-script $(ros2 pkg prefix slicer_ros2_module)/share/slicer_ros2_module/docs/code/point_cloud_publisher.py

Option B – paste the code into Slicer’s Python Interactor (Ctrl + 3 or through the Developer Tools menu):

import slicer
import vtk
import qt

# 1. Build sphere geometry and a transform filter to animate it
sphere = vtk.vtkSphereSource()
sphere.SetRadius(15.0)
sphere.SetThetaResolution(20)
sphere.SetPhiResolution(20)
sphere.Update()

transform = vtk.vtkTransform()
transformFilter = vtk.vtkTransformPolyDataFilter()
transformFilter.SetInputConnection(sphere.GetOutputPort())
transformFilter.SetTransform(transform)
transformFilter.Update()

# 2. Create a Model node seeded with the initial geometry
modelNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLModelNode', 'AnimatedPointCloud')
modelNode.SetAndObservePolyData(vtk.vtkPolyData())
modelNode.GetPolyData().DeepCopy(transformFilter.GetOutput())
modelNode.CreateDefaultDisplayNodes()

# 3. Set up the ROS 2 publisher
rosLogic = slicer.util.getModuleLogic('ROS2')
rosNode = rosLogic.GetDefaultROS2Node()
pub = rosNode.CreateAndAddPublisherNode('PolyData', '/slicer_point_cloud')
pub.SetFrameId('world')

# 4. Auto-republish whenever the model node is marked modified.
#    This also fires if the user edits the model interactively in Slicer.
def onModelModified(caller, event):
    pub.Publish(caller.GetPolyData())

modelNode.AddObserver(vtk.vtkCommand.ModifiedEvent, onModelModified)

# Publish the initial frame immediately
pub.Publish(modelNode.GetPolyData())

# 5. Drive the animation at 10 Hz: rotate the sphere 5 degrees per step
angle = [0.0]

def animate():
    angle[0] = (angle[0] + 5.0) % 360.0
    transform.Identity()
    transform.RotateZ(angle[0])
    transformFilter.Update()
    modelNode.GetPolyData().DeepCopy(transformFilter.GetOutput())
    modelNode.Modified()  # triggers onModelModified -> pub.Publish()

timer = qt.QTimer()
timer.setInterval(100)  # 10 Hz
timer.connect('timeout()', animate)
timer.start()

# Keep a reference so the timer is not garbage-collected
slicer.modules.AnimatedPointCloudTimer = timer

print("Animated point cloud publisher started at 10 Hz on /slicer_point_cloud.")
print("The model will also republish automatically if you edit it in Slicer.")

Once executed, Slicer will create a local model displaying the sphere, and publish the points to the ROS 2 topic /slicer_point_cloud with the frame_id set to world.

Visualizing in RViz

To visualize this published point cloud in RViz:

  • Open a terminal, source the ROS 2 setup script, and launch RViz:

    source /opt/ros/jazzy/setup.bash
    rviz2
    
  • Configure RViz Display:
    • In the Global Options panel, set the Fixed Frame to world.

    • Click the Add button at the bottom left of the window.

    • Switch to the By topic tab, expand /slicer_point_cloud, select PointCloud2, and click OK.

    • (Optional) In the newly added PointCloud2 display properties, increase the Size (m) parameter to 0.2 or larger to render the sphere points clearly in the 3D grid.