ROS 2 MacOS support (Installing and Running ros 2 on MacOS).
This write up and explanation will guide you through the process of installing ROS 2 and running it on macOS (for those using macOS) for robotics programming (ROS) and general programming. Before we commence, we will have a discussion on what ROS 2 is.
What is ROS 2 ?
Robot Operating System 2, or ROS 2, is a framework for creating and controlling robotic systems. It offers features, tools, and frameworks that make robot software development easier. As a replacement for ROS (Robot Operating System), ROS 2 fixes the shortcomings of its predecessor, especially for applications that are crucial to safety and commerce. It is used for programming various robotic systems such as mobile robots, industrial robots (manipulators), drones, and more. It is widely used worldwide across various robotics engineering sectors. In contrast to ROS 1, ROS 2 is compatible with a number of operating systems, including as Windows, Linux, and macOS. This adaptability expands the use of ROS 2 in a variety of contexts and allows developers to work on the platform of their choice. To learn more about ROS 2, be sure to check out .
Installing ROS 2 on Linux Ubuntu OS is quite different from that on macOS. On the Linux Ubuntu operating system, it’s quite straightforward to install ROS 2, but the process for macOS is different. On MacOS we will be using pixi by prefix_dev for the installation and running.
Installing ROS 2 on MacOS using pixi by prefix_dev.
The required steps are needed to be followed in installing pixi and also running ros 2 on MacOs.
- Installing pixi on MacOS .
- Installing ROS 2 distro version via pixi.
- Testing simple simulation on ROS 2 using/via pixi .
- Running simple robot navigation via pixi .
- Spawning a robot into gazebo world using pixi.
Let the show begin😁
Installing pixi on MacOS:
To begin the main installation of pixi, you first need to install Homebrew. Homebrew is a package manager for macOS (and Linux) that simplifies the process of installing, managing, and updating tools and applications via the command line. To install it, open your terminal and paste the following command:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
curl -fsSL https://pixi.sh/install.sh | bash
You shoud now be able to see the installation like the illustration below:
Once installed using the command above, pixi will be automatically set up on your macOS. It will then be ready for use. You can learn more here.
Installing ROS 2 distro version via pixi:
In ROS 2, there are various types of distributions (distros), and these distros are compatible with different Linux Ubuntu operating systems (OS). For example, ROS 2 Humble Hawksbill works on Ubuntu 22.04 LTS (Jammy). We will be installing ROS 2 Humble Hawksbill , which has long term support, on Ubuntu 20.04. You can read about various distributions here . This time, we will be installing ROS 2 Humble Hawksbill on macOS using pixi. You need to have pixi installed beforehand. If you haven’t done so, check the previous instructions. Then, navigate to your terminal on macOS and paste the command below to install ROS 2 Humble Hawksbill.
pixi add ros-humble-desktop
pixi init robot_ros2_project -c robostack-staging -c conda-forge
cd robot_ros2_project
Then build the workspace with the command below:
pixi run colcon build
The above command creates a workspace called “robot_ros2_project” via pixi . We can also say we are creating a pixi project using the command above . So the project name we working with is “robot_ros2_project” . If this is done we will be able to see .gitattributes , .gitignore , pixi.toml located in the workspace called “robot_ros2_project” . The important files we should look at is the “pixi.toml” . The pixi.toml
file contains the metadata for a ROS 2 project, such as its name, version, supported platforms, and dependencies. Additionally, it outlines activation scripts for configuring the project environment and guaranteeing that all required packages are installed. Just like an example below:
[project]
name = "robot_ros2_project" # the name of the pixi project / workspace.
version = "0.1.0"
description = "running ros 2 via pixi" # the description of the project.
authors = ["User Name <user.name@email.url>"]. # the name of the author
channels = ["robostack-staging", "conda-forge"]
# Your project can support multiple platforms, the current platform will be automatically added.
platforms = ["linux-64"]
[tasks]
[dependencies]
By installing ROS 2 humble hawksbill . Copy and paste the command below into your terminal .
pixi add ros-humble-desktop
The command above installs ros 2 distro on your macOS. It will add ros-humble-desktop into pixi.toml
as an installed dependecy. Next we will be installing a package called “turtlesim” to test out with pixi. Open your terminal and paste the command below:
pixi add ros-humble-turtlesim
Once that is done, we have both the humble and turtlesim installed on our MacOS ready for use . To run the “turtlesim” node . Open another terminal and type the command
pixi run ros2 run turtlesim turtlesim_node
Once you run the above command you will see a mini turtle pop up on the screen like the illustration below.
We first step done , you happy right ? Now let’s go get a drink and continue with the other steps 🍻
Programming turtlesim using .py or .cpp, we can program turtlesim to execute various task . To program , we need to create a package in which the node will be located. We will be working with .py
files since they are straightforward and easy for beginners to understand. To create a package paste the command below:
pixi run ros2 pkg create - build-type ament_python - destination-directory src - node-name move robot_node
This should be created in the src directory of your workspace robot_ros2_project/src . In the workspace you will see a directory called robot_node , the robot_node is where our move.py node is located . Open the move.py with your favourite IDE . I use Vscode for development , once the file is open . Copy and paste the node below into the .py script.
#!/usr/bin/env python3
# import necessary libraries....
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
'''
A simple ROS 2 node that sends velocity messages to the wheels of a robot
to drive at a linear velocity/speed of 0.2 m/s.
'''
# Implementation class
class Move(Node):
def __init__(self):
super().__init__("move_robot_code") # initailize your node
self.publisher = self.create_publisher(Twist ,"/turtle1/cmd_vel",100) # topic name
self.timer = 0.5
self.time__ = self.create_timer(self.timer,self.move_bot)
self.velocity_message = Twist() # define the twist message type
# drive turtlesim method
def move_bot(self):
self.velocity_message.linear.x = 0.2
self.publisher.publish(self.velocity_message)
self.get_logger().info(f"The robot drives forward with linear velocity: {self.velocity_message.linear.x:.2f}")
# Main function
def main(args=None):
rclpy.init(args=args)
activate = Move()
rclpy.spin(activate)
rclpy.shutdown()
if __name__ == '__main__':
main() # called the main() function
Save the move.py script and execute the node . In the setup.py make a few modification by adding the line below in the :
entry_points={
'console_scripts': [
'move_robot=robot_node.move:main', # added executable line .
],
},
'move_robot=robot_node.move:main',
Make sure the turtlesim node is still up , then run by executing the node in the robot_ros2_project workspace using the command below :
pixi run colcon build
pixi run ros2 run robot_node move_robot
You will see the mini turtle moving forward related to what we have programmed it to execute.
Testing simple simulation on ROS 2 using/via pixi .
Pixi allows us to execute a variety of simulations and tasks linked to ROS 2. These tasks can include everything from operating a basic simulated robot and directing it to carry out particular tasks to putting a series of SLAM algorithms into practice, which allow a robot to move, map its surroundings, and localize itself.
To do this, add the relevant command to “pixi run.” Let’s perfrom an example of how this can be done. In your src directory clone the repository. We will spawn a robot into a Gazebo created world.
cd robot_ros2_project/src
git clone https://github.com/Daviesss/Ros-2-simulated-robot..git
After that has been done, we need to build the workspace . To build the workspace , follow the command below.
cd robot_ros2_project
pixi run colcon build
Now that the workspace has been built. Let’s run spawn our robot into gazebo world .
cd robot_ros2_project
pixi run ros2 launch robot show.robot.launch.py world:=<path_to_the_world_file_located_in_the_package>
NOTE : The path_to_the_world_file_located_in_the_package is the location to your .world file. In my case the path is “ /Users/daviesiyanuoluwaogunsina/my_ros2_project/src/Ros-2-simulated-robot./worlds/new.world” . So my launch file will be .
pixi run ros2 launch robot show.robot.launch.py world:=/Users/daviesiyanuoluwaogunsina/my_ros2_project/src/Ros-2-simulated-robot./worlds/new.world
After running the above command , you should see the world pop up with a differential drive robot spawned in it .
We can now visualize the robot sensor data with rviz2 using the command
pixi run rviz2
You will see the rviz2 gui pop up to the screen like the picture illustration below:
Rviz2 is used for visualizing sensor data in real time from sensors like Lidars, stereo cameras , etc. As we discussed earlier, the file pixi.toml
contains all dependencies, a description of the maintainer, and other important information. It is similar to the package.xml file in ROS 2, which also lists the installed packages/dependencies and includes a description of the package or project’s maintainer. The procedure is to use “pixi run” along with the launch file to spawn the robot into the Gazebo world. Instead of just using “ros2 launch <package_name> <launch_file>”, you should use “pixi run ros2 launch <package_name> <launch_file>”.
- pixi run — the pixi run is a pixi command that is used in running various nodes .
- pixi add — the pixi add is a pixi command that is used to install various packages and dependecies .
So the major deal here is that we can now work with pixi along with our node files . For example, we can create a ROS 2 project and assign pixi to run the nodes .
Running simple robot navigation with pixi .
Robot navigation refers to the ability of a robot to move from an initial state to a final state or destination. This means that a robot can transition from an initial pose to a final pose. For example, a robot can map its surroundings and localize itself on the predefined map. Once the robot can estimate its pose on the map, a goal can be sent to its base for navigation. Now, let’s run an example to run turtlebot3 navigation stack using the pixi run command. Follow this steps to run the simulation. Once that is done we should see the robot spawned into gazebo world and already created map for the robot to navigate .
git clone https://github.com/prefix-dev/pixi.git
cd pixi/examples/ros2-nav2
pixi run start
Spawning a robot into gazebo world using pixi.
There are different ways to spawn a robot into a Gazebo world, ranging from using a ROS 2 launch file to specifying the path of the .xacro
or URDF file in the launch file. The .xacro
file defines the links and joints of the robot you want to spawn in Gazebo. An example of a .xacro
file is as follows:
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" >
<xacro:include filename="inertial.xacro"/>
<material name="white">
<color rgba="1 1 1 1" />
</material>
<material name="orange">
<color rgba="1 0.3 0.1 1"/>
</material>
<material name="blue">
<color rgba="0.2 0.2 1 1"/>
</material>
<material name="black">
<color rgba="0 0 0 1"/>
</material>
<!-- BASE LINK (Starting referrence frame of the robot....)-->
<link name="base_link">
</link>
<!-- CHASSIS LINK -->
<joint name="chassis_joint" type="fixed">
<parent link="base_link"/>
<child link="chassis"/>
<origin xyz="-0.1 0 0"/>
</joint>
<link name="chassis">
<visual>
<origin xyz="0.15 0 0.075"/>
<geometry>
<box size="0.6 0.6 0.15"/> <!--was 0.3 0.3 0.15-->
</geometry>
<material name="blue"/>
</visual>
<collision>
<origin xyz="0.15 0 0.075"/>
<geometry>
<box size="0.3 0.3 0.15"/>
</geometry>
</collision>
<xacro:inertial_box mass="0.5" x="0.3" y="0.3" z="0.15">
<origin xyz="0.15 0 0.075" rpy="0 0 0"/>
</xacro:inertial_box>
</link>
<!-- Adding colour/fixing the colours to our urdf robot -->
<!-- chassis link colour (Adding chassis link colour) -->
<gazebo reference="chassis">
<material>Gazebo/Blue</material>
</gazebo>
<!-- LEFT WHEEL OF ROBOT -->
<joint name="left_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="left_wheel"/>
<origin xyz="0 0.175 0" rpy="-${pi/2} 0 0" />
<axis xyz="0 0 1"/>
</joint>
<link name="left_wheel">
<visual>
<geometry>
<cylinder radius="0.05" length="0.04"/>
</geometry>
<material name="black"/>
</visual>
<collision>
<geometry>
<cylinder radius="0.05" length="0.04"/>
</geometry>
</collision>
<xacro:inertial_cylinder mass="0.1" length="0.04" radius="0.05">
<origin xyz="0 0 0" rpy="0 0 0"/>
</xacro:inertial_cylinder>
</link>
<gazebo reference="left_wheel">
<material>Gazebo/Black</material>
</gazebo>
<!-- RIGHT WHEEL OF ROBOT -->
<joint name="right_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="right_wheel"/>
<origin xyz="0 -0.175 0" rpy="${pi/2} 0 0" />
<axis xyz="0 0 -1"/>
</joint>
<link name="right_wheel">
<visual>
<geometry>
<cylinder radius="0.05" length="0.04"/>
</geometry>
<material name="black"/>
</visual>
<collision>
<geometry>
<cylinder radius="0.05" length="0.04"/>
</geometry>
</collision>
<xacro:inertial_cylinder mass="0.1" length="0.04" radius="0.05">
<origin xyz="0 0 0" rpy="0 0 0"/>
</xacro:inertial_cylinder>
</link>
<gazebo reference="right_wheel">
<material>Gazebo/Black</material>
</gazebo>
<!-- DOLLY WHEEEL JOINT -->
<joint name="dolly_wheel_joint" type="fixed">
<parent link="chassis"/>
<child link="dolly_wheel"/>
<origin xyz="0.24 0 0"/>
</joint>
<link name="dolly_wheel">
<visual>
<geometry>
<sphere radius="0.05"/>
</geometry>`
<material name="black"/>
</visual>
<collision>
<geometry>
<sphere radius="0.05"/>
</geometry>
</collision>
<xacro:inertial_sphere mass="0.1" radius="0.05">
<origin xyz="0 0 0" rpy="0 0 0"/>
</xacro:inertial_sphere>
</link>
<gazebo reference="dolly_wheel">
<material>Gazebo/Black</material>
<mu1 value="0.100"/>
<mu2 value="0.100"/>
</gazebo>
</robot>
This .xacro
file's path is now linked to a specific launch file, which we will use to spawn the robot into Gazebo .
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription,DeclareLaunchArgument
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import Node
# from launch.actions import ExecuteProcess
def generate_launch_description():
# Include the robot_state_publisher launch file, provided by our own package. Force sim time to be enabled
# !!! MAKE SURE YOU SET THE PACKAGE NAME CORRECTLY !!!
package_name='robot' #Variable name that saves the package name called robot.
show = IncludeLaunchDescription(
PythonLaunchDescriptionSource([os.path.join(
get_package_share_directory(package_name),'launch','robot.launch.py'
)]), launch_arguments={'use_sim_time': 'true'}.items()
)
# Include the Gazebo launch file, provided by the gazebo_ros package
gazebo = IncludeLaunchDescription(
PythonLaunchDescriptionSource([os.path.join(
get_package_share_directory('gazebo_ros'), 'launch', 'gazebo.launch.py')]),
)
# Run the spawner node from the gazebo_ros package. The entity name doesn't really matter if you only have a single robot.
spawn_entity = Node(package='gazebo_ros', executable='spawn_entity.py',name='spawn_entity',
arguments=['-topic', 'robot_description',
'-entity', 'my_bot'],
output='screen')
##########################################################
# Added this for spawning robot in gazebo(ros 2 humble)
# diff_drive_spawner = Node(
# package="controller_manager",
# executable="spawner",
# arguments=["diff_cont"],
# )
# joint_broad_spawner = Node(
# package="controller_manager",
# executable="spawner",
# arguments=["joint_broad"],
# )
##########################################################
# set_contoller_manager_use_sim_time = ExecuteProcess(
# cmd=['ros2', 'param', 'set', '/controller_manager', 'use_sim_time', 'true'],
# output='screen')
# Launch them all!
return LaunchDescription([
DeclareLaunchArgument(
'use_sim_time',
default_value='false',
description='Use sim time if true'),
show,
gazebo,
spawn_entity,
# diff_drive_spawner,
# joint_broad_spawner
# set_contoller_manager_use_sim_time,
])
We can now assign the pixi run command to the launch file to spawn the robot into gazebo world . By executing the command below :
pixi run ros2 launch robot show.robot.launch.py world:=<path_to_the_world_file_located_in_the_package>
We are now prepared and equipped to utilize Pixi on our macOS after completing all the necessary requirements. I advise having a unified memory standard with at least 16GB of RAM. I think this should function well, but I haven’t tested it on a lesser specification.
To find out more about pixi by prefix_dev . You can read here .
Installing and running ROS 2 on macOS using pixi is a seamless way to integrate the power of ROS 2 into your macOS development environment. Through this guide, we’ve explored the essential steps, from setting up Pixi and installing ROS 2 distributions to running simulations and launching robots in Gazebo. By following these instructions, you are well on your way to leveraging ROS 2’s capabilities for robotics programming on macOS.
Whether you are a beginner exploring the world of robotics or an advanced developer pushing the boundaries of robotic systems, this process enables you to bring your ideas to life on a platform of your choice. Remember to explore and experiment, as the potential applications with ROS 2 are limitless. Happy coding, and let your robotics journey begin! 🚀😎