Open Feng-DDXY opened 1 week ago
使用版本:Ubuntu 16.04 ROS Kinetic(已安装)
以下步骤自行完成(仅附终端命令) 1.在已有工作空间下创建功能包ch10_tutorials1 2.导入std_msgs和roscpp作为该功能包的依赖项
catkin_create_pkg ch10_tutorials1 std_msgs roscpp
本文使用TF功能包实现乌龟坐标广播以及跟随,故需要加入TF相关依赖 在功能包内的CMakelists.txt 编写以下语句:(建议ctrl+f搜索)
find_package(catkin REQUIRED COMPONENTS roscpp std_msgs message_generation tf2_ros )
catkin_package( CATKIN_DEPENDS roscpp rospy std_msgs message_runtime tf2_ros )
add_executable(turtle3 src/turtle3.cpp) target_link_libraries(turtle3 ${catkin_LIBRARIES})
其中turtle3是你的cpp文件名,如果以其他的命名需要修改(我这里用的turtle4)
在功能包内的package.xml 创建以下语句:
<depend>tf2_ros</depend>
在\ch10_tutorials1\src下新建turtle3.cpp文件,并编写以下代码:
#include "ros/ros.h" #include "turtlesim/Pose.h" #include "turtlesim/Spawn.h" #include "tf2_ros/transform_broadcaster.h" #include "tf2_ros/transform_listener.h" #include "geometry_msgs/TransformStamped.h" #include "geometry_msgs/Twist.h" #include "tf2/LinearMath/Quaternion.h" #include <cmath> std::string turtle_name = "turtle1"; //代表要跟随乌龟的名字 void doPose(const turtlesim::Pose::ConstPtr& pose, const std::string& turtle_name) { static tf2_ros::TransformBroadcaster broadcaster; geometry_msgs::TransformStamped tfs; tfs.header.frame_id = "world"; tfs.header.stamp = ros::Time::now(); tfs.child_frame_id = turtle_name; //设定2只乌龟发布tf坐标的位置 if (turtle_name == "turtle1") { tfs.transform.translation.x = (pose->x) + 1.0; tfs.transform.translation.y = (pose->y) + 1.0; } else if (turtle_name == "turtle2") { tfs.transform.translation.x = pose->x; tfs.transform.translation.y = pose->y; } tfs.transform.translation.z = 0.0; tf2::Quaternion qtn; qtn.setRPY(0, 0, pose->theta); tfs.transform.rotation.x = qtn.getX(); tfs.transform.rotation.y = qtn.getY(); tfs.transform.rotation.z = qtn.getZ(); tfs.transform.rotation.w = qtn.getW(); broadcaster.sendTransform(tfs); } int main(int argc, char *argv[]) { setlocale(LC_ALL, ""); ros::init(argc, argv, "pub_sub_tf"); ros::NodeHandle nh; ros::ServiceClient spawn_client = nh.serviceClient<turtlesim::Spawn>("/spawn"); //新建2只乌龟 turtlesim::Spawn spawn_msg; spawn_msg.request.x = 5.0; spawn_msg.request.y = 5.0; spawn_msg.request.theta = 0.0; spawn_msg.request.name = "turtle1"; if (spawn_client.call(spawn_msg)) { ROS_INFO("Spawned turtle1 at (5, 5)"); } else { ROS_ERROR("Failed to spawn turtle1"); return -1; } spawn_msg.request.x = 8.0; spawn_msg.request.y = 5.0; spawn_msg.request.theta = 0.0; spawn_msg.request.name = "turtle2"; if (spawn_client.call(spawn_msg)) { ROS_INFO("Spawned turtle2 at (8, 5)"); } else { ROS_ERROR("Failed to spawn turtle2"); return -1; } ros::Publisher pub = nh.advertise<geometry_msgs::Twist>("/turtle2/cmd_vel", 1000); tf2_ros::Buffer buffer; tf2_ros::TransformListener listener(buffer); //发布turtle1和turtle2的坐标(使用doPose函数) ros::Subscriber sub1 = nh.subscribe<turtlesim::Pose>("turtle1/pose", 1000, boost::bind(doPose, _1, "turtle1")); ros::Subscriber sub2 = nh.subscribe<turtlesim::Pose>("turtle2/pose", 1000, boost::bind(doPose, _1, "turtle2")); //ros::Duration(2.0).sleep(); //可以加个延迟,但是还是会有WARN(从3个变成1个) ros::Rate rate(10); while (ros::ok()) { try { // 获取 turtle1 相对 turtle2 的坐标信息 geometry_msgs::TransformStamped tfs = buffer.lookupTransform("turtle2", "turtle1", ros::Time(0)); // 根据坐标信息生成速度信息 geometry_msgs::Twist twist; twist.linear.x = 0.5 * sqrt(pow(tfs.transform.translation.x, 2) + pow(tfs.transform.translation.y, 2)); twist.angular.z = 4 * atan2(tfs.transform.translation.y, tfs.transform.translation.x); pub.publish(twist); } catch (const tf2::TransformException& e) { ROS_WARN("错误提示:%s", e.what()); } rate.sleep(); ros::spinOnce(); } return 0; }
切换到工作空间下编译工作空间:
cd ~/catkin_ws catkin_make
使用方法:
分别在4个终端输入以下指令:
roscore rosrun turtlesim turtlesim_node rosservice call /kill "turtle1" rosrun ch10_tutorials1 turtle4
rosservice call /kill "turtle1"是因为代码里没有写kill小乌龟代码,手动在终端里去掉,不然rosrun turtlesim turtlesim_node会自动生成一只turtle1乌龟,导致重名报错
此时窗口里会出现2个小海龟,打开另一个终端输入以下指令控制其中一只运动
rosrun turtlesim turtle_teleop_key
即可看到一个乌龟跟随另一个了 我的代码里写的是跟随的(pose->x) + 1.0;即一直跟随在相对位置右上角),如果只是跟随可以修改为(pose->x)
或者将乌龟2发布的改成(pose->x) - 1.0 同样跟随在右上角效果
这里提供2份写法,第一种是按照上面的相对位置,纯写死偏移的,只需要修改文件内容,将2只扩展成4只即可:
(写的很烂,属于拿来应付截图效果)
#include "ros/ros.h" #include "turtlesim/Pose.h" #include "turtlesim/Spawn.h" #include "tf2_ros/transform_broadcaster.h" #include "tf2_ros/transform_listener.h" #include "geometry_msgs/TransformStamped.h" #include "geometry_msgs/Twist.h" #include "tf2/LinearMath/Quaternion.h" #include <cmath> std::string turtle_name = "turtle1"; void doPose(const turtlesim::Pose::ConstPtr& pose, const std::string& turtle_name) { static tf2_ros::TransformBroadcaster broadcaster; geometry_msgs::TransformStamped tfs; tfs.header.frame_id = "world"; tfs.header.stamp = ros::Time::now(); tfs.child_frame_id = turtle_name; //通过名字修改相对偏移量 if (turtle_name == "turtle1") { tfs.transform.translation.x = pose->x; tfs.transform.translation.y = pose->y; } else if (turtle_name == "turtle2") { tfs.transform.translation.x = (pose->x)-1.0; tfs.transform.translation.y = (pose->y)-1.0; } else if (turtle_name == "turtle3") { tfs.transform.translation.x = (pose->x)-1.0; tfs.transform.translation.y = (pose->y)+1.0; } else if (turtle_name == "turtle4") { tfs.transform.translation.x = (pose->x)+1.0; tfs.transform.translation.y = (pose->y)-1.0; } tfs.transform.translation.z = 0.0; tf2::Quaternion qtn; qtn.setRPY(0, 0, pose->theta); tfs.transform.rotation.x = qtn.getX(); tfs.transform.rotation.y = qtn.getY(); tfs.transform.rotation.z = qtn.getZ(); tfs.transform.rotation.w = qtn.getW(); broadcaster.sendTransform(tfs); } int main(int argc, char *argv[]) { setlocale(LC_ALL, ""); ros::init(argc, argv, "pub_sub_tf"); ros::NodeHandle nh; ros::ServiceClient spawn_client = nh.serviceClient<turtlesim::Spawn>("/spawn"); turtlesim::Spawn spawn_msg; spawn_msg.request.x = 5.0; spawn_msg.request.y = 5.0; spawn_msg.request.theta = 0.0; spawn_msg.request.name = "turtle1"; if (spawn_client.call(spawn_msg)) { ROS_INFO("Spawned turtle1 at (5, 5)"); } else { ROS_ERROR("Failed to spawn turtle1"); return -1; } spawn_msg.request.x = 8.0; spawn_msg.request.y = 5.0; spawn_msg.request.theta = 0.0; spawn_msg.request.name = "turtle2"; spawn_client.call(spawn_msg); spawn_msg.request.name = "turtle3"; spawn_client.call(spawn_msg); spawn_msg.request.name = "turtle4"; spawn_client.call(spawn_msg); ros::Duration(2.0).sleep(); //将一个改成3个 ros::Publisher pub = nh.advertise<geometry_msgs::Twist>("/turtle2/cmd_vel", 1000); ros::Publisher pub3 = nh.advertise<geometry_msgs::Twist>("/turtle3/cmd_vel", 1000); ros::Publisher pub4 = nh.advertise<geometry_msgs::Twist>("/turtle4/cmd_vel", 1000); tf2_ros::Buffer buffer; tf2_ros::TransformListener listener(buffer); //4个位置都要发布 ros::Subscriber sub1 = nh.subscribe<turtlesim::Pose>("turtle1/pose", 1000, boost::bind(doPose, _1, "turtle1")); ros::Subscriber sub2 = nh.subscribe<turtlesim::Pose>("turtle2/pose", 1000, boost::bind(doPose, _1, "turtle2")); ros::Subscriber sub3 = nh.subscribe<turtlesim::Pose>("turtle3/pose", 1000, boost::bind(doPose, _1, "turtle3")); ros::Subscriber sub4 = nh.subscribe<turtlesim::Pose>("turtle4/pose", 1000, boost::bind(doPose, _1, "turtle4")); ros::Rate rate(10); while (ros::ok()) { try { geometry_msgs::TransformStamped tfs = buffer.lookupTransform("turtle2", "turtle1", ros::Time(0)); geometry_msgs::Twist twist; twist.linear.x = 0.5 * sqrt(pow(tfs.transform.translation.x, 2) + pow(tfs.transform.translation.y, 2)); twist.angular.z = 4 * atan2(tfs.transform.translation.y, tfs.transform.translation.x); pub.publish(twist); geometry_msgs::TransformStamped tfs2 = buffer.lookupTransform("turtle3", "turtle1", ros::Time(0)); twist.linear.x = 0.5 * sqrt(pow(tfs2.transform.translation.x, 2) + pow(tfs2.transform.translation.y, 2)); twist.angular.z = 4 * atan2(tfs2.transform.translation.y, tfs2.transform.translation.x); pub3.publish(twist); geometry_msgs::TransformStamped tfs3 = buffer.lookupTransform("turtle4", "turtle1", ros::Time(0)); twist.linear.x = 0.5 * sqrt(pow(tfs3.transform.translation.x, 2) + pow(tfs3.transform.translation.y, 2)); twist.angular.z = 4 * atan2(tfs3.transform.translation.y, tfs3.transform.translation.x); pub4.publish(twist); } catch (const tf2::TransformException& e) { ROS_WARN("错误提示:%s", e.what()); } rate.sleep(); ros::spinOnce(); } return 0; }
这个就不细讲了,属于是纯纯“护”在上下左右,运行方法也和2只的一样(记得kill乌龟1)
乌龟朝左截图效果最好,如果需要换队形自己修改一下参数
世界坐标系左下角是(0,0) 对乌龟2进行x和y都减1是相对乌龟1来说的右上角(即世界坐标系+1) x减1是往右,y减1是往上 右下角是乌龟3 左上角是乌龟4
世界坐标系左下角是(0,0) 对乌龟2进行x和y都减1是相对乌龟1来说的右上角(即世界坐标系+1)
x减1是往右,y减1是往上
右下角是乌龟3 左上角是乌龟4
例如正方形编队:
也是只需要修改代码:
通过修改tfs 中的坐标改变坐标变换本身的结果进行偏移,运动会更加丝滑(后续所有基于 tfs 的计算都会基于新的坐标偏移)
tfs
#include "ros/ros.h" #include "turtlesim/Pose.h" #include "turtlesim/Spawn.h" #include "tf2_ros/transform_broadcaster.h" #include "tf2_ros/transform_listener.h" #include "geometry_msgs/TransformStamped.h" #include "geometry_msgs/Twist.h" #include "tf2/LinearMath/Quaternion.h" #include <cmath> std::string turtle_name = "turtle1"; void doPose(const turtlesim::Pose::ConstPtr& pose, const std::string& turtle_name) { static tf2_ros::TransformBroadcaster broadcaster; geometry_msgs::TransformStamped tfs; tfs.header.frame_id = "world"; tfs.header.stamp = ros::Time::now(); tfs.child_frame_id = turtle_name; //取消了之前的偏移量 tfs.transform.translation.x = pose->x; tfs.transform.translation.y = pose->y; tfs.transform.translation.z = 0.0; tf2::Quaternion qtn; qtn.setRPY(0, 0, pose->theta); tfs.transform.rotation.x = qtn.getX(); tfs.transform.rotation.y = qtn.getY(); tfs.transform.rotation.z = qtn.getZ(); tfs.transform.rotation.w = qtn.getW(); broadcaster.sendTransform(tfs); } int main(int argc, char *argv[]) { setlocale(LC_ALL, ""); ros::init(argc, argv, "pub_sub_tf"); ros::NodeHandle nh; ros::ServiceClient spawn_client = nh.serviceClient<turtlesim::Spawn>("/spawn"); turtlesim::Spawn spawn_msg; spawn_msg.request.x = 5.0; spawn_msg.request.y = 5.0; spawn_msg.request.theta = 0.0; spawn_msg.request.name = "turtle1"; if (!spawn_client.call(spawn_msg)) { return -1; } spawn_msg.request.x = 8.0; spawn_msg.request.y = 5.0; spawn_msg.request.theta = 0.0; spawn_msg.request.name = "turtle2"; spawn_client.call(spawn_msg); spawn_msg.request.name = "turtle3"; spawn_client.call(spawn_msg); spawn_msg.request.name = "turtle4"; spawn_client.call(spawn_msg); ros::Duration(2.0).sleep(); ros::Publisher pub = nh.advertise<geometry_msgs::Twist>("/turtle2/cmd_vel", 1000); ros::Publisher pub3 = nh.advertise<geometry_msgs::Twist>("/turtle3/cmd_vel", 1000); ros::Publisher pub4 = nh.advertise<geometry_msgs::Twist>("/turtle4/cmd_vel", 1000); tf2_ros::Buffer buffer; tf2_ros::TransformListener listener(buffer); ros::Subscriber sub1 = nh.subscribe<turtlesim::Pose>("turtle1/pose", 1000, boost::bind(doPose, _1, "turtle1")); ros::Subscriber sub2 = nh.subscribe<turtlesim::Pose>("turtle2/pose", 1000, boost::bind(doPose, _1, "turtle2")); ros::Subscriber sub3 = nh.subscribe<turtlesim::Pose>("turtle3/pose", 1000, boost::bind(doPose, _1, "turtle3")); ros::Subscriber sub4 = nh.subscribe<turtlesim::Pose>("turtle4/pose", 1000, boost::bind(doPose, _1, "turtle4")); ros::Rate rate(10); while (ros::ok()) { try { geometry_msgs::TransformStamped tfs = buffer.lookupTransform("turtle2", "turtle1", ros::Time(0)); //通过对tf坐标变化进行偏移 tfs.transform.translation.x -= 1.5; geometry_msgs::Twist twist; twist.linear.x = 0.5 * sqrt(pow(tfs.transform.translation.x, 2) + pow(tfs.transform.translation.y, 2)); twist.angular.z = 4 * atan2(tfs.transform.translation.y, tfs.transform.translation.x); //当距离较近时(通过速度判断)停止运动 if (twist.linear.x < 0.3) { twist.linear.x = 0.0; twist.angular.z = 0.0; } pub.publish(twist); //第二只 geometry_msgs::TransformStamped tfs2 = buffer.lookupTransform("turtle3", "turtle1", ros::Time(0)); tfs2.transform.translation.y -= 1.5; twist.linear.x = 0.5 * sqrt(pow(tfs2.transform.translation.x, 2) + pow(tfs2.transform.translation.y, 2)); twist.angular.z = 4 * atan2(tfs2.transform.translation.y, tfs2.transform.translation.x); if (twist.linear.x < 0.3) { twist.linear.x = 0.0; twist.angular.z = 0.0; } pub3.publish(twist); geometry_msgs::TransformStamped tfs3 = buffer.lookupTransform("turtle4", "turtle1", ros::Time(0)); tfs3.transform.translation.x -= 1.5; tfs3.transform.translation.y -= 1.5; twist.linear.x = 0.5 * sqrt(pow(tfs3.transform.translation.x, 2) + pow(tfs3.transform.translation.y, 2)); twist.angular.z = 4 * atan2(tfs3.transform.translation.y, tfs3.transform.translation.x); if (twist.linear.x < 0.3) { twist.linear.x = 0.0; twist.angular.z = 0.0; } pub4.publish(twist); } catch (const tf2::TransformException& e) { } rate.sleep(); ros::spinOnce(); } return 0; }
总的逻辑差不多,也不详细解释了,但是这种我没有仔细研究停止速度阈值和相对位置关系,有兴趣的可以自己改改
效果如下:
我懒得改文件名了,继续使用turtle4.cpp文件
主要逻辑是写了2个函数:
moveInCircle:对pub发布运动指令,使其朝左进行圆周运动(画圈) 正数是朝左 moveInLine:对pub发布运动指令,使其朝左进行旋转后走指定长度(直线) 正数是朝左
moveInCircle:对pub发布运动指令,使其朝左进行圆周运动(画圈) 正数是朝左
moveInLine:对pub发布运动指令,使其朝左进行旋转后走指定长度(直线) 正数是朝左
一曲一直不就妙了吗,可以实现理想形状,这里实现的是乌龟1走数字2,乌龟2走数字5,如果需要其他形状自行修改
但是我不知道为什么计算的很抽象,实际上的具体数值需要调,和理想输入的不一样,主要是moveInLine函数,两个值都不准,甚至同样的代码重复运行效果都不同
为了配合代码还可以设置乌龟的初始生成位置和角度,代码里有注释,这里不做多描述
#include "ros/ros.h" #include "turtlesim/Spawn.h" #include <turtlesim/Kill.h> #include "geometry_msgs/Twist.h" #include "turtlesim/Pose.h" #include <cmath> double rate_hz =50; double speed = 1.0; int customRound(double value) { return (value >= 0) ? (int)(value + 0.5) : (int)(value - 0.5); } double toRadians(double degrees) { return degrees * M_PI / 180.0; } void moveInCircle(ros::Publisher &pub, double radius, double angle_degrees) { ros::Rate rate(rate_hz); double angle = toRadians(angle_degrees); // 角度转弧度 double angular_speed = speed / radius; int steps = customRound((angle / (2 * M_PI)) * radius * 2 * M_PI * rate_hz / speed); geometry_msgs::Twist twist; twist.linear.x = speed; twist.angular.z = angular_speed; for (int i = 0; i < steps; ++i) { pub.publish(twist); rate.sleep(); } twist.linear.x = 0; twist.angular.z = 0; pub.publish(twist); } void moveInLine(ros::Publisher &pub, double distance, double angle_degrees) { ros::Rate rate(rate_hz); int steps = customRound(distance / (speed / rate_hz)); double angle = toRadians(angle_degrees); // 角度转弧度 geometry_msgs::Twist twist; twist.linear.x = 0; // 保持线速度为0,只有旋转 twist.angular.z = angle; // 设置角速度(正值表示逆时针) // 旋转直到达到目标角度 for (int i = 0; i < 51; ++i) { pub.publish(twist); rate.sleep(); } twist.linear.x = speed; twist.angular.z = 0; for (int i = 0; i < steps; ++i) { pub.publish(twist); rate.sleep(); } twist.linear.x = 0; pub.publish(twist); } int main(int argc, char *argv[]) { setlocale(LC_ALL, ""); ros::init(argc, argv, "pub_sub_tf"); ros::NodeHandle nh; ros::ServiceClient spawn_client = nh.serviceClient<turtlesim::Spawn>("/spawn"); turtlesim::Spawn spawn_msg; //删除第一只乌龟 ros::ServiceClient client_kill = nh.serviceClient <turtlesim::Kill>("kill"); turtlesim::Kill kill_name; kill_name.request.name ="turtle1"; client_kill.call(kill_name); // 启动 turtle1 spawn_msg.request.x = 4.0; // turtle1 起始位置 越小越靠左 spawn_msg.request.y = 5.0; // turtle1 起始位置 越小越靠下 spawn_msg.request.theta = M_PI; //0朝右 M_PI/2朝上 -M_PI/2朝下 M_PI朝左 spawn_msg.request.name = "turtle1"; if (spawn_client.call(spawn_msg)) { //ROS_INFO("Spawned turtle1 at (4, 5)"); } else { ROS_ERROR("Failed to spawn turtle1"); return -1; } // 启动 turtle2 spawn_msg.request.x = 5.0; // turtle2 起始位置 spawn_msg.request.y = 4.9; spawn_msg.request.theta = -M_PI / 18; spawn_msg.request.name = "turtle2"; if (spawn_client.call(spawn_msg)) { //ROS_INFO("Spawned turtle2 at (8, 5)"); } else { ROS_ERROR("Failed to spawn turtle2"); return -1; } // 创建发布者对象,pub1是对乌龟1 ros::Publisher pub1 = nh.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 1000); ros::Publisher pub2 = nh.advertise<geometry_msgs::Twist>("/turtle2/cmd_vel", 1000); ros::Rate rate(10); // 设置循环频率为10Hz if (ros::ok()) { //pub1为乌龟1 //moveInLine(pub1, 2.0,240); // 直线长度为 2 往左转180度左右(实际输入240 有很大偏差) moveInLine(pub1, 2.0,0); // 直线长度为 2 往右转0度(配合前面theta朝向,不进行旋转) moveInLine(pub1, 1.0,-160); // 直线长度为 1 往右转135度 moveInCircle(pub1, 1, 190); // 半径为 1.0 190度(比较精准) moveInCircle(pub2, 0.8, 200); // 半径为 0.8 往左转200度(实际略有偏差) moveInLine(pub2, 0.9,-100); // 直线长度为 1 往右转90度左右(实际输入为100) moveInLine(pub2, 1,-92); rate.sleep(); ros::spinOnce(); } return 0; }
运行:
分别在3个终端输入以下指令:这里面加入了删除乌龟1的代码,所以省了一行
roscore rosrun turtlesim turtlesim_node rosrun ch10_tutorials1 turtle4
效果如下:(分开截的)
hello
使用版本:Ubuntu 16.04 ROS Kinetic(已安装)
两只小乌龟跟随运动(单文件实现)
本文使用TF功能包实现乌龟坐标广播以及跟随,故需要加入TF相关依赖 在功能包内的CMakelists.txt 编写以下语句:(建议ctrl+f搜索)
其中turtle3是你的cpp文件名,如果以其他的命名需要修改(我这里用的turtle4)
在功能包内的package.xml 创建以下语句:
在\ch10_tutorials1\src下新建turtle3.cpp文件,并编写以下代码:
切换到工作空间下编译工作空间:
使用方法:
分别在4个终端输入以下指令:
rosservice call /kill "turtle1"是因为代码里没有写kill小乌龟代码,手动在终端里去掉,不然rosrun turtlesim turtlesim_node会自动生成一只turtle1乌龟,导致重名报错
此时窗口里会出现2个小海龟,打开另一个终端输入以下指令控制其中一只运动
即可看到一个乌龟跟随另一个了 我的代码里写的是跟随的(pose->x) + 1.0;即一直跟随在相对位置右上角),如果只是跟随可以修改为(pose->x)
或者将乌龟2发布的改成(pose->x) - 1.0 同样跟随在右上角效果
四只小乌龟的护航跟随(单文件实现)
这里提供2份写法,第一种是按照上面的相对位置,纯写死偏移的,只需要修改文件内容,将2只扩展成4只即可:
写死版四只乌龟护航
(写的很烂,属于拿来应付截图效果)
这个就不细讲了,属于是纯纯“护”在上下左右,运行方法也和2只的一样(记得kill乌龟1)
乌龟朝左截图效果最好,如果需要换队形自己修改一下参数例如正方形编队:
通过偏移量进行四只乌龟护航(稍微动态)
也是只需要修改代码:
通过修改
tfs
中的坐标改变坐标变换本身的结果进行偏移,运动会更加丝滑(后续所有基于tfs
的计算都会基于新的坐标偏移)总的逻辑差不多,也不详细解释了,但是这种我没有仔细研究停止速度阈值和相对位置关系,有兴趣的可以自己改改
效果如下:
小乌龟的路径规划
我懒得改文件名了,继续使用turtle4.cpp文件
主要逻辑是写了2个函数:
一曲一直不就妙了吗,可以实现理想形状,这里实现的是乌龟1走数字2,乌龟2走数字5,如果需要其他形状自行修改
但是我不知道为什么计算的很抽象,实际上的具体数值需要调,和理想输入的不一样,主要是moveInLine函数,两个值都不准,甚至同样的代码重复运行效果都不同
运行:
分别在3个终端输入以下指令:这里面加入了删除乌龟1的代码,所以省了一行
效果如下:(分开截的)