中文个人网站模板下载,wordpress翻转框,个人网站做排名,接网站开发做多少钱一、前期准备
1.1 安装 1. 首先安装“乌龟跟随”案例的功能包以及依赖项。
安装方式1#xff08;二进制方式安装#xff09;#xff1a;
sudo apt-get install ros-humble-turtle-tf2-py ros-humble-tf2-tools ros-humble-tf-transformations
安装方式2#xff08;克…一、前期准备
1.1 安装 1. 首先安装“乌龟跟随”案例的功能包以及依赖项。
安装方式1二进制方式安装
sudo apt-get install ros-humble-turtle-tf2-py ros-humble-tf2-tools ros-humble-tf-transformations
安装方式2克隆源码并构建
git clone https://github.com/ros/geometry_tutorials.git -b ros2 2. 还需要安装一个名为 transforms3d 的 Python 包它为 tf_transformations包提供四元数和欧拉角变换功能安装命令如下
sudo apt install python3-pip
pip3 install transforms3d
1.2 执行 1. 启动两个终端终端1输入如下命令该命令会启动 turtlesim_node 节点turtlesim_node 节点中自带一只小乌龟 turtle1除此之外还会新生成一只乌龟 turtle2turtle2 会运行至 turtle1 的位置。
ros2 launch turtle_tf2_py turtle_tf2_demo.launch.py 2. 终端2输入如下命令该终端下可以通过键盘控制 turtle1 运动并且 turtle2 会跟随 turtle1 运动
ros2 run turtlesim turtle_teleop_key
1.3 坐标相关消息 坐标变换的实现其本质是基于话题通信的发布订阅模型的发布方可以发布坐标系之间的相对关系订阅方则可以监听这些消息并实现不同坐标系之间的变换。显然的根据之前介绍在话题通信中接口消息作为数据载体在整个通信模型中是比较重要的一部分本节将会介绍坐标变换中常用的两种接口消息
1.geometry_msgs/msg/TransformStamped // 用于描述某一时刻两个坐标系之间相对关系的接口
2.geometry_msgs/msg/PointStamped // 用于描述某一时刻坐标系内某个坐标点的位置的接口 第一种geometry_msgs/msg/TransformStamped
通过如下命令查看接口定义
ros2 interface show geometry_msgs/msg/TransformStamped
接口定义解释
std_msgs/Header headerbuiltin_interfaces/Time stamp # 时间戳int32 secuint32 nanosecstring frame_id # 父级坐标系string child_frame_id # 子级坐标系Transform transform # 子级坐标系相对于父级坐标系的位姿Vector3 translation # 三维偏移量float64 xfloat64 yfloat64 zQuaternion rotation # 四元数float64 x 0float64 y 0float64 z 0float64 w 1 第二种geometry_msgs/msg/PointStamped
通过如下命令查看接口定义
ros2 interface show geometry_msgs/msg/PointStamped
接口定义解释
std_msgs/Header headerbuiltin_interfaces/Time stamp # 时间戳int32 secuint32 nanosecstring frame_id # 参考系
Point point # 三维坐标float64 xfloat64 yfloat64 z
二、乌龟跟随实例 “乌龟跟随”案例的核心是如何确定 turtle1 相对 turtle2 的位置只要该位置确定就可以把其作为目标点并控制 turtle2 向其运动。相对位置的确定可以通过坐标变换实现大致思路是先分别广播 turtle1 相对于 world 和 turtle2 相对于 world 的坐标系关系然后再通过监听坐标系关系进一步获取 turtle1 相对于 turtle2 的坐标系关系。
2.1 C实现流程
2.1.1 准备工作
1.新建功能包
ros2 pkg create cpp05_exercise --build-type ament_cmake --dependencies rclcpp tf2 tf2_ros geometry_msgs turtlesim
2.创建launch目录:
功能包cpp05_exercise下新建launch文件并编辑配置文件。
功能包cpp05_exercise的 CMakeLists.txt 文件添加如下内容
install(DIRECTORY launchDESTINATION share/${PROJECT_NAME}
)
2.1.2 编写生成新乌龟 功能包 cpp05_exercise 的 src 目录下新建 C 文件 exer01_tf_spawn.cpp并编辑文件输入如下内容
/* 需求编写客户端发送请求生成一只新的乌龟。步骤1.包含头文件2.初始化 ROS2 客户端3.定义节点类3-1.声明并获取参数3-2.创建客户端3-3.等待服务连接3-4.组织请求数据并发送4.创建对象指针调用其功能,并处理响应5.释放资源。*/
// 1.包含头文件
#include rclcpp/rclcpp.hpp
#include turtlesim/srv/spawn.hppusing namespace std::chrono_literals;// 3.定义节点类
class TurtleSpawnClient: public rclcpp::Node{public:TurtleSpawnClient():Node(turtle_spawn_client){// 3-1.声明并获取参数this-declare_parameter(x,2.0);this-declare_parameter(y,8.0);this-declare_parameter(theta,0.0);this-declare_parameter(turtle_name,turtle2);x this-get_parameter(x).as_double();y this-get_parameter(y).as_double();theta this-get_parameter(theta).as_double();turtle_name this-get_parameter(turtle_name).as_string();// 3-2.创建客户端client this-create_clientturtlesim::srv::Spawn(/spawn);}// 3-3.等待服务连接bool connect_server(){while (!client-wait_for_service(1s)){if (!rclcpp::ok()){RCLCPP_INFO(this-get_logger(),客户端退出);return false;}RCLCPP_INFO(this-get_logger(),服务连接中请稍候...);}return true;}// 3-4.组织请求数据并发送rclcpp::Clientturtlesim::srv::Spawn::FutureAndRequestId send_request(){auto request std::make_sharedturtlesim::srv::Spawn::Request();request-x x;request-y y;request-theta theta;request-name turtle_name;return client-async_send_request(request);}private:rclcpp::Clientturtlesim::srv::Spawn::SharedPtr client;float_t x,y,theta;std::string turtle_name;
};int main(int argc, char ** argv)
{// 2.初始化 ROS2 客户端rclcpp::init(argc,argv);// 4.创建对象指针并调用其功能auto client std::make_sharedTurtleSpawnClient();bool flag client-connect_server();if (!flag){RCLCPP_INFO(client-get_logger(),服务连接失败!);return 0;}auto response client-send_request();// 处理响应if (rclcpp::spin_until_future_complete(client,response) rclcpp::FutureReturnCode::SUCCESS){RCLCPP_INFO(client-get_logger(),请求正常处理);std::string name response.get()-name;if (name.empty()){RCLCPP_INFO(client-get_logger(),乌龟重名导致生成失败);} else {RCLCPP_INFO(client-get_logger(),乌龟%s生成成功, name.c_str());}} else {RCLCPP_INFO(client-get_logger(),请求异常);}// 5.释放资源。rclcpp::shutdown();return 0;
}
2.1.3 编写坐标变换广播 功能包 cpp05_exercise 的 src 目录下新建 C 文件 exer02_tf_broadcaster.cpp并编辑文件输入如下内容
/* 需求发布乌龟坐标系到窗口坐标系的坐标变换。步骤1.包含头文件2.初始化 ROS 客户端3.定义节点类3-1.声明并解析乌龟名称参数3-2.创建动态坐标变换发布方3-3.创建乌龟位姿订阅方3-4.根据订阅到的乌龟位姿生成坐标帧并广播。4.调用 spin 函数并传入对象指针5.释放资源。*/
// 1.包含头文件
#include geometry_msgs/msg/transform_stamped.hpp#include rclcpp/rclcpp.hpp
#include tf2/LinearMath/Quaternion.h
#include tf2_ros/transform_broadcaster.h
#include turtlesim/msg/pose.hppusing std::placeholders::_1;// 3.定义节点类
class TurtleFrameBroadcaster : public rclcpp::Node
{
public:TurtleFrameBroadcaster(): Node(turtle_frame_broadcaster){// 3-1.声明并解析乌龟名称参数this-declare_parameter(turtle_name,turtle1);turtle_name this-get_parameter(turtle_name).as_string();// 3-2.创建动态坐标变换发布方tf_broadcaster_ std::make_uniquetf2_ros::TransformBroadcaster(*this);std::string topic_name turtle_name /pose;// 3-3.创建乌龟位姿订阅方subscription_ this-create_subscriptionturtlesim::msg::Pose(topic_name, 10,std::bind(TurtleFrameBroadcaster::handle_turtle_pose, this, _1));}private:// 3-4.根据订阅到的乌龟位姿生成坐标帧并广播。 void handle_turtle_pose(const turtlesim::msg::Pose msg){// 组织消息geometry_msgs::msg::TransformStamped t;rclcpp::Time now this-get_clock()-now();t.header.stamp now;t.header.frame_id world;t.child_frame_id turtle_name;t.transform.translation.x msg.x;t.transform.translation.y msg.y;t.transform.translation.z 0.0;tf2::Quaternion q;q.setRPY(0, 0, msg.theta);t.transform.rotation.x q.x();t.transform.rotation.y q.y();t.transform.rotation.z q.z();t.transform.rotation.w q.w();// 发布消息tf_broadcaster_-sendTransform(t);}rclcpp::Subscriptionturtlesim::msg::Pose::SharedPtr subscription_;std::unique_ptrtf2_ros::TransformBroadcaster tf_broadcaster_;std::string turtle_name;
};int main(int argc, char * argv[])
{// 2.初始化 ROS 客户端rclcpp::init(argc, argv);// 4.调用 spin 函数并传入对象指针rclcpp::spin(std::make_sharedTurtleFrameBroadcaster());// 5.释放资源。rclcpp::shutdown();return 0;
}
2.1.4 编写坐标变换监听 功能包 cpp05_exercise 的 src 目录下新建 C 文件 exer03_tf_listener.cpp并编辑文件输入如下内容
/* 需求广播的坐标系消息并生成 turtle2 相对于 turtle1 的坐标系数据并进一步生成控制 turtle2 运动的速度指令。步骤1.包含头文件2.初始化 ROS 客户端3.定义节点类3-1.声明并解析参数3-2.创建tf缓存对象指针3-3.创建tf监听器3-4.按照条件查找符合条件的坐标系并生成变换后的坐标帧。3-5.生成 turtle2 的速度指令并发布。4.调用 spin 函数并传入对象指针5.释放资源。*/
#include rclcpp/rclcpp.hpp
#include tf2_ros/transform_listener.h
#include tf2_ros/buffer.h
#include tf2/LinearMath/Quaternion.h
#include geometry_msgs/msg/twist.hppusing namespace std::chrono_literals;// 3.定义节点类
class TurtleFrameListener : public rclcpp::Node {
public:TurtleFrameListener():Node(turtle_frame_listener){// 3-1.声明并解析参数this-declare_parameter(target_frame,turtle2);this-declare_parameter(source_frame,turtle1);target_frame this-get_parameter(target_frame).as_string();source_frame this-get_parameter(source_frame).as_string();// 3-2.创建tf缓存对象指针tf_buffer_ std::make_uniquetf2_ros::Buffer(this-get_clock());// 3-3.创建tf监听器transform_listener_ std::make_sharedtf2_ros::TransformListener(*tf_buffer_);twist_pub_ this-create_publishergeometry_msgs::msg::Twist(target_frame /cmd_vel,10);timer_ this-create_wall_timer(1s, std::bind(TurtleFrameListener::on_timer,this));}private:void on_timer(){// 3-4.按照条件查找符合条件的坐标系并生成变换后的坐标帧。geometry_msgs::msg::TransformStamped transformStamped;try{transformStamped tf_buffer_-lookupTransform(target_frame,source_frame,tf2::TimePointZero);}catch(const tf2::LookupException e){RCLCPP_INFO(this-get_logger(),坐标变换异常%s,e.what());return;}// 3-5.生成 turtle2 的速度指令并发布。geometry_msgs::msg::Twist msg;static const double scaleRotationRate 1.0;msg.angular.z scaleRotationRate * atan2(transformStamped.transform.translation.y,transformStamped.transform.translation.x);static const double scaleForwardSpeed 0.5;msg.linear.x scaleForwardSpeed * sqrt(pow(transformStamped.transform.translation.x, 2) pow(transformStamped.transform.translation.y, 2));twist_pub_-publish(msg);}rclcpp::Publishergeometry_msgs::msg::Twist::SharedPtr twist_pub_;rclcpp::TimerBase::SharedPtr timer_;std::shared_ptrtf2_ros::TransformListener transform_listener_;std::unique_ptrtf2_ros::Buffer tf_buffer_;std::string target_frame;std::string source_frame;
};int main(int argc, char const *argv[])
{// 2.初始化 ROS 客户端rclcpp::init(argc,argv);// 4.调用 spin 函数并传入对象指针rclcpp::spin(std::make_sharedTurtleFrameListener());// 5.释放资源。rclcpp::shutdown();return 0;
}
2.1.5 编写 launch 文件 launch 目录下新建文件exer01_turtle_follow.launch.py并编辑文件输入如下内容
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfigurationdef generate_launch_description():# 声明参数turtle1 DeclareLaunchArgument(nameturtle1,default_valueturtle1)turtle2 DeclareLaunchArgument(nameturtle2,default_valueturtle2)# 启动 turtlesim_node 节点turtlesim_node Node(packageturtlesim, executableturtlesim_node, namet1)# 生成一只新乌龟spawn Node(packagecpp05_exercise, executableexer01_tf_spawn,namespawn1,parameters[{turtle_name:LaunchConfiguration(turtle2)}])# tf 广播tf_broadcaster1 Node(packagecpp05_exercise,executableexer02_tf_broadcaster,nametf_broadcaster1)tf_broadcaster2 Node(packagecpp05_exercise,executableexer02_tf_broadcaster,nametf_broadcaster1,parameters[{turtle_name:LaunchConfiguration(turtle2)}])# tf 监听tf_listener Node(packagecpp05_exercise,executableexer03_tf_listener,nametf_listener,parameters[{target_frame:LaunchConfiguration(turtle2),source_frame:LaunchConfiguration(turtle1)}])return LaunchDescription([turtle1,turtle2,turtlesim_node,spawn,tf_broadcaster1,tf_broadcaster2,tf_listener])
2.1.6 编辑配置文件
1.package.xml
在创建功能包时所依赖的功能包已经自动配置了配置内容如下
dependrclcpp/depend
dependtf2/depend
dependtf2_ros/depend
dependgeometry_msgs/depend
dependturtlesim/depend
2.CMakeLists.txt
CMakeLists.txt 中发布和订阅程序核心配置如下
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(tf2 REQUIRED)
find_package(tf2_ros REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(turtlesim REQUIRED)add_executable(exer01_tf_spawn src/exer01_tf_spawn.cpp)
ament_target_dependencies(exer01_tf_spawnrclcpptf2tf2_rosgeometry_msgsturtlesim
)add_executable(exer02_tf_broadcaster src/exer02_tf_broadcaster.cpp)
ament_target_dependencies(exer02_tf_broadcasterrclcpptf2tf2_rosgeometry_msgsturtlesim
)add_executable(exer03_tf_listener src/exer03_tf_listener.cpp)
ament_target_dependencies(exer03_tf_listenerrclcpptf2tf2_rosgeometry_msgsturtlesim
)install(TARGETS exer01_tf_spawnexer02_tf_broadcasterexer03_tf_listenerDESTINATION lib/${PROJECT_NAME})install(DIRECTORY launchDESTINATION share/${PROJECT_NAME}
)
2.1.7 编译执行
终端中进入当前工作空间编译功能包
colcon build --packages-select cpp05_exercise
当前工作空间下启动终端输入如下命令运行launch文件
. install/setup.bash
ros2 launch cpp05_exercise exer01_turtle_follow.launch.py
再新建终端启动 turtlesim 键盘控制节点该终端下可以通过键盘控制 turtle1 运动并且 turtle2 会跟随 turtle1 运动。最终的运行结果与演示案例类似。
ros2 run turtlesim turtle_teleop_key
最终效果展示。
2.2 Python实现流程
2.2.1 准备工作
1.新建功能包
ros2 pkg create py05_exercise --build-type ament_python --dependencies rclpy tf_transformations tf2_ros geometry_msgs turtlesim
2.创建launch目录:
功能包py05_exercise下新建launch文件并编辑配置文件。
功能包py05_exercise的 setup.py 文件中需要修改 data_files 属性修改后的内容如下
data_files[(share/ament_index/resource_index/packages,[resource/ package_name]),(share/ package_name, [package.xml],),(share/ package_name, glob(launch/*.launch.xml)),(share/ package_name, glob(launch/*.launch.py)),(share/ package_name, glob(launch/*.launch.yaml)),
],
2.2.2 编写生成新乌龟 功能包 py05_exercise 的 py05_exercise 目录下新建 Python 文件 exer01_tf_spawn_py.py并编辑文件输入如下内容 需求编写客户端发送请求生成一只新的乌龟。步骤1.导包2.初始化 ROS2 客户端3.定义节点类3-1.声明并获取参数3-2.创建客户端3-3.等待服务连接3-4.组织请求数据并发送4.创建对象调用其功能,并处理响应5.释放资源。 # 1.导包
import rclpy
from rclpy.node import Node
from turtlesim.srv import Spawn# 3.定义节点类
class TurtleSpawnClient(Node):def __init__(self):super().__init__(turtle_spawn_client_py)# 3-1.声明并获取参数self.x self.declare_parameter(x,2.0)self.y self.declare_parameter(y,2.0)self.theta self.declare_parameter(theta,0.0)self.turtle_name self.declare_parameter(turtle_name,turtle2)# 3-2.创建客户端self.cli self.create_client(Spawn, /spawn)# 3-3.等待服务连接while not self.cli.wait_for_service(timeout_sec1.0):self.get_logger().info(服务连接中请稍候...)self.req Spawn.Request()# 3-4.组织请求数据并发送def send_request(self):self.req.x self.get_parameter(x).get_parameter_value().double_valueself.req.y self.get_parameter(y).get_parameter_value().double_valueself.req.theta self.get_parameter(theta).get_parameter_value().double_valueself.req.name self.get_parameter(turtle_name).get_parameter_value().string_valueself.future self.cli.call_async(self.req)def main():# 2.初始化 ROS2 客户端rclpy.init()# 4.创建对象并调用其功能client TurtleSpawnClient()client.send_request()# 处理响应rclpy.spin_until_future_complete(client,client.future)try:response client.future.result()except Exception as e:client.get_logger().info(服务请求失败 %r % e)else:if len(response.name) 0:client.get_logger().info(乌龟重名了创建失败)else:client.get_logger().info(乌龟%s被创建 % response.name)# 5.释放资源。rclpy.shutdown()if __name__ __main__:main()
2.2.3 编写坐标变换广播 功能包 py05_exercise 的 py05_exercise 目录下新建 Python 文件 exer02_tf_broadcaster_py.py并编辑文件输入如下内容 需求发布乌龟坐标系到窗口坐标系的坐标变换。步骤1.导包2.初始化 ROS2 客户端3.定义节点类3-1.声明并解析乌龟名称参数3-2.创建动态坐标变换发布方3-3.创建乌龟位姿订阅方3-4.根据订阅到的乌龟位姿生成坐标帧并广播。4.调用 spin 函数并传入对象5.释放资源。
# 1.导包
from geometry_msgs.msg import TransformStamped
import rclpy
from rclpy.node import Node
from tf2_ros import TransformBroadcaster
import tf_transformations
from turtlesim.msg import Pose# 3.定义节点类
class TurtleFrameBroadcaster(Node):def __init__(self):super().__init__(turtle_frame_broadcaster_py)# 3-1.声明并解析乌龟名称参数self.declare_parameter(turtle_name, turtle1)self.turtlename self.get_parameter(turtle_name).get_parameter_value().string_value# 3-2.创建动态坐标变换发布方self.br TransformBroadcaster(self)# 3-3.创建乌龟位姿订阅方self.subscription self.create_subscription(Pose,self.turtlename /pose,self.handle_turtle_pose,1)self.subscriptiondef handle_turtle_pose(self, msg):# 3-4.根据订阅到的乌龟位姿生成坐标帧并广播。t TransformStamped()t.header.stamp self.get_clock().now().to_msg()t.header.frame_id worldt.child_frame_id self.turtlenamet.transform.translation.x msg.xt.transform.translation.y msg.yt.transform.translation.z 0.0q tf_transformations.quaternion_from_euler(0, 0, msg.theta)t.transform.rotation.x q[0]t.transform.rotation.y q[1]t.transform.rotation.z q[2]t.transform.rotation.w q[3]self.br.sendTransform(t)def main():# 2.初始化 ROS2 客户端rclpy.init()# 4.调用 spin 函数并传入对象node TurtleFrameBroadcaster()try:rclpy.spin(node)except KeyboardInterrupt:pass# 5.释放资源。rclpy.shutdown()
2.2.4 编写坐标变换监听 功能包 py05_exercise 的 py05_exercise 目录下新建 Python 文件 exer03_tf_listener_py.py并编辑文件输入如下内容 需求广播的坐标系消息并生成 turtle2 相对于 turtle1 的坐标系数据并进一步生成控制 turtle2 运动的速度指令。步骤1.导包2.初始化 ROS2 客户端3.定义节点类3-1.声明并解析参数3-2.创建tf缓存对象指针3-3.创建tf监听器3-4.按照条件查找符合条件的坐标系并生成变换后的坐标帧。4.调用 spin 函数并传入对象5.释放资源。
# 1.导包
import math
from geometry_msgs.msg import Twist
import rclpy
from rclpy.node import Node
from tf2_ros import TransformException
from tf2_ros.buffer import Buffer
from tf2_ros.transform_listener import TransformListener# 3.定义节点类
class TurtleFrameListener(Node):def __init__(self):super().__init__(turtle_frame_listener_py)# 3-1.声明并解析参数self.declare_parameter(target_frame, turtle2)self.declare_parameter(source_frame, turtle1)self.target_frame self.get_parameter(target_frame).get_parameter_value().string_valueself.source_frame self.get_parameter(source_frame).get_parameter_value().string_value# 3-2.创建tf缓存对象指针self.tf_buffer Buffer()# 3-3.创建tf监听器self.tf_listener TransformListener(self.tf_buffer, self)self.publisher self.create_publisher(Twist, self.target_frame /cmd_vel, 1)self.timer self.create_timer(1.0, self.on_timer)def on_timer(self):# 3-4.按照条件查找符合条件的坐标系并生成变换后的坐标帧。try:now rclpy.time.Time()trans self.tf_buffer.lookup_transform(self.target_frame,self.source_frame,now)except TransformException as ex:self.get_logger().info(%s 到 %s 坐标变换异常 % (self.source_frame,self.target_frame))return# 3-5.生成 turtle2 的速度指令并发布。msg Twist()scale_rotation_rate 1.0msg.angular.z scale_rotation_rate * math.atan2(trans.transform.translation.y,trans.transform.translation.x)scale_forward_speed 0.5msg.linear.x scale_forward_speed * math.sqrt(trans.transform.translation.x ** 2 trans.transform.translation.y ** 2)self.publisher.publish(msg)def main():# 2.初始化 ROS2 客户端rclpy.init()# 4.调用 spin 函数并传入对象node TurtleFrameListener()try:rclpy.spin(node)except KeyboardInterrupt:pass# 5.释放资源。rclpy.shutdown()
2.2.5 编写 launch 文件 launch 目录下新建文件 exer01_turtle_follow.launch.xml并编辑文件输入如下内容
launch!-- 乌龟准备 --node pkgturtlesim execturtlesim_node namet1 /node pkgpy05_exercise execexer01_tf_spawn_py namet2 /!-- 发布坐标变换 --node pkgpy05_exercise execexer02_tf_broadcaster_py nametf_broadcaster1_py/nodenode pkgpy05_exercise execexer02_tf_broadcaster_py nametf_broadcaster2_pyparam nameturtle_name valueturtle2 //node!-- 监听坐标变换 --node pkgpy05_exercise execexer03_tf_listener_py nametf_listener_pyparam nametarget_frame valueturtle2 /param namesource_frame valueturtle1 //node
/launch
2.2.6 编辑配置文件
1.package.xml
在创建功能包时所依赖的功能包已经自动配置了配置内容如下
dependrclpy/depend
dependtf_transformations/depend
dependtf2_ros/depend
dependgeometry_msgs/depend
dependturtlesim/depend2.setup.py
entry_points字段的console_scripts中添加如下内容
entry_points{console_scripts: [exer01_tf_spawn_py py05_exercise.exer01_tf_spawn_py:main,exer02_tf_broadcaster_py py05_exercise.exer02_tf_broadcaster_py:main,exer03_tf_listener_py py05_exercise.exer03_tf_listener_py:main],
},2.2.7 编译执行
终端中进入当前工作空间编译功能包
colcon build --packages-select py05_exercise当前工作空间下启动终端输入如下命令运行launch文件
. install/setup.bash
ros2 launch py05_exercise exer01_turtle_follow.launch.xml再新建终端启动 turtlesim 键盘控制节点
ros2 run turtlesim turtle_teleop_key该终端下可以通过键盘控制 turtle1 运动并且 turtle2 会跟随 turtle1 运动。最终的运行结果与C实现类似。 文章转载自: http://www.morning.ldfcb.cn.gov.cn.ldfcb.cn http://www.morning.pbbzn.cn.gov.cn.pbbzn.cn http://www.morning.ypbp.cn.gov.cn.ypbp.cn http://www.morning.sryyt.cn.gov.cn.sryyt.cn http://www.morning.mbmh.cn.gov.cn.mbmh.cn http://www.morning.dmxzd.cn.gov.cn.dmxzd.cn http://www.morning.hnpkr.cn.gov.cn.hnpkr.cn http://www.morning.bkylg.cn.gov.cn.bkylg.cn http://www.morning.dyzbt.cn.gov.cn.dyzbt.cn http://www.morning.zrrgx.cn.gov.cn.zrrgx.cn http://www.morning.ngcbd.cn.gov.cn.ngcbd.cn http://www.morning.irqlul.cn.gov.cn.irqlul.cn http://www.morning.nywrm.cn.gov.cn.nywrm.cn http://www.morning.tymnr.cn.gov.cn.tymnr.cn http://www.morning.mfzyn.cn.gov.cn.mfzyn.cn http://www.morning.wqmyh.cn.gov.cn.wqmyh.cn http://www.morning.xqjrg.cn.gov.cn.xqjrg.cn http://www.morning.wjzzh.cn.gov.cn.wjzzh.cn http://www.morning.gstmn.cn.gov.cn.gstmn.cn http://www.morning.nuobeiergw.cn.gov.cn.nuobeiergw.cn http://www.morning.zwhtr.cn.gov.cn.zwhtr.cn http://www.morning.mumgou.com.gov.cn.mumgou.com http://www.morning.ctsjq.cn.gov.cn.ctsjq.cn http://www.morning.zcwwb.cn.gov.cn.zcwwb.cn http://www.morning.pycpt.cn.gov.cn.pycpt.cn http://www.morning.ljcjc.cn.gov.cn.ljcjc.cn http://www.morning.wnhml.cn.gov.cn.wnhml.cn http://www.morning.bztzm.cn.gov.cn.bztzm.cn http://www.morning.xqcst.cn.gov.cn.xqcst.cn http://www.morning.xpzgg.cn.gov.cn.xpzgg.cn http://www.morning.dbrpl.cn.gov.cn.dbrpl.cn http://www.morning.kdjtt.cn.gov.cn.kdjtt.cn http://www.morning.rmqlf.cn.gov.cn.rmqlf.cn http://www.morning.gwjnm.cn.gov.cn.gwjnm.cn http://www.morning.gfqj.cn.gov.cn.gfqj.cn http://www.morning.rdymd.cn.gov.cn.rdymd.cn http://www.morning.zzaxr.cn.gov.cn.zzaxr.cn http://www.morning.fssjw.cn.gov.cn.fssjw.cn http://www.morning.ktlfb.cn.gov.cn.ktlfb.cn http://www.morning.kjgdm.cn.gov.cn.kjgdm.cn http://www.morning.cmcjp.cn.gov.cn.cmcjp.cn http://www.morning.pwsnr.cn.gov.cn.pwsnr.cn http://www.morning.zwppm.cn.gov.cn.zwppm.cn http://www.morning.bykqg.cn.gov.cn.bykqg.cn http://www.morning.rxydr.cn.gov.cn.rxydr.cn http://www.morning.sogou66.cn.gov.cn.sogou66.cn http://www.morning.dqpd.cn.gov.cn.dqpd.cn http://www.morning.mfsxd.cn.gov.cn.mfsxd.cn http://www.morning.kncrc.cn.gov.cn.kncrc.cn http://www.morning.zpdjh.cn.gov.cn.zpdjh.cn http://www.morning.jpzcq.cn.gov.cn.jpzcq.cn http://www.morning.hphrz.cn.gov.cn.hphrz.cn http://www.morning.mgtrc.cn.gov.cn.mgtrc.cn http://www.morning.kpcdc.cn.gov.cn.kpcdc.cn http://www.morning.rbknf.cn.gov.cn.rbknf.cn http://www.morning.krjyq.cn.gov.cn.krjyq.cn http://www.morning.qlwfz.cn.gov.cn.qlwfz.cn http://www.morning.pfggj.cn.gov.cn.pfggj.cn http://www.morning.gnwse.com.gov.cn.gnwse.com http://www.morning.ktbjk.cn.gov.cn.ktbjk.cn http://www.morning.nqmwk.cn.gov.cn.nqmwk.cn http://www.morning.lcbt.cn.gov.cn.lcbt.cn http://www.morning.ssjry.cn.gov.cn.ssjry.cn http://www.morning.ljjmr.cn.gov.cn.ljjmr.cn http://www.morning.qxnlc.cn.gov.cn.qxnlc.cn http://www.morning.gpsr.cn.gov.cn.gpsr.cn http://www.morning.rywn.cn.gov.cn.rywn.cn http://www.morning.tfwg.cn.gov.cn.tfwg.cn http://www.morning.tjqcfw.cn.gov.cn.tjqcfw.cn http://www.morning.yrpd.cn.gov.cn.yrpd.cn http://www.morning.dydqh.cn.gov.cn.dydqh.cn http://www.morning.rwzqn.cn.gov.cn.rwzqn.cn http://www.morning.rgsnk.cn.gov.cn.rgsnk.cn http://www.morning.rnnq.cn.gov.cn.rnnq.cn http://www.morning.hhpbj.cn.gov.cn.hhpbj.cn http://www.morning.mmkrd.cn.gov.cn.mmkrd.cn http://www.morning.rfkyb.cn.gov.cn.rfkyb.cn http://www.morning.wclxm.cn.gov.cn.wclxm.cn http://www.morning.gycyt.cn.gov.cn.gycyt.cn http://www.morning.mdwlg.cn.gov.cn.mdwlg.cn