diff --git a/README.md b/README.md
index 051ce02..aa113ac 100644
--- a/README.md
+++ b/README.md
@@ -53,11 +53,23 @@ Supported parameters:
Displays the robot's model in rviz, starts joint_state_publisher and robot_state_publisher
```bash
#### Load generation 3 model
-ros2 launch robotont_description display_simulated_robot.launch.py
+ros2 launch robotont_description display_robot_model.launch.py
```
+Displays the robot's model in rviz, with different frame colors (blue, light_blue, purple, yellow, green, dark_green, black, gray):
+```bash
+#### Load generation 3 model with specific frame color
+ros2 launch robotont_description display_robot_model.launch.py primary_color:=light_blue
+```
+
+Supported formats for `primary_color`:
+- color name: primary_color:=lightblue
+- RGBA (0..1): primary_color:="0.16 0.65 0.98 1.0"
+- RGBA (0..255): primary_color:="41 166 250 255"
+- HEX: primary_color:="#29a6faff"
+
```bash
#### Load generation 2.1 model
-ros2 launch robotont_description display_simulated_robot.launch.py generation:=2.1
+ros2 launch robotont_description display_robot_model.launch.py generation:=2.1
```
#### 2.2. Description
Starts joint_state_publisher and robot_state_publisher, robot model is published on /robot_description topic.
diff --git a/launch/display_robot_model.launch.py b/launch/display_robot_model.launch.py
new file mode 100644
index 0000000..13bc76b
--- /dev/null
+++ b/launch/display_robot_model.launch.py
@@ -0,0 +1,123 @@
+from ament_index_python.packages import get_package_share_path
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument, OpaqueFunction
+from launch.substitutions import LaunchConfiguration, Command
+from launch_ros.actions import Node
+from launch_ros.parameter_descriptions import ParameterValue
+
+
+def _rgba_string_from_user_value(user_value: str) -> str:
+ s = (user_value or "").strip()
+ if not s:
+ raise ValueError("Empty color string")
+
+ # RGBA numeric input (space or comma separated)
+ parts = s.replace(",", " ").split()
+ if len(parts) == 4:
+ nums = [float(p) for p in parts]
+ if any(v > 1.0 for v in nums):
+ nums = [v / 255.0 for v in nums]
+ nums = [min(1.0, max(0.0, v)) for v in nums]
+ return f"{nums[0]} {nums[1]} {nums[2]} {nums[3]}"
+
+ # Hex input
+ if s.startswith("#") and len(s) in (7, 9):
+ hexv = s[1:]
+ r = int(hexv[0:2], 16)
+ g = int(hexv[2:4], 16)
+ b = int(hexv[4:6], 16)
+ a = int(hexv[6:8], 16) if len(hexv) == 8 else 255
+ return f"{r/255.0} {g/255.0} {b/255.0} {a/255.0}"
+
+ # Color names
+ name = s.lower().replace("_", "").replace("-", "")
+ try:
+ from PIL import ImageColor
+ r, g, b, a = ImageColor.getcolor(name, "RGBA")
+ return f"{r/255.0} {g/255.0} {b/255.0} {a/255.0}"
+ except Exception:
+ fallback = {
+ "lightblue": "0.16 0.65 0.98 1.0",
+ "blue": "0.00 0.35 0.90 1.0",
+ "yellow": "1.00 1.00 0.00 1.0",
+ "black": "0.10 0.10 0.10 1.0",
+ "purple": "0.45 0.20 0.65 1.0",
+ "gray": "0.75 0.75 0.75 1.0",
+ "darkgreen": "0.00 0.45 0.25 1.0",
+ "green": "0.00 0.80 0.30 1.0",
+ }
+ if name in fallback:
+ return fallback[name]
+ raise ValueError(
+ f"Unknown color '{user_value}'. Provide RGBA ('0 1 0 1' or '0 255 0 255'), "
+ f"hex ('#00ff00' or '#00ff00ff'), or install Pillow for CSS color names."
+ )
+
+
+def _setup(context, *args, **kwargs):
+ pkg = get_package_share_path("robotont_description")
+
+ model = LaunchConfiguration("model").perform(context).strip()
+ generation = LaunchConfiguration("generation").perform(context).strip()
+
+ primary_in = LaunchConfiguration("primary_color").perform(context)
+
+ if model:
+ robot_model_path = model
+ else:
+ if generation == "2.1":
+ robot_model_path = str(pkg / "urdf/gen2_1/robotont.urdf.xacro")
+ else:
+ # Default to gen3
+ robot_model_path = str(pkg / "urdf/gen3/robotont.urdf.xacro")
+
+ primary_rgba = _rgba_string_from_user_value(primary_in)
+
+ robot_description = ParameterValue(
+ Command([
+ "xacro ", robot_model_path,
+ ' main_color:="', primary_rgba, '"',
+ ]),
+ value_type=str,
+ )
+
+ rsp = Node(
+ package="robot_state_publisher",
+ executable="robot_state_publisher",
+ parameters=[{"robot_description": robot_description}],
+ )
+
+ jsp = Node(
+ package="joint_state_publisher",
+ executable="joint_state_publisher",
+ )
+
+ rviz = Node(
+ package="rviz2",
+ executable="rviz2",
+ name="rviz2",
+ output="screen",
+ arguments=[
+ "-d", LaunchConfiguration("rviz_config"),
+ "--fixed-frame", LaunchConfiguration("rviz_fixed_frame"),
+ ],
+ )
+
+ return [jsp, rsp, rviz]
+
+
+def generate_launch_description():
+ pkg = get_package_share_path("robotont_description")
+ default_rviz_config_path = pkg / "config/robotont_description.rviz"
+
+ return LaunchDescription([
+ DeclareLaunchArgument("model", default_value=""),
+ DeclareLaunchArgument("rviz_config", default_value=str(default_rviz_config_path)),
+ DeclareLaunchArgument("rviz_fixed_frame", default_value="base_link"),
+ DeclareLaunchArgument("generation", default_value="3"),
+
+ DeclareLaunchArgument("primary_color", default_value="0.16 0.65 0.98 1.0"),
+
+ OpaqueFunction(function=_setup),
+ ])
diff --git a/launch/display_simulated_robot.launch.py b/launch/display_simulated_robot.launch.py
deleted file mode 100644
index 11d7a54..0000000
--- a/launch/display_simulated_robot.launch.py
+++ /dev/null
@@ -1,65 +0,0 @@
-from ament_index_python.packages import get_package_share_path
-
-from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument
-from launch.substitutions import Command, LaunchConfiguration, PythonExpression
-from launch_ros.actions import Node
-from launch_ros.parameter_descriptions import ParameterValue
-
-def generate_launch_description():
- package = get_package_share_path('robotont_description')
- default_rviz_config_path = package / 'config/robotont_description.rviz'
-
- model_decl = DeclareLaunchArgument(name='model', default_value='')
- rviz_config_decl = DeclareLaunchArgument(name='rviz_config', default_value=str(default_rviz_config_path))
- rviz_fixed_frame_decl = DeclareLaunchArgument(name='rviz_fixed_frame', default_value='base_link')
- generation_decl = DeclareLaunchArgument(name='generation', default_value='3')
-
- model_arg = LaunchConfiguration('model')
- rviz_config_arg = LaunchConfiguration('rviz_config')
- rviz_fixed_frame_arg = LaunchConfiguration('rviz_fixed_frame')
- generation_arg = LaunchConfiguration('generation')
-
- robot_model_path = PythonExpression([
- '"" if "', model_arg,
- '" else "', str(package / "urdf/"), '" + (',
- '"/gen2_1/robotont.urdf.xacro" if "', generation_arg, '" == "2.1" else "/gen3/robotont.urdf.xacro")'
- ])
-
- robot_description = ParameterValue(Command(['xacro ', robot_model_path]), value_type=str)
-
- robot_state_publisher_node = Node(
- package='robot_state_publisher',
- executable='robot_state_publisher',
- parameters=[{ 'robot_description': robot_description }]
- )
-
- joint_state_publisher_node = Node(
- package='joint_state_publisher',
- executable='joint_state_publisher'
- )
-
- rviz_node = Node(
- package='rviz2',
- executable='rviz2',
- name='rviz2',
- output='screen',
- arguments=[
- '-d',
- rviz_config_arg,
- '--fixed-frame',
- rviz_fixed_frame_arg
- ]
- )
-
- return LaunchDescription(
- [
- model_decl,
- generation_decl,
- rviz_config_decl,
- rviz_fixed_frame_decl,
- joint_state_publisher_node,
- robot_state_publisher_node,
- rviz_node
- ]
- )
diff --git a/meshes/gen3/encoder_button.STL b/meshes/gen3/encoder_button.STL
new file mode 100644
index 0000000..dc1cb81
Binary files /dev/null and b/meshes/gen3/encoder_button.STL differ
diff --git a/meshes/gen3/frame_module_pla.STL b/meshes/gen3/frame_module_pla.STL
new file mode 100644
index 0000000..1bcae46
Binary files /dev/null and b/meshes/gen3/frame_module_pla.STL differ
diff --git a/meshes/gen3/frame_module_spacers.STL b/meshes/gen3/frame_module_spacers.STL
new file mode 100644
index 0000000..a812095
Binary files /dev/null and b/meshes/gen3/frame_module_spacers.STL differ
diff --git a/meshes/gen3/motor_module2.STL b/meshes/gen3/motor_module2.STL
new file mode 100644
index 0000000..1c029aa
Binary files /dev/null and b/meshes/gen3/motor_module2.STL differ
diff --git a/meshes/gen3/ssd1306.STL b/meshes/gen3/ssd1306.STL
new file mode 100644
index 0000000..806a407
Binary files /dev/null and b/meshes/gen3/ssd1306.STL differ
diff --git a/meshes/gen3/wheel_adapter_v05_one_bolt.STL b/meshes/gen3/wheel_adapter_v05_one_bolt.STL
new file mode 100644
index 0000000..9e79390
Binary files /dev/null and b/meshes/gen3/wheel_adapter_v05_one_bolt.STL differ
diff --git a/meshes/shared/2.75rim.STL b/meshes/shared/2.75rim.STL
new file mode 100644
index 0000000..217f5ef
Binary files /dev/null and b/meshes/shared/2.75rim.STL differ
diff --git a/meshes/shared/2.75roller.STL b/meshes/shared/2.75roller.STL
new file mode 100644
index 0000000..0674185
Binary files /dev/null and b/meshes/shared/2.75roller.STL differ
diff --git a/urdf/gen3/base.urdf.xacro b/urdf/gen3/base.urdf.xacro
index eea4155..65573f3 100644
--- a/urdf/gen3/base.urdf.xacro
+++ b/urdf/gen3/base.urdf.xacro
@@ -5,6 +5,7 @@
-
+
@@ -48,7 +49,25 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -114,6 +133,28 @@
/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -135,5 +176,27 @@
rpy="${pi / 2} 0 ${pi / 2}"
/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/urdf/gen3/motor.urdf.xacro b/urdf/gen3/motor.urdf.xacro
index 19850a7..87f90e6 100644
--- a/urdf/gen3/motor.urdf.xacro
+++ b/urdf/gen3/motor.urdf.xacro
@@ -23,7 +23,7 @@
-
+
diff --git a/urdf/gen3/robotont.urdf.xacro b/urdf/gen3/robotont.urdf.xacro
index 26f2d31..64ddd58 100644
--- a/urdf/gen3/robotont.urdf.xacro
+++ b/urdf/gen3/robotont.urdf.xacro
@@ -7,6 +7,9 @@
+
+
+
@@ -16,6 +19,7 @@
+
@@ -28,7 +32,8 @@
-
+
@@ -22,7 +22,7 @@
-
+
@@ -129,6 +129,26 @@
no_of_roller="6"
prefix="${prefix}"
/>
+
+
+
+
diff --git a/urdf/shared/roller.urdf.xacro b/urdf/shared/roller.urdf.xacro
index 3953d2c..f8bd66c 100644
--- a/urdf/shared/roller.urdf.xacro
+++ b/urdf/shared/roller.urdf.xacro
@@ -16,7 +16,7 @@
-
+
@@ -35,8 +35,8 @@