Monday, March 19, 2012

ROS Development Tips & Tricks

I just participated in another hackathon with ROS and the PR2. After each hackathon, we think about ways to improve our process and code and this time around we tested out some ideas we came up with last time and they worked out well.

Here's my current list of tips and tricks for ROS and robot development. It's mostly intended for ourselves next time so it's very concise and I  won't spend any time trying to convince you why something is a good idea :-) Hopefully it'll be useful to you too!

  • Use rviz
    • Visualize intermediate steps. For example if the robot needs to get within grasping range of something and has some candidate destinations, visualize them and then visualize which candidate was chosen. This is way easier to read and understand than text output listing coordinates.
    • Visualize detected objects. Then you can overlay them with video and make sure they're detected in the pose they're actually in. I think you can do this in rviz with markers although I haven't tried it yet.
    • Ben's got a c++ library called pviz which visualizes the PR2. This is much better than putting an arrow at the base pose. Next time around, I'd like pull it into my future simple-ros library and expose it as a service.
  • Speed up the process of testing changes
    • Make your launch files respawn nodes when they die (respawn="true"). This way if nodes crash or you rebuild them and kill the old ones, you bring up a new node without wasting time tracking it down or worse, stopping and restarting the whole launch.
    • Put constants in config files or the param server or anywhere that you can adjust them without rebuilding. This matters much more in c++ code than in python.
    • Make long running nodes re-read their configs on each request or action so you can tweak settings and not need to restart the nodes.
    • Nodes you write which provide complex services should have debug service calls or messages which do parts of the action. This makes debugging much easier. For example, a node which has a service to pick up blocks should also expose actions to put the arm in position, close the gripper, lift the block, etc.
    • (Eventually) use the auto dev builder. It's a tool which detects changes to source files and tries to rebuild them and kill the running node so it can be respawned with the new executable. It doesn't support python programs yet and has a bug where it can run make in the wrong dir and use the wrong Makefile.
  • Misc
    • Set things up so you can run the robot while connected to power. If the robot has to move around in a limited area, run power from above using pulleys to take up the slack as the robot gets closer to where the cable comes from.
    • If more than one person is involved, develop in simulation. Only push code to the central repo and test on the robot once a feature is done and working in simulation.
    • If you're not using stacks which treat launch files as part of a stable api, don't use their launch files, especially with high level code. Copy and paste needed bits into your own launch files. In theory it's good practice not to duplicate code, but it's too hard to track down broken launches when they're spread out over many files and some launch files do strange things.
    • Give packages unique names. ROS doesn't use the stack as part of the namespace so all packages must have unique names.
    • If you're building a package which is meant to be reused, put it in its own stack and its own repo unless you're using svn and are sure no one will ever migrate the repo to a different vcs. With git, hg, etc, you have to check our the full tree and there's no good way to tell ROS you're only interested in a particular subdirectory. People who want to use one package get every package from the repo in their ROS path. If one of the other packages in the repo conflicts with something else (like a fork of the project) their life becomes difficult.
  • Set up bash aliases:
    # Standard ROS setup
    source /opt/ros/electric/setup.bash
    export ROS_ROOT=/opt/ros/electric/ros
    export PATH=$ROS_ROOT/bin:$PATH
    export PYTHONPATH=$ROS_ROOT/core/roslib/src:$PYTHONPATH
    export ROS_PACKAGE_PATH=~/ros:/opt/ros/electric/stacks:$ROS_PACKAGE_PATH
    export ROS_PARALLEL_JOBS='-j4'
    export ROBOT=sim # or pr2, whichever you use most
    # Handy commands
    alias pr2dash='rosrun pr2_dashboard pr2_dashboard'
    alias rviz='rosrun rviz rviz'
    alias prl='export ROBOT=pr2 && export ROS_MASTER_URI=http://prl:11311'
    alias sim='export ROBOT=sim && export ROS_MASTER_URI=http://localhost:11311'
    # Show cameras
    alias rightwide='rosrun image_view image_view image:=/wide_stereo/right/image_rect_color theora ;'
    alias leftwide='rosrun image_view image_view image:=/wide_stereo/left/image_rect_color theora ;'
    alias rightarm='rosrun image_view image_view image:=/r_forearm_cam/image_rect_color theora'
    alias leftarm='rosrun image_view image_view image:=/l_forearm_cam/image_rect_color theora'
    # Show arm joint angles
    alias rightjoints='rostopic echo -n 1 r_arm_controller/state | grep -B1 -A0 "positions:"'
    alias leftjoins='rostopic echo -n 1 l_arm_controller/state | grep -B1 -A0 "positions:"'
    alias bothjoints='rostopic echo -n 1 both_arms_controller/state | grep -B1 -A0 "positions:"'
    # Show wrist pose in base footprint (middle of robot on floor)
    alias rightwrist='rosrun tf tf_echo base_footprint r_wrist_roll_link'
    alias leftwrist='rosrun tf tf_echo base_footprint l_wrist_roll_link'