ROS_Python - RicoJia/notes GitHub Wiki

========================================================================

Basics

======================================================================== 0. Compilation 1. when you first create a python script, you must do this: then catkin_make python catkin_install_python(PROGRAMS scripts/motion_controller.py scripts/test_pub.py DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} )

  1. get params

    val = rospy.get_param("~val")
    • rospy param: Don't know why, but rospy.get_param("~name", default) works, but not rospy.get_param("name", default)
    • works with rosrun pkg node _name:=val
  2. time

    1. 比较时间 rospy.Time.now() - start_time > rospy.Duration(secs = n)
    2. rospy.Timer(rospy.Duration(1), callback)
  3. Try except

    try:
        main()
    except rospy.ROSInterruptException:     # this can be thrown by rospy.sleep when ctrl_c. 
        pass
  4. Include

    1. numpy: just import numpy in code, no need to include this in CMakelist, package.xml. Cuz this is a script, no need to be compiled
      • But Numpy is not included in rospy!!
  5. Subscribers:

    class CmdVelExecutioner(object):
    def __init__(self):
        self.cmd_vel_sub_ = rospy.Subscriber("/cmd_vel", Twist, self.cmdVelCB)
    def cmdVelCB(self, twist_msg):
    1. Each subscriber will start a new thread, but rospy.spin() will simply puts the main thread to sleep until shutdown... So generally, you don't need this .
      • Therefore, rospy has no spinOnce, cuz each subscriber will handle its own cb.
    2. Working with custom message typefrom beginner_tutorials.msg import Person
  6. shbang:

    • #!/usr/bin/env python3 for ROS Noetic. Shbang tells which interpreter to use.
    • shbang withenv is better than #!/usr/bin/python because it env will figure out which python to use.
  7. ROS-python, naming convention: https://www.ros.org/reps/rep-0008.html#id19 ========================================================================

TF

========================================================================

  1. when using transformlistener, you need to do: link, another link

    listener.waitForTransform('/base_link','/end_effector',rospy.Time(), rospy.Duration(1.0))
    (trans,rot) = listener.lookupTransform( '/base_link','/end_effector', rospy.Time(0))
    rospy.sleep(0.1)
    • Also, the tutorial is misleading: you need to transform from end effector to base link to see the trajectory of end effector in the base frame!!
  2. publish transforms: Always make sure there is a child frame when you publish a transform! The child frame cannot be itself. link

    from nav_msgs.msg import Odometry
    from std_msgs.msg import Header
    
    odom_pub = rospy.Publisher('/my_odom', Odometry, queue_size=10);
    odom = Odometry()
    header = Header(); #empty header, stamps, 
    header.frame_id = '/odom';
    
    #try rossrv show Odometry to see what info needs to be filled in. 
    odom.pose.pose = 
    odom.twist.twist = 
    header.stamp = rospy.Time.now()
    odom_pub.publish(odom)

    注意: 你在做tf listerner 的时候, broadcaster 会有delay!

  3. what goes into sensor_msgs/joint_state's velocity - the velocity right after we get to the specified position or right before? How does tf use the velocity in its broadcasted transform?
    A: It is just an information. Should be updated is the body velocity in body frame

  4. if you have roll pitch yaw like roscpp's tf2::Quaternion::setEulerZYX(yaw, pitch, roll), use Bullet: getEulerFromQuaternion(quat)

  5. quat

    tf2::Quaternion quat; 
    quat.setEulerZYX(yaw,pitch,roll)

========================================================================

Multi-threading

========================================================================

  1. Rospy.spin() is different than roscpp ros::spin(), which has its own thread. its job is to keep the code from exiting the node until terminated. the thread does not interfere with other threads, such as subscriber callbacks

    • Side Question: Why do timer threads not execute? Because you need to wait a second for the timer callback to fire!
  2. there is one main thread. But one subscriber callback has one thread!

  3. rospy::spin() is just a sleep function

    try:
        while not rospy.core.is_shutdown() and not self.done:
            time.sleep(0.5)
    except KeyboardInterrupt:
        logdebug("keyboard interrupt, shutting down")
    • in rospy, since spin() is simply waiting for shutdown request:
      # method 1
      while not rospy.is_shutdown():
        #do stuff
      
      #method 2
      rospy.spin()
    • shutdown handler: messages are not guaranteed to be published. But you can do service calls / parameter server calls safely.
      def my_handler():
        #do stuff
      rospy.on_shutdown(my_handler)

========================================================================

Message

========================================================================

  1. message generation: (source code to be checked)
    package_name/msg/Foo.msg → package_name.msg.Foo
    # 2 ways to generate the message
    #1
    import std_msgs.msg.String
    #2
    from std_msgs.msg import String
    msg = String()
    
    # 3 ways to populate
    #1
    msg.data = "lol"
    #2, In-Order Arguments, must provide all args
    msg = String("lol")
    msg = std_msgs.msg.ColorRGBA(255,255,255,128)
    #for this schema:
    float32 r
    float32 g
    float32 b
    float32 a
    
    # 3 keyword args, or kwargs, get default msgs afterwards
    msg = std_msgs.msg.ColorRGBA(b=255)
    

========================================================================

Lower Level Details

========================================================================

  1. rospy allows only one node, with a unique name. Code?
    • this is a design choice, multiple nodes don't make sense to be in the same process. However, if we call this funtion with the same args, then it's considered the same node.
    • there's a check for args
      global _init_node_args
      if _init_node_args != (name, argv, anonymous, log_level, disable_rostime, disable_signals):
          raise rospy.exceptions.ROSException("rospy.init_node() has already been called with different arguments: "+str(_init_node_args))

========================================================================

Past Mistakes

========================================================================

  1. you main loop is placed in the init function, and should be AFTER everything else!!
    • e.g, 这样这个sub永远不会被执行!!
      self.strategy2_main()
      self.map_sub = rospy.Subscriber("map",    OccupancyGrid,self.map_callback)

========================================================================

Common Errors:

========================================================================

  1. Anaconda changes default path of Python, ROS may not run. Solution:
    echo export PATH=\"/home/$USER/anaconda3/bin:$PATH\" > ~/.bashrc
⚠️ **GitHub.com Fallback** ⚠️