ROS
ROS stands for Robot Operating System. It is a collection of libraries and utilities that make up a software framework that is a node based publisher-subscriber model. It allows developers to quickly write modular code for a robot that does a specifc task while providing a layer of useful libraries and introspection tools. The publisher-subscriber principal allows for minimal coupling by leaving it up to each node to subscribe and publish data as it wishes to a common bus that is accessible to all other nodes.
The following are a set of notes I have taken while learning how to use ROS, and applying ROS to build an autonomous street robot name Primo.
ROS Package
A package in ROS is the “software organization unit of ROS code”. Think of a package as software project that does one task. It might contain multiple nodes, aka executable ROS processes, and a bunch of configuration information.
For example, say we have a robot with a robotic arm that uses a motor that is connected to an output shaft via some gearing, and also cause we're fancy we have a torque transducers on the output shaft along with an encoder for position information. That is a good amount of stuff for just one arm joint, but this “arm joint stuff” perfect candidate for a package. We can boil it down to set of tasks relating to the joint and cease a package. On top of that we can create the package in such a way that it can be configurable, and applied to more of the similar joints. The package would be responsible for the following:
Sketch of package responsibilities
- Take in commands to control the joint in different ways
- Position control to send the joint in a specific place
- Force control to push against something or grip with a set force
- Velocity control for a spinning joint such as a screwing task
- Make sure the joint obeys within its limits of force, torque, velocity and position
- Hold a set of configuration data such as position of the joint, name, etc
- Have a driver interface to the motor controller which could be a serial, or CAN connection
- Monitor any fault conditions and have a mechanism in place for what to do when bad things happen.
With that sketch of responsibilities we can create engineering requirements that fully define the tasks and then go on to either try to find a package that does this already, or make our own. If we decide to make our own we'll build out our package and have nodes that handle each of those tasks that can easily talk to each other etc. Hope that gives you a good idea of what a ROS package is!
ROS Node
Nodes: A node is an executable that uses ROS to communicate with other nodes. Messages: ROS data type used when subscribing or publishing to a topic. Topics: Nodes can publish messages to a topic as well as subscribe to a topic to receive messages. Master: Name service for ROS (i.e. helps nodes find each other) rosout: ROS equivalent of stdout/stderr roscore: Master + rosout + parameter server (parameter server will be introduced later)
Use the rosnode command to get information about currently running nodes.
Package Structure
The following is the simplest structure of a ROS package:
my_package/ CMakeLists.txt package.xml
The manifest is a file called package.xml
and is a description of the package.
A package in a catkin workspace looks like this:
workspace_folder/ -- WORKSPACE src/ -- SOURCE SPACE CMakeLists.txt -- 'Toplevel' CMake file, provided by catkin package_1/ CMakeLists.txt -- CMakeLists.txt file for package_1 package.xml -- Package manifest for package_1 ... package_n/ CMakeLists.txt -- CMakeLists.txt file for package_n package.xml -- Package manifest for package_n
ROS Services
Services are another way nodes can communicate with each other. Services allow nodes to send a request and receive a response.
You can look up the type of a service. Then using rossrv
you can get more information
on the that service. For example:
rosservice type /spawn | rossrv show
There is also such a thing as the ROS parameter server. It stores data.
You can look at all this data by using the rosparam
command. You can
list the parameters, set them and even save and load them.
Service files (srv) are just like msg files, except they contain two parts: a request and a response. The two parts are separated by a '—' line. Here is an example of a srv file:
int64 A
int64 B
---
int64 Sum
Adding a ROS Service
Here are the steps to add a ROS service to a package. These are similar to adding a message.
- Add a
.srv
file to thesrv/
directory of a package - Make sure
message_generation
andmessage_runtime
build and runtime dependencies are enabled in thepackage.xml
file. - Add message_generation to the find_package() function
- Make sure the service is added to the add_service_files() function
When this is done, running catkin_make install
will build all the necessary stuff and also generate the header files required.
Building a ROS Service/Client
A service/client is another form of communication in ROS. Instead of relying on publisher/subscriber messages, which are asynchronous, you have a synchronous service based communication. A client calls a service and waits for a response.
- First define the service you will use in the package. See Adding a ROS service.
- Write a server node in the
srv/
folder- Server node includes the generated header for the service
- Server node has a function that returns a boolean and takes in the request and the response of the service.
- Server node has a main function that:
- calls
ros::init
- has a
ros::NodeHandle
- advertises the service using
ros::ServiceServer
ros::spin();
- Write a client node in the
src/
folder- Has a main
ros::init()
ros::NodeHandle
ros::ServiceClient
- calls
client.call(srv)
which acts as calling the service and gets returned with a response. Pretty neat.
- Add the executables in the
CMakeLists.txt
files
Compiling Node With Multiple Source Files
## Declare a cpp executable add_executable(executable_one src/source_one.cpp src/source_two.cpp src/source_n.cpp)
To create multiple executables, you just add multiple instances of that line:
## Declare a cpp executable add_executable(executable_one src/source_one.cpp src/source_two.cpp src/source_n.cpp) add_executable(executable_two src/source_one.cpp src/source_two.cpp src/source_n.cpp) add_executable(executable_three src/source_one.cpp src/source_two.cpp src/source_n.cpp)
tf
tf keeps track of all these frames over time, and allows you to ask questions like:
- Where was the head frame relative to the world frame, 5 seconds ago?
- What is the pose of the object in my gripper relative to my base?
- What is the current pose of the base frame in the map frame?
- A ROS transform contains a pose (x,y,z) and a quaternion rotation (w, r, p, y)
Transform is a deprecated package with tf2 being the new one.
I did the tutorials where I created a broadcaster and a listener in c++. I also did some stuff where you can compute the transform between two frames and convert it to a velocity command to a robot.
There are two things that you need to do to use tf. You have to listen and broadcast them.
- Listen - Receive and buffer all coordinate frames that are broadcaster in the system, and query for specific transforms between frames
- Broadcast - Send out the relative prose of coordinate frames to the rest of the system
There is a REP that outlines lots of good best practice stuff when defining your robot link.
Quaternions
For learning about quaternions check out the OGRE Quaternions intro.
For conversions use this calculator link.
Transform Debugging
You can use rosrun tf2_tools view_frames.py
to show you information about the frames.
You can use rosrun tf tf_monitor frame1 frame2
to show you timing info.
tf::MessageFilter
tf::MessageFilter
is a function that allows us to process stamped datatypes.
turtle1 wants to know its position relative to itself
- listen to the topic where turtle3's pose is being published
- wait until transforms into the desired frame are ready
- do its operations
tf::TransformListener
Convenient class that allows to do the following:
- Create a listener for frames
- Handle transforms such as points from one frame to another
robot_state_publisher
The robot state publisher helps you to broadcast the state of your robot to the tf transform library.
It needs two things to run:
- The URDF file
- A source that publishes the joint positions as a
sensor_msgs/JointState
It subscribes to joint_states topic for joint position information.
It uses the following parameters
- robot_description(URDF map)
- tf_prefix(string)
- publish_frequency(double)
ROS Topic
The rostopic
command lets you monitor all aspects of a ROS topic.
You can also use rostopic
to publish data of any message into a ROS topic.
rostopic hz <topic>
: look at how often a message is being sentrostopic pub <topic> <data>
: publish data to the topic
When using rostopic pub
, make sure you do not have a /
before the message name or
else autocomplete won't work!!!
ROS Record
Recording and playing back data is really nice and easy with ROS.
To record everything you run:
rosbag record -a
To play back a bag file you run:
rosbag play <your bag file>
URDF
URDF is an XML file that stands for Unified Robot Description Format.
Unfortunately the tutorials do not give you a step by step guide on how to make a URDF. Instead they just explain a sample URDF tutorial project, which leaves me guessing as to how integrate a URDF in an existing package.
There is a Solidworks to URDF exporter but is old and does not work very well. Do not use it! You are much better off learning how to code your own URDF files.
In a URDF there is a tree structure with one root link. The way you organize these links is with joints. You declare the links and then specify joints between them. If you have two links with no joint specified, RViz will give you an error.
If there are any joints in a mesh, you need to run the joint_state_publisher and also the robot_state_publisher. That means in your launch file you will have the following:
<node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher" /> <node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher" />
You specify meshes in a URDF with the following:
<mesh filename="package://NAME_OF_PACKAGE/path"/>
You can use the command check_urdf
to text the URDF file. URDF_basic
For URDF files to work in gazebo they need to have the following:
- A header robot tag with an optional xml namespace. Essentially, the following:
<robot name="rrbot" xmlns:xacro="http://www.ros.org/wiki/xacro">
<intertia>
element within each<link>
element.
Positioning
The link tab has an origin tag with rpy and xyz params. These only change the origin of the object. They do not change the position. To change the position you set up a joint.
:URDF_basic: http://wiki.ros.org/urdf/Tutorials/Create%20your%20own%20urdf%20file :Gazebo_tut: http://gazebosim.org/tutorials/?tut=ros_urdf#Simplifycollisionmodel
Xacro
Xacro is a URDF macro scripting language that gives you functionality to create more powerful and dynamically generated URDF code. It is a bit tricky to learn but it is very helpful.
The way it works is that you make a xacro file and then have the URDF get automatically generated.
You need these two lines at the top of a xacro file.
<?xml version="1.0"?> <robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="firefighter">