• ROS1云课→23turtlesim绘制小结(数学和编程)


    ROS1云课→22机器人轨迹跟踪


    这一节就有些乱了……

    机器人如何走出一个正方形的轨迹呢?

    这里,采用了官方例程中:

    1. //draw_square
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. turtlesim::PoseConstPtr g_pose;
    8. turtlesim::Pose g_goal;
    9. enum State
    10. {
    11. FORWARD,
    12. STOP_FORWARD,
    13. TURN,
    14. STOP_TURN,
    15. };
    16. State g_state = FORWARD;
    17. State g_last_state = FORWARD;
    18. bool g_first_goal_set = false;
    19. #define PI 3.141592
    20. void poseCallback(const turtlesim::PoseConstPtr& pose)
    21. {
    22. g_pose = pose;
    23. }
    24. bool hasReachedGoal()
    25. {
    26. return fabsf(g_pose->x - g_goal.x) < 0.1 && fabsf(g_pose->y - g_goal.y) < 0.1 && fabsf(g_pose->theta - g_goal.theta) < 0.01;
    27. }
    28. bool hasStopped()
    29. {
    30. return g_pose->angular_velocity < 0.0001 && g_pose->linear_velocity < 0.0001;
    31. }
    32. void printGoal()
    33. {
    34. ROS_INFO("New goal [%f %f, %f]", g_goal.x, g_goal.y, g_goal.theta);
    35. }
    36. void commandTurtle(ros::Publisher twist_pub, float linear, float angular)
    37. {
    38. geometry_msgs::Twist twist;
    39. twist.linear.x = linear;
    40. twist.angular.z = angular;
    41. twist_pub.publish(twist);
    42. }
    43. void stopForward(ros::Publisher twist_pub)
    44. {
    45. if (hasStopped())
    46. {
    47. ROS_INFO("Reached goal");
    48. g_state = TURN;
    49. g_goal.x = g_pose->x;
    50. g_goal.y = g_pose->y;
    51. g_goal.theta = fmod(g_pose->theta + PI/2.0, 2*PI);
    52. printGoal();
    53. }
    54. else
    55. {
    56. commandTurtle(twist_pub, 0, 0);
    57. }
    58. }
    59. void stopTurn(ros::Publisher twist_pub)
    60. {
    61. if (hasStopped())
    62. {
    63. ROS_INFO("Reached goal");
    64. g_state = FORWARD;
    65. g_goal.x = cos(g_pose->theta) * 2 + g_pose->x;
    66. g_goal.y = sin(g_pose->theta) * 2 + g_pose->y;
    67. g_goal.theta = g_pose->theta;
    68. printGoal();
    69. }
    70. else
    71. {
    72. commandTurtle(twist_pub, 0, 0);
    73. }
    74. }
    75. void forward(ros::Publisher twist_pub)
    76. {
    77. if (hasReachedGoal())
    78. {
    79. g_state = STOP_FORWARD;
    80. commandTurtle(twist_pub, 0, 0);
    81. }
    82. else
    83. {
    84. commandTurtle(twist_pub, 1.0, 0.0);
    85. }
    86. }
    87. void turn(ros::Publisher twist_pub)
    88. {
    89. if (hasReachedGoal())
    90. {
    91. g_state = STOP_TURN;
    92. commandTurtle(twist_pub, 0, 0);
    93. }
    94. else
    95. {
    96. commandTurtle(twist_pub, 0.0, 0.4);
    97. }
    98. }
    99. void timerCallback(const ros::TimerEvent&, ros::Publisher twist_pub)
    100. {
    101. if (!g_pose)
    102. {
    103. return;
    104. }
    105. if (!g_first_goal_set)
    106. {
    107. g_first_goal_set = true;
    108. g_state = FORWARD;
    109. g_goal.x = cos(g_pose->theta) * 2 + g_pose->x;
    110. g_goal.y = sin(g_pose->theta) * 2 + g_pose->y;
    111. g_goal.theta = g_pose->theta;
    112. printGoal();
    113. }
    114. if (g_state == FORWARD)
    115. {
    116. forward(twist_pub);
    117. }
    118. else if (g_state == STOP_FORWARD)
    119. {
    120. stopForward(twist_pub);
    121. }
    122. else if (g_state == TURN)
    123. {
    124. turn(twist_pub);
    125. }
    126. else if (g_state == STOP_TURN)
    127. {
    128. stopTurn(twist_pub);
    129. }
    130. }
    131. int main(int argc, char** argv)
    132. {
    133. ros::init(argc, argv, "draw_square");
    134. ros::NodeHandle nh;
    135. ros::Subscriber pose_sub = nh.subscribe("turtle1/pose", 1, poseCallback);
    136. ros::Publisher twist_pub = nh.advertise("turtle1/cmd_vel", 1);
    137. ros::ServiceClient reset = nh.serviceClient("reset");
    138. ros::Timer timer = nh.createTimer(ros::Duration(0.016), boost::bind(timerCallback, _1, twist_pub));
    139. std_srvs::Empty empty;
    140. reset.call(empty);
    141. ros::spin();
    142. }

    由于原来代码中,对于到达目标的误差设定较大:

    bool hasReachedGoal()
    {
      return fabsf(g_pose->x - g_goal.x) < 0.1 && fabsf(g_pose->y - g_goal.y) < 0.1 && fabsf(g_pose->theta - g_goal.theta) < 0.01;
    }

    如果单纯提升精度,会由于控制速度是阶跃信号(控制要么为零要么是一个固定值),导致控制失效。

        commandTurtle(twist_pub, 1.0, 0.0);

    这种类似bang-bang控制。

    enum State
    {
      FORWARD,
      STOP_FORWARD,
      TURN,
      STOP_TURN,
    };

    State g_state = FORWARD;
    State g_last_state = FORWARD;
    bool g_first_goal_set = false;

    为何定义四种状态?

    为何控制不连续?

    机器人轨迹和数学之间的联系。

    数学或物理上位移和速度的关系就类似为机器人运行轨迹和机器人速度控制量之间的关系。

    定理:如果函数 f 在 x = a 中可导,则 f 在 x = a 中也是连续的。

    每个直角∟点,显然不连续,机器人轨迹是分段的,控制自然也分段了。

    改了如上的代码,实现了高精度的曲线绘制,但是依然存在误差,更不能忍受的是,一直画下去,根本停不下来啊……

    那怎么办?

    需要用topic/service/action  

    用服务或者行动更为合适。

    参考如下程序:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. // This class computes the command_velocities of the turtle to draw regular polygons
    10. class ShapeAction
    11. {
    12. public:
    13. ShapeAction(std::string name) :
    14. as_(nh_, name, false),
    15. action_name_(name)
    16. {
    17. //register the goal and feeback callbacks
    18. as_.registerGoalCallback(boost::bind(&ShapeAction::goalCB, this));
    19. as_.registerPreemptCallback(boost::bind(&ShapeAction::preemptCB, this));
    20. //subscribe to the data topic of interest
    21. sub_ = nh_.subscribe("/turtle1/pose", 1, &ShapeAction::controlCB, this);
    22. pub_ = nh_.advertise("/turtle1/cmd_vel", 1);
    23. as_.start();
    24. }
    25. ~ShapeAction(void)
    26. {
    27. }
    28. void goalCB()
    29. {
    30. // accept the new goal
    31. turtle_actionlib::ShapeGoal goal = *as_.acceptNewGoal();
    32. //save the goal as private variables
    33. edges_ = goal.edges;
    34. radius_ = goal.radius;
    35. // reset helper variables
    36. interior_angle_ = ((edges_-2)*M_PI)/edges_;
    37. apothem_ = radius_*cos(M_PI/edges_);
    38. //compute the side length of the polygon
    39. side_len_ = apothem_ * 2* tan( M_PI/edges_);
    40. //store the result values
    41. result_.apothem = apothem_;
    42. result_.interior_angle = interior_angle_;
    43. edge_progress_ =0;
    44. start_edge_ = true;
    45. }
    46. void preemptCB()
    47. {
    48. ROS_INFO("%s: Preempted", action_name_.c_str());
    49. // set the action state to preempted
    50. as_.setPreempted();
    51. }
    52. void controlCB(const turtlesim::Pose::ConstPtr& msg)
    53. {
    54. // make sure that the action hasn't been canceled
    55. if (!as_.isActive())
    56. return;
    57. if (edge_progress_ < edges_)
    58. {
    59. // scalar values for drive the turtle faster and straighter
    60. double l_scale = 6.0;
    61. double a_scale = 6.0;
    62. double error_tol = 0.00001;
    63. if (start_edge_)
    64. {
    65. start_x_ = msg->x;
    66. start_y_ = msg->y;
    67. start_theta_ = msg->theta;
    68. start_edge_ = false;
    69. }
    70. // compute the distance and theta error for the shape
    71. dis_error_ = side_len_ - fabs(sqrt((start_x_- msg->x)*(start_x_-msg->x) + (start_y_-msg->y)*(start_y_-msg->y)));
    72. theta_error_ = angles::normalize_angle_positive(M_PI - interior_angle_ - (msg->theta - start_theta_));
    73. if (dis_error_ > error_tol)
    74. {
    75. command_.linear.x = l_scale*dis_error_;
    76. command_.angular.z = 0;
    77. }
    78. else if (dis_error_ < error_tol && fabs(theta_error_)> error_tol)
    79. {
    80. command_.linear.x = 0;
    81. command_.angular.z = a_scale*theta_error_;
    82. }
    83. else if (dis_error_ < error_tol && fabs(theta_error_)< error_tol)
    84. {
    85. command_.linear.x = 0;
    86. command_.angular.z = 0;
    87. start_edge_ = true;
    88. edge_progress_++;
    89. }
    90. else
    91. {
    92. command_.linear.x = l_scale*dis_error_;
    93. command_.angular.z = a_scale*theta_error_;
    94. }
    95. // publish the velocity command
    96. pub_.publish(command_);
    97. }
    98. else
    99. {
    100. ROS_INFO("%s: Succeeded", action_name_.c_str());
    101. // set the action state to succeeded
    102. as_.setSucceeded(result_);
    103. }
    104. }
    105. protected:
    106. ros::NodeHandle nh_;
    107. actionlib::SimpleActionServer as_;
    108. std::string action_name_;
    109. double radius_, apothem_, interior_angle_, side_len_;
    110. double start_x_, start_y_, start_theta_;
    111. double dis_error_, theta_error_;
    112. int edges_ , edge_progress_;
    113. bool start_edge_;
    114. geometry_msgs::Twist command_;
    115. turtle_actionlib::ShapeResult result_;
    116. ros::Subscriber sub_;
    117. ros::Publisher pub_;
    118. };
    119. int main(int argc, char** argv)
    120. {
    121. ros::init(argc, argv, "turtle_shape");
    122. ShapeAction shape(ros::this_node::getName());
    123. ros::spin();
    124. return 0;
    125. }

    这里面,对于精度设置如下:

    error_tol = 0.00001; 

    用这种方式实现,还有一个有点就是可以设置任意边。

    1. #include
    2. #include
    3. #include
    4. #include
    5. int main (int argc, char **argv)
    6. {
    7. ros::init(argc, argv, "test_shape");
    8. // create the action client
    9. // true causes the client to spin it's own thread
    10. actionlib::SimpleActionClient ac("turtle_shape", true);
    11. ROS_INFO("Waiting for action server to start.");
    12. // wait for the action server to start
    13. ac.waitForServer(); //will wait for infinite time
    14. ROS_INFO("Action server started, sending goal.");
    15. // send a goal to the action
    16. turtle_actionlib::ShapeGoal goal;
    17. goal.edges = 5;
    18. goal.radius = 1.3;
    19. ac.sendGoal(goal);
    20. //wait for the action to return
    21. bool finished_before_timeout = ac.waitForResult(ros::Duration(40.0));
    22. if (finished_before_timeout)
    23. {
    24. actionlib::SimpleClientGoalState state = ac.getState();
    25. ROS_INFO("Action finished: %s",state.toString().c_str());
    26. }
    27. else
    28. ROS_INFO("Action did not finish before the time out.");
    29. //exit
    30. return 0;
    31. }

    在如下程序加入一些程序段:

      int edges_input;
      float radius_input;
      ROS_INFO("Please input edges:");
      std::cin>>edges_input;
      ROS_INFO("Please input radius:");
      std::cin>>radius_input;
      if(edges_input>2)
        goal.edges = edges_input;
      if(radius_input>0)
        goal.radius = radius_input;

    全部代码如下: 

    1. #include
    2. #include
    3. #include
    4. #include
    5. int main (int argc, char **argv)
    6. {
    7. ros::init(argc, argv, "test_shape");
    8. // create the action client
    9. // true causes the client to spin it's own thread
    10. actionlib::SimpleActionClient ac("turtle_shape", true);
    11. ROS_INFO("Waiting for action server to start.");
    12. // wait for the action server to start
    13. ac.waitForServer(); //will wait for infinite time
    14. ROS_INFO("Action server started, sending goal.");
    15. // send a goal to the action
    16. turtle_actionlib::ShapeGoal goal;
    17. goal.edges = 5;
    18. goal.radius = 1.3;
    19. int edges_input;
    20. float radius_input;
    21. ROS_INFO("Please input edges:");
    22. std::cin>>edges_input;
    23. ROS_INFO("Please input radius:");
    24. std::cin>>radius_input;
    25. if(edges_input>2)
    26. goal.edges = edges_input;
    27. if(radius_input>0)
    28. goal.radius = radius_input;
    29. ac.sendGoal(goal);
    30. //wait for the action to return
    31. bool finished_before_timeout = ac.waitForResult(ros::Duration(40.0));
    32. if (finished_before_timeout)
    33. {
    34. actionlib::SimpleClientGoalState state = ac.getState();
    35. ROS_INFO("Action finished: %s",state.toString().c_str());
    36. }
    37. else
    38. ROS_INFO("Action did not finish before the time out.");
    39. //exit
    40. return 0;
    41. }

    三角形:

     

    九边形:

     

    二十七边形: 

     

    相关博文,供参考:

    ROS2趣味题库之turtlesim魔幻步伐(轨迹类题型)

    玫瑰线轨迹如何规划?(desmos+ROS2+turtlesim+……) 


     

  • 相关阅读:
    1688关键字搜索商品
    乐观模式下分库分表合并迁移
    混淆矩阵绘制
    根据身份证号回填信息
    AutoDL使用手册
    Web基础与HTTP协议
    JavaScript中的深拷贝和浅拷贝
    你知道HTTP与HTTPS有什么区别吗?
    如何在linux系统中设置定时任务?
    Dubbo启动报错
  • 原文地址:https://blog.csdn.net/ZhangRelay/article/details/126789498