{"id":21644328,"url":"https://github.com/vectorxxxx/25-ros","last_synced_at":"2026-05-01T12:31:44.579Z","repository":{"id":250583998,"uuid":"822031155","full_name":"vectorxxxx/25-ROS","owner":"vectorxxxx","description":"【古月居】古月·ROS入门21讲 | 一学就会的ROS机器人入门教程","archived":false,"fork":false,"pushed_at":"2024-07-28T15:35:20.000Z","size":35,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-25T05:03:21.037Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://www.bilibili.com/video/BV1zt411G7Vn/","language":"CMake","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vectorxxxx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-30T05:39:25.000Z","updated_at":"2025-06-21T13:50:32.000Z","dependencies_parsed_at":"2024-11-02T18:46:03.531Z","dependency_job_id":null,"html_url":"https://github.com/vectorxxxx/25-ROS","commit_stats":null,"previous_names":["vectorxxxx/25-ros"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/vectorxxxx/25-ROS","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vectorxxxx%2F25-ROS","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vectorxxxx%2F25-ROS/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vectorxxxx%2F25-ROS/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vectorxxxx%2F25-ROS/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vectorxxxx","download_url":"https://codeload.github.com/vectorxxxx/25-ROS/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vectorxxxx%2F25-ROS/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32497810,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-25T05:39:42.128Z","updated_at":"2026-05-01T12:31:44.553Z","avatar_url":"https://github.com/vectorxxxx.png","language":"CMake","funding_links":[],"categories":[],"sub_categories":[],"readme":"## 1、安装准备\n\n镜像下载地址：[https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/18.04.6/](https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/18.04.6/)\n\n```bash\n# 打开终端\nCtrl+Alt+T\n\n# 更新可用软件包列表\nsudo apt-get update\n# 安装这可用软件包的最新版本\nsudo apt-get upgrade\n\n# 查看系统版本\ncat /proc/version\nuname -a\nlsb_release -a\n```\n\n## 2、安装 g++ 编译器和 python 解释器\n\n```bash\n# 安装 g++ 编译器\nsudo apt-get install g++\n# E: 无法获得锁 /var/lib/dpkg/lock-frontend - open (11: 资源暂时不可用)\n# E: 无法获取 dpkg 前端锁 (/var/lib/dpkg/lock-front)\nps -e | grep apt  \nsudo kill [PID]\nsudo rm /var/lib/dpkg/lock-frontend \nsudo rm /var/cache/apt/archives/lock\nsudo apt-get update\n\n# 安装 python 解释器\nsudo apt-get install python -y\n```\n\n## 3、编译运行与解释执行\n\n```bash\n# 编译\ng++ c++_for.cpp -o c++_for\n# 运行\n./c++_for\n\n# 解释执行\npython python_for.py\n```\n\n## 4、安装 ROS\n\n```bash\n# 1、添加ROS软件源\nsudo sh -c 'echo \"deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main\" \u003e /etc/apt/sources.list.d/ros-latest.list'\n\n# 2、添加密钥\nsudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654\n\n# 3、安装 ROS\nsudo apt update\n# ros版本需要和Ubuntu版本一致\n# Ubuntu20.04 \tnoetic\n# Ubuntu18.04\tmelodic\n# Ubuntu16.04\tkinetic\n# Ubuntu14.04\tindigo\nsudo apt install ros-melodic-desktop-full\n\n# 4、初始化 rosdep\nsudo apt install python-rosdep\nsudo rosdep init\n#######\nsudo gedit /usr/lib/python2.7/dist-packages/rosdep2/sources_list.py\n# def download_rosdep_data(url):\n# url = \"https://mirror.ghproxy.com/\" + url\nsudo gedit /usr/lib/python2.7/dist-packages/rosdep2/gbpdistro_support.py\n# gbpdistro_url = \"https://mirror.ghproxy.com/\" + gbpdistro_url\nsudo gedit /usr/lib/python2.7/dist-packages/rosdistro/__init__.py\nsudo gedit /usr/lib/python2.7/dist-packages/rosdep2/gbpdistro_support.py\nsudo gedit /usr/lib/python2.7/dist-packages/rosdep2/sources_list.py\nsudo gedit /usr/lib/python2.7/dist-packages/rosdep2/rep3.py\nsudo gedit /usr/lib/python2.7/dist-packages/rosdistro/manifest_provider/github.py\n# https://raw.githubusercontent.com/ 前添加 https://mirror.ghproxy.com/\n#######\nrosdep update\n\n# 5、设置环境变量\necho \"source /opt/ros/melodic/setup.bash\" \u003e\u003e ~/.bashrc\nsource ~/.bashrc\n\n# 6、安装 rosinstall\nsudo apt install python-rosinstall python-rosinstall-generator python-wstool build-essential\n```\n\n## 5、ROS 命令行工具\n\n```bash\n# 启动 ros master\nroscore\n\n# 启动小海龟仿真器\nrosrun turtlesim turtlesim_node\n\n# 启动海龟控制节点\nrosrun turtlesim turtle_teleop_key\n\n# 图形化工具\nrqt_graph\n\n# 节点信息\nrosnode list\nrosnode info turtlesim\n\n# 话题信息\nrostopic list\n# 发布一次话题\nrostopic pub /turtle1/cmd_vel geometry_msgs/Twist \"linear:\n  x: 1.0\n  y: 0.0\n  z: 0.0\nangular:\n  x: 0.0\n  y: 0.0\n  z: 0.0\"\n# 以10Hz频率发布话题\nrostopic pub -r 10 /turtle1/cmd_vel geometry_msgs/Twist \"linear:\n  x: 1.0\n  y: 0.0\n  z: 0.0\nangular:\n  x: 0.0\n  y: 0.0\n  z: 1.0\"\n\n# 查看ros消息数据结构\nrosmsg show geometry_msgs/Twist\n\n# 服务请求列表\nrosservice list\n# 发布服务请求\nrosservice call /spawn \"x: 2.0\ny: 2.0\ntheta: 0.0\nname: 'turtle2'\"\n\n# 话题记录\nrosbag record -a -O cmd_record\n# 话题复现\nrosbag play cmd_record.bag\n```\n\n## 6、创建工作空间与功能包\n\n```bash\n# 创建工作空间\nmkdir -p ~/catkin_ws/src\ncd ~/catkin_ws/src\ncatkin_init_workspace\n# 编译工作空间\ncd ~/catkin_ws\ncatkin_make\ncatkin_make install\n\n# 创建功能包\ncd ~/catkin_ws/src\ncatkin_create_pkg test_pkg roscpp rospy std_msgs\n# 编译功能包\ncd ~/catkin_ws\ncatkin_make\n\n# 设置环境变量\nsource ~/catkin_ws/devel/setup.bash\n# 检查环境变量\necho $ROS_PACKAGE_PATH\n```\n\n## 7、发布者Publisher\n\n```bash\n# 创建功能包\ncd ~/catkin_ws/src\ncatkin_create_pkg learning_topic roscpp rospy std_msgs geometry_msgs turtlesim\n\n# 创建发布者Publisher\ncd ~/catkin_ws/src/learning_topic/src\ntouch velocity_publisher.cpp\nvi velocity_publisher.cpp\n```\n\n`velocity_publisher.cpp`\n\n```cpp\n#include \u003cros/ros.h\u003e\n#include \u003cgeometry_msgs/Twist.h\u003e\n\nint main(int argc, char **argv)\n{\n    // ROS节点初始化\n    ros::init(argc, argv, \"velocity_publisher\");\n    \n    // 创建节点句柄\n    ros::NodeHandle n;\n    \n    // 创建发布者，消息类型为geometry_msgs::Twist，发布名为turtle1/cmd_vel，队列长度为10\n    ros::Publisher turtle_vel_pub = n.advertise\u003cgeometry_msgs::Twist\u003e(\"turtle1/cmd_vel\", 10);\n    \n    // 设置循环频率\n    ros::Rate loop_rate(10);\n    \n    int count = 0;\n    while (ros::ok())\n    {\n        // 初始化geometry_msgs::Twist类型消息\n        geometry_msgs::Twist vel_msg;\n        vel_msg.linear.x = 0.5;\n        vel_msg.angular.z = 0.2;\n        \n        // 发布消息\n        turtle_vel_pub.publish(vel_msg);\n        ROS_INFO(\"Publish turtle velocity command[%0.2f m/s, %0.2f rad/s]\",\n                vel_msg.linear.x, vel_msg.angular.z);\n        \n        // 按照循环频率延时\n        loop_rate.sleep();\n    }\n    \n    return 0;\n}\n```\n\n`CMakeLists.txt`\n\n```bash\nadd_executable(velocity_publisher src/velocity_publisher.cpp)\ntarget_link_libraries(velocity_publisher ${catkin_LIBRARIES})\n```\n\n编译运行\n\n```bash\n# 编译\ncd ~/catkin_ws\ncatkin_make\n\n# 设置环境变量\nsource ~/catkin_ws/devel/setup.bash\n# 如果不想每次或者怕忘记设置环境变量，可以按如下方式修改\ncd ~\n# 显示隐藏文件\nCtrl+H\nvi .bashrc\n# 添加设置环境变量命令（路径改成自己的）\nsource /home/vectorx/catkin_ws/devel/setup.bash\n\n# 重启终端才能生效\nroscore\nrosrun turtlesim turtlesim_node\nrosrun learning_topic velocity_publisher\n```\n\n------------------------------------------------------------------------------------------------\n\n同理，python实现如下：\n\n```bash\n# 创建发布者Publisher\nmkdir -p ~/catkin_ws/src/learning_topic/scripts\ncd ~/catkin_ws/src/learning_topic/scripts\ntouch velocity_publisher.py\nvi velocity_publisher.py\n```\n\n`velocity_publisher.py`\n\n```python\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport rospy\nfrom geometry_msgs.msg import Twist\n\ndef velocity_publisher():\n    # ROS节点初始化\n    rospy.init_node('velocity_publisher', anonymous=True)\n    \n    # 创建发布者，发布名为/turtle1/cmd_vel', 消息类型为geometry_msgs::Twist，队列长度为10\n    turtle_vel_pub = rospy.Publisher('/turtle1/cmd_vel', Twist, queue_size=10)\n    \n    # 设置循环的频率\n    rate = rospy.Rate(10)\n    \n    while not rospy.is_shutdown():\n        # 初始化geometry_msgs::Twist类型的消息\n        vel_msg = Twist()\n        vel_msg.linear.x = 0.5\n        vel_msg.angular.z = 0.2\n        \n        # 发布消息\n        turtle_vel_pub.publish(vel_msg)\n        rospy.loginfo(\"Publish turtle velocity command[%0.2f m/s, %0.2f rad/s]\",\n\t\t\t\tvel_msg.linear.x, vel_msg.angular.z)\n        \n        # 按照循环频率延时\n        rate.sleep()\n\nif __name__ == '__main__':\n    try:\n        velocity_publisher()\n    except rospy.ROSInterruptException:\n        pass\n```\n\n## 8、订阅者Subscriber\n\n```bash\ncd ~/catkin_ws/src/learning_topic/src\ntouch pose_subscriber.cpp\nvi pose_subscriber.cpp\n```\n\n`pose_subscriber.cpp`\n\n```cpp\n#include \u003cros/ros.h\u003e\n#include \"turtlesim/Pose.h\"\n\nvoid poseCallback(const turtlesim::Pose::ConstPtr\u0026 msg)\n{\n    ROS_INFO(\"Turtle pose: x:%0.6f, y:%0.6f\", msg-\u003ex, msg-\u003ey);\n}\n\nint main(int argc, char **argv)\n{\n    // 初始化ROS节点\n    ros::init(argc, argv, \"pose_subscriber\");\n    \n    // 创建节点句柄\n    ros::NodeHandle n;\n    \n    // 创建订阅者，订阅名为/turtle1/pose的topic，注册回调函数poseCallback\n    ros::Subscriber pose_sub = n.subscribe(\"/turtle1/pose\", 10, poseCallback);\n    \n    // 循环等待回调函数\n    ros::spin();\n    \n    return 0;\n}\n```\n\n`CMakeLists.txt`\n\n```bash\nadd_executable(pose_subscriber src/pose_subscriber.cpp)\ntarget_link_libraries(pose_subscriber ${catkin_LIBRARIES})\n```\n\n编译运行\n\n```bash\n# 编译\ncd ~/catkin_ws\ncatkin_make\n\n# 因为之前已在.bashrc中添加过环境变量，这一步跳过\n\n# 运行\nroscore\nrosrun turtlesim turtlesim_node\nrosrun learning_topic pose_subscriber\nrosrun turtlesim turtle_teleop_key\n```\n\n------------------------------------------------------------------------------------------------\n\n同理，python实现如下：\n\n```python\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport rospy\nfrom turtlesim.msg import Pose\n\ndef poseCallback(msg):\n    rospy.loginfo(\"Turtle pose: x:%0.6f, y:%0.6f\", msg.x, msg.y)\n\ndef pose_subscriber():\n\t# ROS节点初始化\n    rospy.init_node('pose_subscriber', anonymous=True)\n\n\t# 创建一个Subscriber，订阅名为/turtle1/pose的topic，注册回调函数poseCallback\n    rospy.Subscriber(\"/turtle1/pose\", Pose, poseCallback)\n\n\t# 循环等待回调函数\n    rospy.spin()\n\nif __name__ == '__main__':\n    pose_subscriber()\n```\n\n运行之前需要勾选为可执行\n\n![image-20240630161456720](https://s2.loli.net/2024/06/30/ycRuEBN2zW95kOV.png)\n\nPython版本的无需编译，直接运行即可\n\n```bash\nrosrun learning_topic pose_subscriber.py\n```\n\n## 9、话题消息的定义与使用\n\n### 9.1、话题消息的定义\n\n```bash\nmkdir -p ~/catkin_ws/src/learning_topic/msg\ncd ~/catkin_ws/src/learning_topic/msg\ntouch Person.msg\nvi Person.msg\n```\n\n`Person.msg`\n\n```msg\nstring name\nuint8 sex\nuint8 age\n\nuint8 unknown = 0\nuint8 male    = 1\nuint8 female  = 2\n```\n\n`package.xml`\n\n```xml\n\u003cbuild_depend\u003emessage_generation\u003c/build_depend\u003e\n\u003cexec_depend\u003emessage_runtime\u003c/exec_depend\u003e\n```\n\n`CMakeLists.txt`\n\n```txt\nfind_package(......\n  message_generation\n)\n\nadd_message_files(FILES Person.msg)\ngenerate_messages(DEPENDENCIES std_msgs)\n\ncatkin_package(......\n   CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs turtlesim message_runtime\n)\n```\n\n编译\n\n```bash\ncatkin_make\n```\n\n### 9.2、话题消息的使用\n\n`person_publisher.cpp`\n\n```cpp\n#include \u003cros/ros.h\u003e\n#include \"learning_topic/Person.h\"\n\nint main(int argc, char **argv)\n{\n    // ROS节点初始化\n    ros::init(argc, argv, \"person_publisher\");\n\n    // 创建节点句柄\n    ros::NodeHandle n;\n\n    // 创建一个Publisher，发布名为/person_info的topic，消息类型为learning_topic::Person，队列长度10\n    ros::Publisher person_info_pub = n.advertise\u003clearning_topic::Person\u003e(\"/person_info\", 10);\n\n    // 设置循环的频率\n    ros::Rate loop_rate(1);\n\n    int count = 0;\n    while (ros::ok())\n    {\n        // 初始化learning_topic::Person类型的消息\n    \tlearning_topic::Person person_msg;\n\t\tperson_msg.name = \"Tom\";\n\t\tperson_msg.age  = 18;\n\t\tperson_msg.sex  = learning_topic::Person::male;\n\n        // 发布消息\n\t\tperson_info_pub.publish(person_msg);\n       \tROS_INFO(\"Publish Person Info: name:%s  age:%d  sex:%d\", \n\t\t\t\t  person_msg.name.c_str(), person_msg.age, person_msg.sex);\n\n        // 按照循环频率延时\n        loop_rate.sleep();\n    }\n\n    return 0;\n}\n```\n\n`person_subscriber.cpp`\n\n```cpp\n#include \u003cros/ros.h\u003e\n#include \"learning_topic/Person.h\"\n\n// 接收到订阅的消息后，会进入消息回调函数\nvoid personInfoCallback(const learning_topic::Person::ConstPtr\u0026 msg)\n{\n    // 将接收到的消息打印出来\n    ROS_INFO(\"Subcribe Person Info: name:%s  age:%d  sex:%d\", \n\t\t\t msg-\u003ename.c_str(), msg-\u003eage, msg-\u003esex);\n}\n\nint main(int argc, char **argv)\n{\n    // 初始化ROS节点\n    ros::init(argc, argv, \"person_subscriber\");\n\n    // 创建节点句柄\n    ros::NodeHandle n;\n\n    // 创建一个Subscriber，订阅名为/person_info的topic，注册回调函数personInfoCallback\n    ros::Subscriber person_info_sub = n.subscribe(\"/person_info\", 10, personInfoCallback);\n\n    // 循环等待回调函数\n    ros::spin();\n\n    return 0;\n}\n```\n\n`CMakeLists.txt`\n\n```txt\nadd_executable(person_publisher src/person_publisher.cpp)\ntarget_link_libraries(person_publisher ${catkin_LIBRARIES})\nadd_dependencies(person_publisher ${PROJECT_NAME}_generate_messages_cpp)\n\nadd_executable(person_subscriber src/person_subscriber.cpp)\ntarget_link_libraries(person_subscriber ${catkin_LIBRARIES})\nadd_dependencies(person_subscriber ${PROJECT_NAME}_generate_messages_cpp)\n```\n\n编译\n\n```bash\ncatkin_make\n```\n\n使用\n\n```bash\nroscore\nrosrun learning_topic person_subscriber\nrosrun learning_topic person_publisher\n```\n\n--------------------------------------------------------------------------------\n\n同理，Python实现如下：\n\n`person_publisher.py`\n\n```python\nimport rospy\nfrom learning_topic.msg import Person\n\ndef velocity_publisher():\n\t# ROS节点初始化\n    rospy.init_node('person_publisher', anonymous=True)\n\n\t# 创建一个Publisher，发布名为/person_info的topic，消息类型为learning_topic::Person，队列长度10\n    person_info_pub = rospy.Publisher('/person_info', Person, queue_size=10)\n\n\t#设置循环的频率\n    rate = rospy.Rate(10) \n\n    while not rospy.is_shutdown():\n\t\t# 初始化learning_topic::Person类型的消息\n    \tperson_msg = Person()\n    \tperson_msg.name = \"Tom\";\n    \tperson_msg.age  = 18;\n    \tperson_msg.sex  = Person.male;\n\n\t\t# 发布消息\n        person_info_pub.publish(person_msg)\n    \trospy.loginfo(\"Publsh person message[%s, %d, %d]\", \n\t\t\t\tperson_msg.name, person_msg.age, person_msg.sex)\n\n\t\t# 按照循环频率延时\n        rate.sleep()\n\nif __name__ == '__main__':\n    try:\n        velocity_publisher()\n    except rospy.ROSInterruptException:\n        pass\n```\n\n`person_subscriber.py`\n\n```python\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport rospy\nfrom learning_topic.msg import Person\n\ndef personInfoCallback(msg):\n    rospy.loginfo(\"Subcribe Person Info: name:%s  age:%d  sex:%d\", \n\t\t\t msg.name, msg.age, msg.sex)\n\ndef person_subscriber():\n\t# ROS节点初始化\n    rospy.init_node('person_subscriber', anonymous=True)\n\n\t# 创建一个Subscriber，订阅名为/person_info的topic，注册回调函数personInfoCallback\n    rospy.Subscriber(\"/person_info\", Person, personInfoCallback)\n\n\t# 循环等待回调函数\n    rospy.spin()\n\nif __name__ == '__main__':\n    person_subscriber()\n```\n\n## 10、客户端Client\n\n```bash\n# 创建功能包\ncd ~/catkin_ws/src\ncatkin_create_pkg learning_service roscpp rospy std_msgs geometry_msgs turtlesim\n```\n\n`turtle_spawn.cpp`\n\n```cpp\n#include \u003cros/ros.h\u003e\n#include \u003cturtlesim/Spawn.h\u003e\n\nint main(int argc, char **argv)\n{\n    // 初始化节点\n    ros::init(argc, argv, \"turtle_spawn\");\n    \n    // 创建节点句柄\n    ros::NodeHandle node;\n    \n    // 发现/spawn服务后，创建一个服务客户端，连接名为/spawn的service\n    ros::service::waitForService(\"/spawn\");\n    ros::ServiceClient add_turtle = node.serviceClient\u003cturtlesim::Spawn\u003e(\"/spawn\");\n        \n    // 初始化turtlesim::Spawn的数据\n    turtlesim::Spawn srv;\n    srv.request.x = 2.0;\n    srv.request.y = 2.0;\n    srv.request.name = \"turtle2\";\n    ROS_INFO(\"Call service to spawn turtle[x:%0.6f, y:%0.6f, name:%s]\",\n             srv.request.x, srv.request.y, srv.request.name.c_str());\n    \n    // 请求服务调用\n    add_turtle.call(srv);\n    // 显示服务调用结果\n    ROS_INFO(\"Spawn turtle successful [name:%s]\", srv.response.name.c_str());\n    \n    return 0;\n}\n```\n\n配置编译规则 `CMakeLists.txt`\n\n```bash\nadd_executable(turtle_spawn src/turtle_spawn.cpp)\ntarget_link_libraries(turtle_spawn ${catkin_LIBRARIES})\n```\n\n编译运行客户端\n\n```bash\ncd ~/catkin_ws\ncatkin_make\n# 成功后，catkin_ws/devel/lib/learning_service下会生成可执行文件turtle_spawn\n\nroscore\nrosrun turtlesim turtlesim_node\nrosrun learning_service turtle_spawn\n```\n\n------------------------------------------------------------------------------------------------\n\n同理，python实现如下：\n\n`turtle_spawn.py`\n\n```python\nimport sys\nimport rospy\nfrom turtlesim.srv import Spawn\n\ndef turtle_spawn():\n\t# ROS节点初始化\n    rospy.init_node('turtle_spawn')\n\n\t# 发现/spawn服务后，创建一个服务客户端，连接名为/spawn的service\n    rospy.wait_for_service('/spawn')\n    try:\n        add_turtle = rospy.ServiceProxy('/spawn', Spawn)\n\n\t\t# 请求服务调用，输入请求数据\n        response = add_turtle(2.0, 2.0, 0.0, \"turtle2\")\n        return response.name\n    except rospy.ServiceException, e:\n        print \"Service call failed: %s\"%e\n\nif __name__ == \"__main__\":\n\t#服务调用并显示调用结果\n    print \"Spwan turtle successfully [name:%s]\" %(turtle_spawn())\n```\n\n## 11、服务端Server\n\n`turtle_command_server.cpp`\n\n```cpp\n#include \u003cros/ros.h\u003e\n#include \u003cgeometry_msgs/Twist.h\u003e\n#include \u003cstd_srvs/Trigger.h\u003e\n\nros::Publisher turtle_vel_pub;\nbool pubCommand = false;\n\nbool commandCallback(std_srvs::Trigger::Request \u0026req,\n                    std_srvs::Trigger::Response \u0026res)\n{\n    // 显示请求数据\n    pubCommand = !pubCommand;\n    ROS_INFO(\"Publish turtle velocity command [%s]\", pubCommand ? \"Yes\" : \"No\");\n    \n    // 设置反馈数据\n    res.success = true;\n    res.message = \"Change turtle command state!\";\n\t\n    return true;\n}\n\nint main(int argc, char **argv)\n{\n    // 初始化节点\n    ros::init(argc, argv, \"turtle_command_server\");\n    \n    // 创建节点句柄\n    ros::NodeHandle n;\n   \n    // 创建一个名为/turtle_command的server，注册回调函数commandCallback\n    ros::ServiceServer command_service = n.advertiseService(\"/turtle_command\", commandCallback);\n    \n    // 创建一个Publisher，发布名为/turtle1/cmd_vel，消息类型为geometry_msgs::Twist，队列长度为10\n    turtle_vel_pub = n.advertise\u003cgeometry_msgs::Twist\u003e(\"/turtle1/cmd_vel\", 10);\n    \n    // 循环等待回调函数\n    ROS_INFO(\"Ready to receive turtle command.\");\n    \n    // 设置循环的频率\n    ros::Rate loop_rate(10);\n    \n    while(ros::ok())\n    {\n        // 查看一次回调函数队列\n        ros::spinOnce();\n        \n        if (pubCommand)\n        {\n            geometry_msgs::Twist vel_msg;\n            vel_msg.linear.x = 0.5;\n            vel_msg.angular.z = 0.2;\n            turtle_vel_pub.publish(vel_msg);\n        }\n        \n        // 按照循环频率延时\n        loop_rate.sleep();\n    }\n    \n    return 0;\n}\n```\n\n查看 std_srvs/Trigger消息结构\n\n```bash\nrossrv show std_srvs/Trigger\n---\nbool success\nstring message\n```\n\n配置编译规则 `CMakeLists.txt`\n\n```bash\nadd_executable(turtle_command_server src/turtle_command_server.cpp)\ntarget_link_libraries(turtle_command_server ${catkin_LIBRARIES})\n```\n\n编译运行客户端\n\n```bash\ncd ~/catkin_ws\ncatkin_make\n# 查看 ~/catkin_ws/devel/lib/learning_service 下会生成可执行文件turtle_spawn\n\nroscore\nrosrun turtlesim turtlesim_node\nrosrun learning_service turtle_command_server\nrosservice call /turtle_command \"{}\"\n```\n\n------------------------------------------------------------------------------------------------\n\n同理，python实现如下：\n\n```python\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport rospy\nimport thread,time\nfrom geometry_msgs.msg import Twist\nfrom std_srvs.srv import Trigger, TriggerResponse\n\npubCommand = False;\nturtle_vel_pub = rospy.Publisher('/turtle1/cmd_vel', Twist, queue_size=10)\n\ndef command_thread():\t\n\twhile True:\n\t\tif pubCommand:\n\t\t\tvel_msg = Twist()\n\t\t\tvel_msg.linear.x = 0.5\n\t\t\tvel_msg.angular.z = 0.2\n\t\t\tturtle_vel_pub.publish(vel_msg)\n\t\t\t\n\t\ttime.sleep(0.1)\n\ndef commandCallback(req):\n\tglobal pubCommand\n\tpubCommand = bool(1-pubCommand)\n\n\t# 显示请求数据\n\trospy.loginfo(\"Publish turtle velocity command![%d]\", pubCommand)\n\n\t# 反馈数据\n\treturn TriggerResponse(1, \"Change turtle command state!\")\n\ndef turtle_command_server():\n\t# ROS节点初始化\n    rospy.init_node('turtle_command_server')\n\n\t# 创建一个名为/turtle_command的server，注册回调函数commandCallback\n    s = rospy.Service('/turtle_command', Trigger, commandCallback)\n\n\t# 循环等待回调函数\n    print \"Ready to receive turtle command.\"\n\n    thread.start_new_thread(command_thread, ())\n    rospy.spin()\n\nif __name__ == \"__main__\":\n    turtle_command_server()\n```\n\n## 12、服务数据的定义与使用\n\n### 12.1、服务数据的定义\n\n```bash\nmkdir -p ~/catkin_ws/src/learning_service/srv\ncd ~/catkin_ws/src/learning_service/srv\ntouch Person.srv\nvi Person.srv\n```\n\n`Person.srv`\n\n```\nstring name\nuint8  age\nuint8  sex\n\nuint8  unknown = 0\nuint8  male    = 1\nuint8 female   = 2\n---\nstring result\n```\n\n`package.xml`\n\n```xml\n\u003cbuild_depend\u003emessage_generation\u003c/build_depend\u003e\n\u003cexec_depend\u003emessage_runtime\u003c/exec_depend\u003e\n```\n\n`CMakeLists.txt`\n\n```txt\nfind_package(......\n  message_generation\n)\n\nadd_service_files(FILES Person.srv)\ngenerate_messages(DEPENDENCIES std_msgs)\n\ncatkin_package(......\n   CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs turtlesim message_runtime\n)\n```\n\n编译\n\n```bash\ncatkin_make\n# 查看 ~/catkin_ws/devel/include/learning_service下面是否存在下列三个文件\nPerson.h\nPersonRequest.h\nPersonResponse.h\n```\n\n### 12.2、服务数据的使用\n\n`person_client.cpp`\n\n```cpp\n#include \u003cros/ros.h\u003e\n#include \"learning_service/Person.h\"\n\nint main(int argc, char** argv)\n{\n    // 初始化ROS节点\n\tros::init(argc, argv, \"person_client\");\n\n    // 创建节点句柄\n\tros::NodeHandle node;\n\n    // 发现/spawn服务后，创建一个服务客户端，连接名为/spawn的service\n\tros::service::waitForService(\"/show_person\");\n\tros::ServiceClient person_client = node.serviceClient\u003clearning_service::Person\u003e(\"/show_person\");\n\n    // 初始化learning_service::Person的请求数据\n\tlearning_service::Person srv;\n\tsrv.request.name = \"Tom\";\n\tsrv.request.age  = 20;\n\tsrv.request.sex  = learning_service::Person::Request::male;\n\n    // 请求服务调用\n\tROS_INFO(\"Call service to show person[name:%s, age:%d, sex:%d]\", \n\t\t\t srv.request.name.c_str(), srv.request.age, srv.request.sex);\n\n\tperson_client.call(srv);\n\n\t// 显示服务调用结果\n\tROS_INFO(\"Show person result : %s\", srv.response.result.c_str());\n\n\treturn 0;\n};\n```\n\n`person_server.cpp`\n\n```cpp\n#include \u003cros/ros.h\u003e\n#include \"learning_service/Person.h\"\n\n\n// service回调函数，输入参数req，输出参数res\nbool personCallback(learning_service::Person::Request  \u0026req,\n         \t\t\tlearning_service::Person::Response \u0026res)\n{\n    // 显示请求数据\n    ROS_INFO(\"Person: name:%s  age:%d  sex:%d\", req.name.c_str(), req.age, req.sex);\n\n\t// 设置反馈数据\n\tres.result = \"OK\";\n\n    return true;\n}\n\nint main(int argc, char **argv)ac\n{\n    // ROS节点初始化\n    ros::init(argc, argv, \"person_server\");\n\n    // 创建节点句柄\n    ros::NodeHandle n;\n\n    // 创建一个名为/show_person的server，注册回调函数personCallback\n    ros::ServiceServer person_service = n.advertiseService(\"/show_person\", personCallback);\n\n    // 循环等待回调函数\n    ROS_INFO(\"Ready to show person informtion.\");\n\n    ros::spin();\n\n    return 0;\n}\n```\n\n`CMakeLists.txt`\n\n```txt\nadd_executable(person_server src/person_server.cpp)\ntarget_link_libraries(person_server ${catkin_LIBRARIES})\nadd_dependencies(person_server ${PROJECT_NAME}_gencpp)\n\nadd_executable(person_client src/person_client.cpp)\ntarget_link_libraries(person_client ${catkin_LIBRARIES})\nadd_dependencies(person_client ${PROJECT_NAME}_gencpp)\n```\n\n编译\n\n```bash\ncatkin_make\n# 查看 ~/catkin_ws/devel/lib/learning_service 下是否存在下面两个可执行文件\n# person_client\n# person_server\n```\n\n使用\n\n```bash\nroscore\nrosrun learning_service person_server\nrosrun learning_service person_client\n```\n\n--------------------------------------------------------------------------------\n\n同理，python实现如下：\n\n`person_client.py`\n\n```python\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport sys\nimport rospy\nfrom learning_service.srv import Person, PersonRequest\n\ndef person_client():\n\t# ROS节点初始化\n    rospy.init_node('person_client')\n\n\t# 发现/spawn服务后，创建一个服务客户端，连接名为/spawn的service\n    rospy.wait_for_service('/show_person')\n    try:\n        person_client = rospy.ServiceProxy('/show_person', Person)\n\n\t\t# 请求服务调用，输入请求数据\n        response = person_client(\"Tom\", 20, PersonRequest.male)\n        return response.result\n    except rospy.ServiceException, e:\n        print \"Service call failed: %s\"%e\n\nif __name__ == \"__main__\":\n\t#服务调用并显示调用结果\n    print \"Show person result : %s\" %(person_client())\n```\n\n`person_server.py`\n\n```python\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport rospy\nfrom learning_service.srv import Person, PersonResponse\n\ndef personCallback(req):\n\t# 显示请求数据\n    rospy.loginfo(\"Person: name:%s  age:%d  sex:%d\", req.name, req.age, req.sex)\n\t# 反馈数据\n    return PersonResponse(\"OK\")\n\ndef person_server():\n\t# ROS节点初始化\n    rospy.init_node('person_server')\n\n\t# 创建一个名为/show_person的server，注册回调函数personCallback\n    s = rospy.Service('/show_person', Person, personCallback)\n\n\t# 循环等待回调函数\n    print \"Ready to show person informtion.\"\n    rospy.spin()\n\nif __name__ == \"__main__\":\n    person_server()\n```\n\n## 13、参数的使用\n\n使用准备\n\n```bash\n# 创建功能包\ncd ~/catkin_ws/src\ncatkin_create_pkg learning_parameter roscpp rospy\n\n# 启动小海龟\nroscore\nrosrun turtlesim turtlesim_node\n```\n\n参数命令\n\n```bash\n# 查询所有参数\nrosparam list\n# 查询背景色\nrosparam get /turtlesim/background_b\nrosparam get /turtlesim/background_g\nrosparam get /turtlesim/background_r\n# 查询发行版\nrosparam get /rosdistro\nrosparam get /rosversion\n\n# 修改参数\nrosparam set /turtlesim/background_b 100\n# 修改并不能立即生效，需要发送clear请求\nrosservice call /clear \"{}\"\n\n# 转存参数\nrosparam dump param.yaml\n# 加载参数\n# 修改 param.yaml 文件内容后执行（验证同理）\nrosparam load param.yaml\n\n# 删除参数（验证同理）\nrosparam delete /turtlesim/background_b\n```\n\n代码实现\n\n`parameter_config.cpp`\n\n```cpp\n#include \u003cstring.h\u003e\n#include \u003cros/ros.h\u003e\n#include \u003cstd_srvs/Empty.h\u003e\n\nint main(int argc, char **argv)\n{\n    int red, green, blue;\n    \n    // 初始化ROS节点\n    ros::init(argc, argv, \"parameter_config\");\n    \n    // 创建节点句柄\n    ros::NodeHandle node;\n    \n    // 读取背景色参数\n    ros::param::get(\"/turtlesim/background_r\", red);\n    ros::param::get(\"/turtlesim/background_g\", green);\n    ros::param::get(\"/turtlesim/background_b\", blue);\n    ROS_INFO(\"Get Background Color[%d, %d, %d]\", red, green, blue);\n    \n    // 设置背景色参数\n    red   = 255;\n    green = 255;\n    blue  = 255;\n    ros::param::set(\"/turtlesim/background_r\", red);\n\tros::param::set(\"/turtlesim/background_g\", green);\n\tros::param::set(\"/turtlesim/background_b\", blue);\n    ROS_INFO(\"Set Background Color[%d, %d, %d]\", red, green, blue);\n\n    // 读取背景色参数\n    ros::param::get(\"/turtlesim/background_r\", red);\n    ros::param::get(\"/turtlesim/background_g\", green);\n    ros::param::get(\"/turtlesim/background_b\", blue);\n    ROS_INFO(\"Re-get Background Color[%d, %d, %d]\", red, green, blue);\n    \n    // 调用服务，刷新背景色\n    ros::service::waitForService(\"/clear\");\n    ros::ServiceClient clear_background = node.serviceClient\u003cstd_srvs::Empty\u003e(\"/clear\");\n    std_srvs::Empty srv;\n    clear_background.call(srv);\n    \n    sleep(1);\n    \n    return 0;\n}\n```\n\n配置编译规则 `CMakeLists.txt`\n\n```bash\nadd_executable(parameter_config src/parameter_config.cpp)\ntarget_link_libraries(parameter_config ${catkin_LIBRARIES})\n```\n\n编译运行客户端\n\n```bash\ncd ~/catkin_ws\ncatkin_make\n# 查看 ~/catkin_ws/devel/lib/learning_service 下会生成可执行文件turtle_spawn\n\nroscore\nrosrun turtlesim turtlesim_node\nrosrun learning_parameter parameter_config\nrosservice call /turtle_command \"{}\"\n```\n\n------------------------------------------------------------------------------------------------\n\n同理，Python实现如下：\n\n```python\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport sys\nimport rospy\nfrom std_srvs.srv import Empty\n\ndef parameter_config():\n\t# ROS节点初始化\n    rospy.init_node('parameter_config', anonymous=True)\n\n\t# 读取背景颜色参数\n    red   = rospy.get_param('/turtlesim/background_r')\n    green = rospy.get_param('/turtlesim/background_g')\n    blue  = rospy.get_param('/turtlesim/background_b')\n    rospy.loginfo(\"Get Backgroud Color[%d, %d, %d]\", red, green, blue)\n    \n\t# 设置背景颜色参数\n    red   = 255;\n    green = 255;\n    blue  = 255;\n    rospy.set_param(\"/turtlesim/background_r\", red);\n    rospy.set_param(\"/turtlesim/background_g\", green);\n    rospy.set_param(\"/turtlesim/background_b\", blue);\n    rospy.loginfo(\"Set Backgroud Color[%d, %d, %d]\", red, green, blue)\n\n\t# 读取背景颜色参数\n    red   = rospy.get_param('/turtlesim/background_r')\n    green = rospy.get_param('/turtlesim/background_g')\n    blue  = rospy.get_param('/turtlesim/background_b')\n    rospy.loginfo(\"Get Backgroud Color[%d, %d, %d]\", red, green, blue)\n\n\t# 发现/spawn服务后，创建一个服务客户端，连接名为/spawn的service\n    rospy.wait_for_service('/clear')\n    try:\n        clear_background = rospy.ServiceProxy('/clear', Empty)\n        \n\t\t# 请求服务调用，输入请求数据\n        response = clear_background()\n        return response\n    except rospy.ServiceException, e:\n        print \"Service call failed: %s\"%e\n\nif __name__ == \"__main__\":\n    parameter_config()\n```\n\n## 14、ROS坐标系管理系统\n\n```bash\n# 安装tf包\nsudo apt-get install ros-melodic-turtle-tf\n\n# 启动launch脚本\nroslaunch turtle_tf turtle_tf_demo.launch\n\n# 运行海龟键盘控制节点\nrosrun turtlesim turtle_teleop_key\n\n# tf树工具\nrosrun tf view_frames\n# 打开当前文件夹下的frames.pdf文件即可\n\n# 坐标变换关系命令行工具\nrosrun tf tf_echo turtle1 turtle2\n\n# 坐标变换关系可视化工具（三维关系）\nrosrun rviz rviz -d `rospack find turtle_tf` /rviz/turtle_rviz.rviz\n# 1、Global Options \u003e Fixed Frame 修改为 world\n# 2、Add \u003e By display type \u003e rviz \u003e tf \u003e OK\n```\n\n## 15、tf坐标系广播与监听\n\n```bash\n# 创建功能包\ncd ~/catkin_ws/src\ncatkin_create_pkg learning_tf roscpp rospy tf turtlesim\n```\n\n`turtle_tf_broadcaster.cpp`\n\n```cpp\n#include \u003cros/ros.h\u003e\n#include \u003ctf/transform_broadcaster.h\u003e\n#include \u003cturtlesim/Pose.h\u003e\n\nstd::string turtle_name;\n\nvoid poseCallback(const turtlesim::PoseConstPtr\u0026 msg)\n{\n    // 创建tf广播器\n    tf::TransformBroadcaster br;\n    \n    // 初始化tf数据\n    tf::Transform transform;\n    transform.setOrigin(tf::Vector3(msg-\u003ex, msg-\u003ey, 0.0));\n    tf::Quaternion q;\n    q.setRPY(0, 0, msg-\u003etheta);\n    transform.setRotation(q);\n    \n    // 广播world与海龟坐标系的tf数据\n    br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), \"world\", turtle_name));\n}\n\nint main(int argc, char **argv)\n{\n    // 初始化ROS节点\n    ros::init(argc, argv, \"my_tf_broadcaster\");\n    \n    // 输入参数作为海龟的名字\n    if (argc != 2)\n    {\n        ROS_ERROR(\"need turtle name as argument\");\n        return -1;\n    }\n\tturtle_name = argv[1];    \n\n    // 订阅海龟的位姿话题\n    ros::NodeHandle node;\n    ros::Subscriber sub = node.subscribe(turtle_name + \"/pose\", 10, \u0026poseCallback);\n    \n    // 循环等待回调函数\n    ros::spin();\n    \n    return 0;\n}\n```\n\n`turtle_tf_listener.cpp`\n\n```cpp\n#include \u003cros/ros.h\u003e\n#include \u003ctf/transform_listener.h\u003e\n#include \u003cgeometry_msgs/Twist.h\u003e\n#include \u003cturtlesim/Spawn.h\u003e\n\nint main(int argc, char **argv)\n{\n    // 初始化ROS节点\n    ros::init(argc, argv, \"my_tf_listener\");\n    \n    // 创建节点句柄\n    ros::NodeHandle node;\n    \n    // 请求产生turtle2\n    ros::service::waitForService(\"/spawn\");\n    ros::ServiceClient add_turtle = node.serviceClient\u003cturtlesim::Spawn\u003e(\"/spawn\");\n    turtlesim::Spawn srv;\n    add_turtle.call(srv);\n    \n    // 创建发布turtle2速度控制指令的发布者\n    ros::Publisher turtle_vel = node.advertise\u003cgeometry_msgs::Twist\u003e(\"/turtle2/cmd_vel\", 10);\n    \n    // 创建tf的监听器\n    tf::TransformListener listener;\n    \n    ros::Rate rate(10.0);\n    while(node.ok())\n    {\n        // 获取turtle1与turtle2坐标系之间的tf数据\n        tf::StampedTransform transform;\n        try\n        {\n            listener.waitForTransform(\"/turtle2\", \"/turtle1\", ros::Time(0), ros::Duration(3.0));\n            listener.lookupTransform(\"/turtle2\", \"/turtle1\", ros::Time(0), transform);\n        }\n        catch (tf::TransformException \u0026ex)\n        {\n            ROS_ERROR(\"%s\", ex.what());\n            ros::Duration(1.0).sleep();\n            continue;\n        }\n        \n        // 根据turtle1与turtle2坐标系之间的位置关系，发布turtle2速度控制指令\n        geometry_msgs::Twist vel_msg;\n        vel_msg.angular.z = 4.0 * atan2(transform.getOrigin().y(),\n\t\t\t\t                        transform.getOrigin().x());\n\t\tvel_msg.linear.x = 0.5 * sqrt(pow(transform.getOrigin().x(), 2) +\n\t\t\t\t                      pow(transform.getOrigin().y(), 2));\n        turtle_vel.publish(vel_msg);\n        \n        rate.sleep();\n    }\n    \n    return 0;\n}\n```\n\n配置编译规则 `CMakeLists.txt`\n\n```bash\nadd_executable(turtle_tf_broadcaster src/turtle_tf_broadcaster.cpp)\ntarget_link_libraries(turtle_tf_broadcaster ${catkin_LIBRARIES})\n\nadd_executable(turtle_tf_listener src/turtle_tf_listener.cpp)\ntarget_link_libraries(turtle_tf_listener ${catkin_LIBRARIES})\n```\n\n编译运行客户端\n\n```bash\ncd ~/catkin_ws\ncatkin_make\n# 查看 ~/catkin_ws/devel/lib/learning_tf 下会生成可执行文件 turtle_tf_broadcaster 和 turtle_tf_listener\n\nroscore\nrosrun turtlesim turtlesim_node\n# ros重映射机制，控制节点名称不同，防止冲突\nrosrun learning_tf turtle_tf_broadcaster __name:=turtle1_tf_broadcaster /turtle1\nrosrun learning_tf turtle_tf_broadcaster __name:=turtle2_tf_broadcaster /turtle2\nrosrun learning_tf turtle_tf_listener\nrosrun turtlesim turtle_teleop_key\n\n# 报错日志：[ERROR] [1720012351.995199109]: \"turtle2\" passed to lookupTransform argument target_frame does not exist. [ERROR] [1720012356.027073796]: Lookup would require extrapolation into the past. Requested time 1720012353.161287154 but the earliest data is at time 1720012353.788391871, when looking up transform from frame [turtle1] to frame [turtle2]\n```\n\n------------------------------------------------------------------------------------------------\n\n同理，python实现如下：\n\n`turtle_tf_broadcaster.py`\n\n```python\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport roslib\nroslib.load_manifest('learning_tf')\nimport rospy\n\nimport tf\nimport turtlesim.msg\n\ndef handle_turtle_pose(msg, turtlename):\n    br = tf.TransformBroadcaster()\n    br.sendTransform((msg.x, msg.y, 0),\n                     tf.transformations.quaternion_from_euler(0, 0, msg.theta),\n                     rospy.Time.now(),\n                     turtlename,\n                     \"world\")\n\nif __name__ == '__main__':\n    rospy.init_node('turtle_tf_broadcaster')\n    turtlename = rospy.get_param('~turtle')\n    rospy.Subscriber('/%s/pose' % turtlename,\n                     turtlesim.msg.Pose,\n                     handle_turtle_pose,\n                     turtlename)\n    rospy.spin()\n```\n\n`turtle_tf_listener.py`\n\n```python\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport roslib\nroslib.load_manifest('learning_tf')\nimport rospy\nimport math\nimport tf\nimport geometry_msgs.msg\nimport turtlesim.srv\n\nif __name__ == '__main__':\n    rospy.init_node('turtle_tf_listener')\n\n    listener = tf.TransformListener()\n\n    rospy.wait_for_service('spawn')\n    spawner = rospy.ServiceProxy('spawn', turtlesim.srv.Spawn)\n    spawner(4, 2, 0, 'turtle2')\n\n    turtle_vel = rospy.Publisher('turtle2/cmd_vel', geometry_msgs.msg.Twist, queue_size=1)\n\n    rate = rospy.Rate(10.0)\n    while not rospy.is_shutdown():\n        try:\n            (trans,rot) = listener.lookupTransform('/turtle2', '/turtle1', rospy.Time(0))\n        except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException):\n            continue\n\n        angular = 4 * math.atan2(trans[1], trans[0])\n        linear = 0.5 * math.sqrt(trans[0] ** 2 + trans[1] ** 2)\n        cmd = geometry_msgs.msg.Twist()\n        cmd.linear.x = linear\n        cmd.angular.z = angular\n        turtle_vel.publish(cmd)\n\n        rate.sleep()\n```\n\n编译运行客户端\n\n```bash\ncd ~/catkin_ws\ncatkin_make\n# 查看 ~/catkin_ws/devel/lib/learning_tf 下会生成可执行文件 turtle_tf_broadcaster 和 turtle_tf_listener\n\nroscore\nrosrun turtlesim turtlesim_node\n# ros重映射机制，控制节点名称不同，防止冲突\nrosrun learning_tf turtle_tf_broadcaster.py __name:=turtle1_tf_broadcaster _turtle:=turtle1\nrosrun learning_tf turtle_tf_broadcaster.py __name:=turtle2_tf_broadcaster _turtle:=turtle2\nrosrun learning_tf turtle_tf_listener.py\nrosrun turtlesim turtle_teleop_key\n```\n\n## 16、launch启动文件\n\n```bash\ncd ~/catkin_ws/src\ncatkin_create_pkg learning_launch\n\nmkdir ~/catkin_ws/src/learning_launch/launch\ncd ~/catkin_ws/src/learning_launch/launch\ntouch simple.launch\n```\n\n`simple.launch`\n\n```xml\n\u003claunch\u003e\n\t\u003cnode pkg=\"learning_topic\" type=\"person_subscriber\" name=\"listener\" output=\"screen\"/\u003e\n    \u003cnode pkg=\"learning_topic\" type=\"person_publisher\" name=\"talker\" output=\"screen\"/\u003e\n\u003c/launch\u003e\n```\n\n编译运行\n\n```bash\ncd ~/catkin_ws\ncatkin_make\n\nroslaunch learning_launch simple.launch\n```\n\n`turtlesim_parameter_config.launch`\n\n```xml\n\u003claunch\u003e\n\t\u003cparam name=\"/turtle_number\" value=\"2\"/\u003e\n    \u003cnode pkg=\"turtlesim\" type=\"turtlesim_node\" name=\"turtlesim_node\"\u003e\n\t\t\u003cparam name=\"turtle_name1\" value=\"Tom\"/\u003e\n\t\t\u003cparam name=\"turtle_name2\" value=\"Jerry\"/\u003e\n\t\t\u003crosparam file=\"$(find learning_launch)/config/param.yaml\" command=\"load\"/\u003e\n\t\u003c/node\u003e\n    \u003cnode pkg=\"turtlesim\" type=\"turtle_teleop_key\" name=\"turtle_teleop_key\" output=\"screen\"/\u003e\n\u003c/launch\u003e\n```\n\n`param.yaml`\n\n```yaml\nA: 123\nB: \"hello\"\n\ngroup:\n  C: 456\n  D: \"hello\"\n```\n\n编译运行\n\n```bash\n# 编译\ncd ~/catkin_ws\ncatkin_make\n\n# 启动\nroslaunch learning_launch turtlesim_parameter_config.launch\n# 验证\nrosparam list\nrosparam get /turtle_number\nrosparam get /turtlesim_node/turtle_name1\nrosparam get /turtlesim_node/turtle_name2\nrosparam get /turtlesim_node/A\nrosparam get /turtlesim_node/B\nrosparam get /turtlesim_node/group/C\nrosparam get /turtlesim_node/group/D\n```\n\n`start_tf_demo_c++.launch`\n\n```xml\n\u003claunch\u003e\n    \u003cnode pkg=\"turtlesim\" type=\"turtlesim_node\" name=\"sim\"/\u003e\n    \u003cnode pkg=\"turtlesim\" type=\"turtle_teleop_key\" name=\"teleop\" output=\"screen\"/\u003e\n    \u003cnode pkg=\"learning_tf\" type=\"turtle_tf_broadcaster\" name=\"turtle1_tf_broadcaster\" args=\"/turtle1\"/\u003e\n    \u003cnode pkg=\"learning_tf\" type=\"turtle_tf_broadcaster\" name=\"turtle2_tf_broadcaster\" args=\"/turtle2\"/\u003e\n    \u003cnode pkg=\"learning_tf\" type=\"turtle_tf_listener\" name=\"listener\"/\u003e\n\u003c/launch\u003e\n```\n\n`start_tf_demo_py.launch`\n\n```xml\n\u003claunch\u003e\n\t\u003cnode pkg=\"turtlesim\" type=\"turtlesim_node\" name=\"sim\"/\u003e\n\t\u003cnode pkg=\"turtlesim\" type=\"turtle_teleop_key\" name=\"teleop\" output=\"screen\"/\u003e\n\t\u003cnode pkg=\"learning_tf\" type=\"turtle_tf_broadcaster.py\" name=\"turtle1_tf_broadcaster\"\u003e\n\t  \u003cparam name=\"turtle\" type=\"string\" value=\"turtle1\" /\u003e\n\t\u003c/node\u003e\n\t\u003cnode pkg=\"learning_tf\" type=\"turtle_tf_broadcaster.py\" name=\"turtle2_tf_broadcaster\"\u003e\n\t  \u003cparam name=\"turtle\" type=\"string\" value=\"turtle2\" /\u003e \n\t\u003c/node\u003e\n    \u003cnode pkg=\"learning_tf\" type=\"turtle_tf_listener.py\" name=\"listener\" /\u003e\n\u003c/launch\u003e\n```\n\n`turtlesim_remap.launch`\n\n```xml\n\u003claunch\u003e\n\t\u003cinclude file=\"$(find learning_launch)/launch/simple.launch\" /\u003e\n    \u003cnode pkg=\"turtlesim\" type=\"turtlesim_node\" name=\"turtlesim_node\"\u003e\n\t\t\u003cremap from=\"/turtle1/cmd_vel\" to=\"/cmd_vel\"/\u003e\n\t\u003c/node\u003e\n\u003c/launch\u003e\n```\n\n运行\n\n```bash\n# 启动\nroslaunch learning_launch turtlesim_remap.launch\n\n# 验证 /turtle1/cmd_vel 是否变成了 /cmd_vel\nrostopic list\n# 发布 /cmd_vel 话题，验证是否生效\nrostopic pub /cmd_vel geometry_msgs/Twist \"linear:\n  x: 1.0\n  y: 0.0\n  z: 0.0\nangular:\n  x: 0.0\n  y: 0.0\n  z: 0.0\"\n```\n\n## 17、可视化工具\n\n### 17.1、Qt工具箱\n\n```bash\n# 日志输出工具\nrqt_console\n```\n\n![image-20240704232024629](https://s2.loli.net/2024/07/04/vj745JAb9hIyW1N.png)\n\n```bash\n# 计算图可视化工具\nrqt_graph\n```\n\n![image-20240704232157744](https://s2.loli.net/2024/07/04/4CAYIQV19kSxNl5.png)\n\n```bash\n# 数据绘图工具\nrqt_plot\n```\n\n![image-20240704231951423](https://s2.loli.net/2024/07/04/KE4o5biTLBxWdJr.png)\n\n```bash\n# 图像渲染工具\nrqt_image_view\n```\n\n![image-20240704232311320](https://s2.loli.net/2024/07/04/3qvY8MmI9JZaEip.png)\n\n```bash\n# 工具集成\nrqt\n```\n\n![image-20240704232453207](https://s2.loli.net/2024/07/04/kJNTmohSGU6blfv.png)\n\n![image-20240704232620663](https://s2.loli.net/2024/07/04/6nZRHYOBmhrXoLp.png)\n\n### 17.2、Rviz\n\n![image-20240704233014818](https://s2.loli.net/2024/07/04/pbJWSxKNtAqMD9j.png)\n\n![image-20240704233026597](https://s2.loli.net/2024/07/04/TZuwWsabelnkC2f.png)\n\n```bash\n# 启动 \nroscore\nrosrun rviz rviz\n```\n\n### 17.3、Gazebo\n\n![image-20240704233353053](https://s2.loli.net/2024/07/04/7m2oZtPxwSeFOW1.png)\n\n![image-20240704233505370](https://s2.loli.net/2024/07/04/4Z1ICLuxXjFfw6h.png)\n\n```bash\n# 启动\nroslaunch gazebo_ros willowgarage_world.launch\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvectorxxxx%2F25-ros","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvectorxxxx%2F25-ros","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvectorxxxx%2F25-ros/lists"}