diff --git a/VirtualRobot/CMakeLists.txt b/VirtualRobot/CMakeLists.txt
index 64ba542d46056a7fd704d6f8be58c53ae1f31a6c..d8608e962d692d2d1eeb4f3c059f87705fc0410a 100644
--- a/VirtualRobot/CMakeLists.txt
+++ b/VirtualRobot/CMakeLists.txt
@@ -329,6 +329,7 @@ SET(SOURCES
     MJCF/elements/types/statistic.cpp
     MJCF/elements/types/tendon.cpp
     MJCF/elements/types/visual.cpp
+    MJCF/visitors/Collector.cpp
     
     Nodes/CameraSensor.cpp
     Nodes/CameraSensorFactory.cpp
@@ -538,6 +539,8 @@ SET(INCLUDES
     MJCF/elements/types/statistic.h
     MJCF/elements/types/tendon.h
     MJCF/elements/types/visual.h
+    MJCF/visitors.h
+    MJCF/visitors/Collector.h
     
     Nodes/CameraSensor.h
     Nodes/CameraSensorFactory.h
diff --git a/VirtualRobot/MJCF/visitors.h b/VirtualRobot/MJCF/visitors.h
new file mode 100644
index 0000000000000000000000000000000000000000..f2cec55304656772d1fcdfddcef23870ac4deb99
--- /dev/null
+++ b/VirtualRobot/MJCF/visitors.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#include "visitors/Collector.h"
diff --git a/VirtualRobot/MJCF/visitors/Collector.cpp b/VirtualRobot/MJCF/visitors/Collector.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3f838bce55a8fe3bdd79dc2948abc2cdb042c234
--- /dev/null
+++ b/VirtualRobot/MJCF/visitors/Collector.cpp
@@ -0,0 +1,7 @@
+#include "Collector.h"
+
+
+namespace mjcf
+{
+
+}
diff --git a/VirtualRobot/MJCF/visitors/Collector.h b/VirtualRobot/MJCF/visitors/Collector.h
new file mode 100644
index 0000000000000000000000000000000000000000..bb8ed7e45f3c50c5ea7177b91103c7371d14f2a5
--- /dev/null
+++ b/VirtualRobot/MJCF/visitors/Collector.h
@@ -0,0 +1,49 @@
+#pragma once
+
+#include "../elements.h"
+
+
+namespace mjcf
+{
+    
+    /**
+     * @brief A visitor that collects all elements of the specified type.
+     */
+    template <class ElementT>
+    class Collector : public Visitor
+    {
+    public:
+        
+        static std::vector<ElementT> collect(Document& document, AnyElement root);
+        
+        Collector(Document& document) : Visitor(document) {}
+        
+        // Visitor interface
+        virtual bool visitEnter(const AnyElement& element) override
+        {
+            if (element.is<ElementT>())
+            {
+                collected.push_back(element.as<ElementT>());
+            }
+            return true;
+        }
+        
+        std::vector<ElementT>& getCollected() { return collected; }
+        const std::vector<ElementT>& getCollected() const  { return collected; }
+        
+        
+    private:
+        
+        std::vector<ElementT> collected;
+        
+    };
+    
+    template <class ElementT>
+    std::vector<ElementT> Collector<ElementT>::collect(Document& document, AnyElement root)
+    {
+        mjcf::Collector<ElementT> collector(document);
+        root.accept(collector);
+        return collector.getCollected();
+    }
+}
+