ROS_General - RicoJia/notes GitHub Wiki
-
Neato Robot: Camp Peavy, can get a docking station: https://www.servomagazine.com/magazine/article/neato-ros-robot-navigation https://github.com/cpeavy2/botvac_node [email protected]
========================================================================
========================================================================
-
sudo apt-get update, apt-get install ros-noetic-desktop-full
问题: 网络可能卡顿- rosdep update 报错: 网络,重来
-
sudo rosdep init
: Python 组件缺失sudo apt-get install python-rosdep python-wstool
========================================================================
======================================================================== 0. Good read on Basics
-
Create Packages
- before catkin build, catkin make, source /opt/ros/noetic/setup.bash
- catkin_cretae_pkg pkg_name dependencies (like rospy, roscpp).
-
catkin_create_pkg beginner_tutorials std_msgs rospy roscpp
for dependent packages
-
catkin_make --only-pkg-with-deps <target_package>
-
ros package Basic Structure
- src(one workspace) -> multiple packages(no nested packages) -> each package has one package.xml and Cmakelist.txt.
- ROS only thinks of a directory with package.xml and CMakelists.txt as a package. So you can group different packages however way you want, and they can be repos.
- you have devel, src, build. devel is where libraries, executables go. build is where cmake and make are called to configure packages.
- Dependency check:
-
rospack depends1 pkg_name
(first order dependencies) -
rospack depends pkg_name
(all dependencies)
-
- src(one workspace) -> multiple packages(no nested packages) -> each package has one package.xml and Cmakelist.txt.
-
Build
- run catkin_make
- In python, Cmakelist is only for installing dependencies. So your code works if you don't have everything installed. However, in C++, you compile first.
source ~/catkin_ws/devel/setup.bash
- run catkin_make
- After you move your pakcage to another place, or simply change its name, you need to catkin_make it. CMakelist wont match
-
Workspace
- where you run a project, with multiple packages.
source devel/setup.bash
- overlay current workspace on top of other, adding the current package path to the$ROS_PACKAGE_PATH
- check current workspace:
$echo $ROS_PACKAGE_PATH
needs to be current_workspace:root workspace - Workspace overlaying: using dependencies from multiple workspaces. Useful when making a newer version of a package. Just source setup.bash
- where you run a project, with multiple packages.
-
Basic Structure:
- build: CMake is invoked with cached info.
- devel: built targets.
-
catkin clean
cleans the entire build and devel space. - src
- config
- include/package_name
- launch
- src
- test (unit/ROS tests)
- package.xml (MUST INCLUDE IN catkin-compliant packages)
- CMakeLists.txt - to be put into CMake Build System
- package_name_msgs
- action
- msg
- srv
========================================================================
========================================================================
-
shut down ros with signal
rospy.signal_shutdown("Action Ser ver not Available!")
-
To make your python file to be ros file,
- no .py, cuz in linux suffix doesnt matter
shbang #!/usr/bin/env python
chmod 775 node
-
robot_state_publisher: transforms joint states to TF2, given "robot_description"
========================================================================
========================================================================
- get info from array: use iterator.
for (auto i = msg.landmarks.begin(); i!= msg.landmarks.end(); ++i){ nuslam::Landmark landmark = *i; std::cout<<landmark.range; }
- Message examples
-
geometry_msgs.msg.PoseStamped
. example - There's still no
geometry_msgs::Quaternion(1,2,3,4);
yet.
-
-
Command line tools
rotopic - hz rostopic list -v: verbose list rostopic pub /TOPIC MESSAGE_TYPE '{field_name:[1,2,34]}' #publishing an array
-
rqt_plot
:source setup.bash; rosrun rqt_plot rqt_plot
- then, you add exact msg (name/subfields)to it. you can check the name by rostopic echo
- if the topic you specified does not exist when you launch, then it will not show that topic and you will have to manually select it.
-
use latched topic (last message will be sent to any new subscriber), as in:
pub = rospy.Publisher('topic_name', std_msgs.msg.String, queue_size=10, latch = False).
-
Ros_publish and subscriber should be in the same call back function. 1. code is simpler 2.single thread.
-
boost::bind
and passing extra arguments into a callback function. (boost::bind "binds" arguments to functions. )- example
bool update_grid_map_data(std::vector<int>& data, prm::update_grid_map_data::Request& req, prm::update_grid_map_data::Response& res) { return true; } std::vector<int> grid_map_data = grid_map.get_data(); ros::ServiceServer srv = nh.advertiseService<prm::update_grid_map_data::Request, rm::update_grid_map_data::Response> ("update_grid_map_data", boost::bind( &update_grid_map_data, grid_map_data, _1, _2)); boost::bind(&omnid_group_planning::Omnid_Group_Planner::subCB, this,_1)); //_1, _2 are placeholders; // you need to declare type <prm::update_grid_map_data::Request, prm::update_grid_map_data::Response>
- TODO: can we use
std::bind
as well?
- example
========================================================================
========================================================================
-
Motivation: it's a schema, like proto! So it generates code in python/c++ for msgs. it's in msg folder
- supports simple data types. int, float... but also array/vector.
-
Command Line Operations
rosmsg show -- check msg composition
-
msg file
#MyMessageType.msg Header header; SomeExtraInfo info IntMultiArray theUnderlyingData
- Special type: header, contains time stamp and coords
-
add to
package.xml
so code can be generated.<buildtool_depend>catkin</buildtool_depend> <build_depend>message_generation</build_depend> <exec_depend>message_runtime</exec_depend> <depend>geometry_msgs</depend>
- You need build_depend only for messages in another package
-
add to Cmakelist.txt:
# catkin combines all your projects into one, and without this your code may run fine if a previous proj has it. But it will break!!! find_package{message_generation} # export runtime dependency catkin_package(CATKIN_DEPENDS message_runtime add_message_files( FILES Your_MSG_File.msg # #FILES is just CMake syntax. )) generate_messages( #make sure this func is called DEPENDENCIES std_msgs )
-
Code
-
std_msgs
: contains all the basic message types, int, float, Float64MultiArray, etc. even has std_empty. - Native conversion b/w arr and vector.
```cpp
int32[] arr = std::vector;
std::vector = int32[] arr;
-
-
catkin_make install
- Now c++ code is generated here,
~/catkin_ws/devel/include/beginner_tutorials/
, python in~/catkin_ws/devel/lib/python2.7/dist-packages/beginner_tutorials/msg
-
catkin_make install
: createinstall
, and a binary version in it. Good for distribution.
- Now c++ code is generated here,
-
Vector3
does not use[]
. you should do msg.property -
latched msg will be saved and sent to any subscriber that connects in the future.
========================================================================
========================================================================
- check what goes into a message
rostopic pub topic_name, then hit double tab
- In Python Create a message
duration_msg = Duration() #Duration is the comes from std_msgs Duration duration_msg.data.secs = sleep_time #data is the field duration_msg.data.nsecs = 0
- Python Call Service
#serviceproxy returns an object that you can call. self._tele = rospy.ServiceProxy(self._mark + "/teleport_absolute",TeleportAbsolute) # the serivce name is "mark/teleport_absolute", and TeleportAbsolute is the rosservice type. TeleportAbsolute.srv is in the roaming_turtle folder self.teleport1(x = init_pos[0],y=init_pos[1],theta = init_heading)
- Make your own service
- create srv folder,
- Create
.msg
geometry_msgs/Twist twist --- TurtleVel tvel #unit8 off is used for boolean values
- Create
.srv
, composed of request and response types.int64 A int64 B --- int64 sum
- Create
- In main file:
from turtle_control.srv import VelTranslate, VelTranslateResponse self._srv = rospy.Service("vel_translate", VelTranslate, self.from_twist) #callback function ... def from_twist(self, req): if req.twist.linear.y != 0.0 return None else:` return VelTranslateResponse(TurtleVel(linear = req.twist.linear.x, angular = req.twist.angular.z)) # You don't have to set every single value, cuz there're default values
- cpp
#include Service_Name, Service_NameResponse
- cpp
- in your Cmakelist file,
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs message_generation ) # Note that message_generation is also needed here! add_service_files( FILES VelTranslate.srv ) # make sure your srv is compiled before linking: add_dependencies(executable ${project_EXPORTED_TARGETS})
-
package.xml
<build_depend>message_generation</build_depend> <build_export_depend>message_runtime</build_export_depend> <exec_depend>message_runtime</exec_depend>
- create srv folder,
- if you intend to return nothing, you should return std_srvs.Empty. Returning none or not returning will trigger error.
- You also need the full ros msg stuff - service is built on top of
msg
# Python from std_srvs.srv import Empty.srv #in Cmakelist find_package(catkin REQUIRED COMPONENTS geome try_msgs message_generation std_srvs) generate_messages(DEPENDENCIES geometry_msgs std_msgs std_srvs) #在add_message_files、add_service_files宏之后必须加上这句话,用于生成srv msg头文件/module,生成的文件位于devel/include中 catkin_package(CATKIN_DEPENDS geometry_msgs message_runtime std_msgs std_srvs) # catkin宏命令,用于配置ROS的package配置文件和CMake文件 # package.xml: <depend>std_msgs</depend>
========================================================================
========================================================================
- Purpose: long services can be stopped. link
- actionlib API is used for implementing an action server, or use an action client that depends on actionlib)
- Structure: action server + action client, with goal, feedback, and result
- goal: move_base, go to that destination! Laser_scanner: tilt to that angle!
- feedback: we're at this location sir.
- result: turtle has moved to this location! laser scanner is tilted AND THIS IS THE LASER scanned file!
-
build
./action/DoDishes.action
- similar to service, but 6 messages are generated! one on the client, one on the server.
# Define the goal uint32 dishwasher_id # Specify which dishwasher we want to use --- # Define the result uint32 total_dishes_cleaned --- # Define a feedback message float32 percent_complete
-
CMakelist
find_package(catkin REQUIRED genmsg actionlib_msgs actionlib) add_action_files(DIRECTORY action FILES DoDishes.action) generate_messages(DEPENDENCIES actionlib_msgs)
-
package.xml
<build_depend>actionlib</build_depend> <build_depend>actionlib_msgs</build_depend> <exec_depend>actionlib</exec_depend> <exec_depend>actionlib_msgs</exec_depend>
-
Python example simple client:
import roslib roslib.load_manifest('my_pkg_name') import rospy import actionlib from chores.msg import DoDishesAction, DoDishesGoal if __name__ == '__main__': rospy.init_node('do_dishes_client') client = actionlib.SimpleActionClient('do_dishes', DoDishesAction) client.wait_for_server() goal = DoDishesGoal() # Fill in the goal here client.send_goal(goal) client.wait_for_result(rospy.Duration.from_sec(5.0))
-
Launch
roslaunch pkg_name node_name arg:= something #or if arg is private (so in launch file it's ~, here it's _) roslaunch pkg_name node_name _arg:= something
========================================================================
========================================================================
-
Xml file is indentation-insensitive
-
Syntax: Don't forget to include / at the end!
<node name = "roaming_turtle" pkg="turtlesim" type="turtlesim_node"/>.
-
namspace: like directories. they can be nested.
-
it's relative naming - names will be prepended with namespace
node1 resolves to /workspace1/node1 topic1 resolves to /node1/topic1
-
global names: with
/
-
private name: specific to only one node.
~topic1 resolves to /namespace1/node1/topic1
-
Always use relative names in your code, so you can have two nodes with the same code, but different parameters.
-
-
launch file should have
<?xml version="1.0"?>
so vim will recognize this
-
Launch file
roslaunch rjje_arm rjje_arm.launch
-
You need to source the workspace before using
$find$ in launch files-
$(find pkg)
: useful for finding a package, but again you need to source it!
-
-
<include>
: include another launch file -
<group>
: group nodes together, so you can apply the same settings on them.- Use: assign name space to a group of nodes
<group ns="threes">
- Use: assign name space to a group of nodes
-
<machine>
: specifies a remote machine, which nodes will run upon- in command line,
export ROS_MASTER_URI=http://host_name:11311/
- Specify Name
<machine name="local_from_turtlebot" address="localhost" user="student" env-loader="/home/student/install/env.sh"/>
- in command line,
-
inside
<node>
- The order in which nodes start is non-deterministic.
- All launch files are launched before the nodes are launched in the launch file.
- so these params are launched before the node is launched.
-
use different namespaces if two nodes have the same name.
<group ns="turtlesim1"> <node pkg="turtlesim" name="sim" type="turtlesim_node"/> </group>
- if the node dies, the whole roslaunch is terminated.
-
<node pkg="usb_cam" type="usb_cam_node" name="cam1" required="true" output="screen">
- executable file name is type,
- name is the base name (relative name) of the node. 这样你这个topic 里所有的topic, service,等 都叫/cam1/something
-
output=screen
error message will be sent to screen. otherwise, output = "log", error message will be sent to log. This is important if you want to output msgs to screen.
- The order in which nodes start is non-deterministic.
-
remap:
- change the name of your node, etc. from command line
$ rosrun turtlesim turtlesim_node __name:=my_turtle
- remap private name even inside
<node name="ekf_and_odometer" type="ekf_and_odometer" pkg="nuslam > <remap from="~set_pose" to="/Odometer/set_pose"/> </node>
- change the name of your node, etc. from command line
-
<env>
: set environement variables for nodes.-
$(env ENVIRONMENT_VARIABLE)$
Expands the environment variable
-
-
<arg>
: specify arguments, which can be used in command line.$(arg myarg)
: expands myarguement- set up an argument and its doc. In lauchfile,
<arg name="up" default="true" doc="Launch the count up node if true"/>
- Appplication: conditionals
//example 1 <group if="$(arg foo)"> ... </group> <param name="foo" value="bar" unless="$(arg foo)" /> // example 2 <node pkg="counter" type="count_down" name="up" if="$(arg up)" />
- Application: argument for next included arg:
// example 3 - arg <include file="$(find darknet_ros)/launch/darknet_ros.launch" > <arg name="image" value="/cam1/image_raw"/> </include>
- set up an argument and its doc. In lauchfile,
-
use : set individual parameters on the parameter server.
- in launchfile, inside the corresponding node section,
<param name="~val" value="$(eval 2+2)" type='double|bool|str|int'/> or <param name="landmark_mode" value="$(arg robot)"/>
- in code
nh2.getParam("val", val);
-
rosrun pkg node _arg_name
should have _at the front, otherwise it is not recognized by ros.// in cpp nh2.getParam("arg_name", arg_name)
- Param Server
# get param rosparam get /use_sim_time
- in launchfile, inside the corresponding node section,
-
command line -> launch file -> node: (TODO: untested, used to pass args into nodes. )
- launch file
<arg name="use_default_grid" default='1'/> <node name=....> <param name="use_default_grid" value="$(arg use_default_grid)"/> </node>
- cpp
nh2.getParam("use_default_grid",use_default_grid);
- launch file
-
evaluate args:
$(eval <expression>)
runs python code and expands the result.$(eval arg('somearg') + str(arg('another_arg'))"
- tricks
0. Check doc of the arguments passed to the launch file
bash $roslaunch me495_image usb_cam.launch --ros-args
- Check nodes being launched
$roslaunch me495_image libuvc_cam.launch --node
- Check arguments passed to a node
$roslaunch me495_image usb_cam.launch --args = /cam1
- Check if there are oterh launch files included
$roslaunch me495_image usb_cam.launch --files
- Find which launch file starts this node:
$roslaunch me495_image usb_cam.launch --find /cam1
- Check nodes being launched
========================================================================
========================================================================
-
What do ros::spin() and ros::spinOnce() do?
- Messages will come to a buffer first and wait to be processed by these two functions.
ros::spin()
will enter a loop, wait and execute callbacks. So messages are processed on ONE thread - You can do timer callbacks+ ros::spinOnce at the same time.
- ros has a single-threaded model, but behind the scenes it might use multiple threads to do network management, scheduling etc.
ros::spinOnce
will read all available callbacks on the queue.
- Messages will come to a buffer first and wait to be processed by these two functions.
-
For callbacks on Multithreads
-
ros::MultiThreadedSpinner
is a blocking spinner (so blocks a callback from being run on multiple threads)? - start right away
-
ros::MultiThreadedSpinner(num_thread)
, num_thread = 0 will use 1 thread from all cores - Of course, if callbacks have shared data, there might be a race condition.
-
- To truly do "concurrent subscriptions", do
ros::SubscribeOptions ops; ops.template init<std_msgs::String>("chatter", 1000, chatterCallback); ops.transport_hints = ros::TransportHints(); ops.allow_concurrent_callbacks = true; ros::Subscriber sub = nh.subscribe(ops);
========================================================================
========================================================================
-
ROS plugin is not a node, but instead a dynamic class that is launched into a node. READ THIS FIRST
ros::NodeHandle nh("~"+name); //so this will get the intended namespace std::string full_name = nh.resolveName("") // will prepend the full node name here.
ros::NodeHandle::resolveName(NAME, remap=true);
-
Principle:
- Shared Object, Dynamically Linked Lib
- package.xml register it.
- Pluginlib is a dir of header files that's copied to public space, which defines
The parent class
- Shared Object, Dynamically Linked Lib
-
Code
- cpp parent class
polygon_base.h
namespace polygon_base{ class RegularPolygon { public: virtual void initialize(double side_length) = 0; // Use this since ctor is not available virtual double area() = 0; virtual ~RegularPolygon(){} protected: RegularPolygon(){} // required by pluginlib };
}; };
2. CPP child class (you can have multiple):
polygon_child.h3. Export classes:
src.cpp// Full namespace is necessary PLUGINLIB_EXPORT_CLASS(polygon_plugins::Triangle, polygon_base::RegularPolygon) ```
-
In class
CMakelist.txt
, you can compile this now, sowe have a lib (not known for loading yet)include_directories(include) add_library(polygon_plugins src/polygon_plugins.cpp)
-
Make plugin visible to catkin toolchain,
(polygon_plugins.xml)
<library path="lib/libpolygon_plugins"> # path to devel/lib/libpolygon_plugins.so <class type="polygon_plugins::Triangle" base_class_type="polygon_base::RegularPolygon"> </class> </library>
-
Export plugin
<export> <pluginlib_tutorials_ plugin="${prefix}/polygon_plugins.xml" /> #${prefix} defines path to the current pkg #pluginlib_tutorials_ is the name of the package base class lives in </export>
- Verify you can see the plugin:
rospack plugins --attrib=plugin ParentClass
: gives full path to thepolygon_plugins.xml
. - tool chain has
"ros/package.h"
ros::package::getPlugins(package, attrib_name, paths, force_recrawl);
- Verify you can see the plugin:
-
Use a plugin cpp
#include <pluginlib/class_loader.h> #include <pluginlib_tutorials_/polygon_base.h> int main(int argc, char** argv) { pluginlib::ClassLoader<polygon_base::RegularPolygon> poly_loader("pluginlib_tutorials_", "polygon_base::RegularPolygon"); try { boost::shared_ptr<polygon_base::RegularPolygon> triangle = poly_loader.createInstance("polygon_plugins::Triangle"); triangle->initialize(10.0); boost::shared_ptr<polygon_base::RegularPolygon> square = poly_loader.createInstance("polygon_plugins::Square"); square->initialize(10.0); } catch(pluginlib::PluginlibException& ex) { ROS_ERROR("The plugin failed to load for some reason. Error: %s", ex.what()); }
-
Use a plugin,
CMakelist.txt
add_executable(polygon_loader src/polygon_loader.cpp) target_link_libraries(polygon_loader ${catkin_LIBRARIES})
- cpp parent class
-
rviz plugins
- if parent class is
rviz::Display
, then you will see it under display Add. If parent classrviz::Tools
, click +
- if parent class is
========================================================================
========================================================================
-
Principle: Tutorial
-
Motivation: High volume of data transfer between nodes: e.g., TCP's serialization & copying, has a lot of packaging & unpackaging.
- On same computer, you can pass a pointer. Assuming message is not changed!
- Nodelets do the above. They're "threaded nodes" that
- Are DYNAMICALLY loaded (so they're plugins) into nodelet manager, then grouped into a single process
- If two nodelets communicate over the same topic, you can pass the address around. Otherwise, the message type will be the same?
- Appear to be independent, because their namespaces are preserved.
- Only available at roscpp
-
Caveats: https://www.clearpathrobotics.com/assets/guides/kinetic/ros/Nodelet%20Everything.html
========================================================================
========================================================================
-
rosinstall file - include your own repository
-
download things: if its
melodic
, you can use apt. turn all_
turned into-
-
Wstool: for downloading packages from multiple repos.
- e.g: downloading from github
wstool init #if path not specified, the rosinstall file should be in the same directory. cd src wstool set proj_name --git https://github.com:user/project.git # downloading the package. \# here the local .rosinstall file will be replaced by the .rosinstall file on github. wstool merge -t src PATH_TO_ROSINSTALL_FILE #merge in .rosinstall file
- making your own proj.rosinstall - so other ppl can pull using wstool
- change name to proj.rosinstall, then use Yaml to include local name, git name. Link
- put it up on github repo
- if there's another .rosintall file already, do wstool init something.rosinstall
- e.g: downloading from github
-
Ros Shortcuts
- roscd
roscd beginner_tutorials
- Ros editor: rosed?
- Powerful combination:
- $rospack - providing info about packages. e.g,
$rospack list |grep turtle # this will give you all items with turtle.
- roscd: cd to the directory of the pacakge, once it's you can see in
- $rospack - providing info about packages. e.g,
- roscd
-
Rospack 1. roscp: copy files from one to another repository, separate -
roscp [package_name] [file_to_copy_path] [copy_path] #copy files from one package to another.
========================================================================
========================================================================
- no module named 'yaml': turn off your conda environment
- 有时,当你看到 msg_type doesn't have attribute, 你有可能一个field 给看漏了!
- launchfile
<arg name="debug" default="false" /> <arg unless="$(arg debug)" name="launch_prefix" value="" /> <arg if="$(arg debug)" name="launch_prefix" value="gdb --ex run --args" />