• ROS python实现乌龟跟随


            产生两只乌龟,中间的乌龟(A) 和 左下乌龟(B), B 会自动运行至A的位置,并且键盘控制时,只是控制 A 的运动,但是 B 可以跟随 A 运行

            乌龟跟随实现的核心,是乌龟A和B都要发布相对世界坐标系的坐标信息,然后,订阅到该信息需要转换获取A相对于B坐标系的信息,最后,再生成速度信息,并控制B运动。

    Python实现

    1.创建功能包

    创建项目功能包依赖于 tf2、tf2_ros、tf2_geometry_msgs、roscpp rospy std_msgs geometry_msgs、turtlesim

    2.服务客户端(生成乌龟)
    1. #! /usr/bin/env python
    2. import rospy
    3. from turtlesim.srv import Spawn,SpawnRequest,SpawnResponse
    4. """
    5. 需求:向服务器发送请求生成一只乌龟
    6. 话题:/spawn
    7. 消息:turtlesim/Spawn
    8. 1、导包
    9. 2、初始化ROS节点
    10. 3、创建服务的客户端对象
    11. 4、组织数据并发送请求
    12. 5、处理响应结果
    13. """
    14. if __name__=="__main__":
    15. #2、初始化ROS节点
    16. rospy.init_node("service_call_p")
    17. #3、创建服务的客户端对象
    18. client=rospy.ServiceProxy("/spawn",Spawn)
    19. #4、组织数据并发送请求
    20. request = SpawnRequest()
    21. request.x=4.5
    22. request.y=2.0
    23. request.theta=-3
    24. request.name="turtle2"
    25. #4-2 判断服务器状态并发送
    26. client.wait_for_service()#客户端等待服务,若服务端没有启动则挂起
    27. try:
    28. response=client.call(request)
    29. #5、处理响应结果
    30. rospy.loginfo("生成乌龟的名字叫:%s",response.name)
    31. except Exception as e:
    32. rospy.logerr("请求处理异常")

    3.发布方(发布两只乌龟的坐标信息)

    1. #! /usr/bin/env python
    2. import rospy
    3. import tf.transformations
    4. from turtlesim.msg import Pose
    5. import tf2_ros
    6. from geometry_msgs.msg import TransformStamped
    7. import tf
    8. import sys
    9. """
    10. 发布方:订阅乌龟的位姿信息,转换处呢个坐标系的相对关系,再发布
    11. 准备:
    12. 话题:/turtle1/pose
    13. 类型:/turtlesim/Pose
    14. 流程:
    15. 1、导包
    16. 2、初始化ROS节点
    17. 3、创建订阅对象
    18. 4、回调函数处理订阅到的消息(核心)
    19. 5、spin()
    20. """
    21. #接受乌龟名称的变量
    22. turtle_name = ""
    23. def doPose(pose): #参数为订阅到的消息 pose
    24. #创建发布坐标系相对关系的对象
    25. pub=tf2_ros.TransformBroadcaster()
    26. #将pose转换成坐标系相对关系消息
    27. ts=TransformStamped()
    28. ts.header.frame_id="world" #被参考的坐标系
    29. ts.header.stamp=rospy.Time.now()
    30. #修改2--------------------------------------------------------------
    31. ts.child_frame_id=turtle_name
    32. #子级坐标系相对于父级坐标系的偏移量
    33. ts.transform.translation.x=pose.x
    34. ts.transform.translation.y=pose.y
    35. ts.transform.translation.z=0
    36. #四元数
    37. #从欧拉角转换四元数
    38. """
    39. 乌龟是2D的,不存在X上的翻滚Y上偏航,只有Z上的偏航
    40. 0 0 pose.thera
    41. """
    42. qtn=tf.transformations.quaternion_from_euler(0,0,pose.theta)
    43. ts.transform.rotation.x=qtn[0]
    44. ts.transform.rotation.y=qtn[1]
    45. ts.transform.rotation.z=qtn[2]
    46. ts.transform.rotation.w=qtn[3]
    47. #发布
    48. pub.sendTransform(ts)
    49. if __name__=="__main__":
    50. # 2、初始化ROS节点
    51. rospy.init_node("dynamic_pub_p")
    52. # 3、创建订阅对象
    53. #解析传入的参数(现在传入几个参数?文件全路径+传入的参数+自己定义的节点名称+日志文件路径)
    54. if len(sys.argv)!=4:
    55. rospy.loginfo("参数个数不对")
    56. sys.exit(1)
    57. else:
    58. turtle_name=sys.argv[1]
    59. #修改1
    60. sub=rospy.Subscriber(turtle_name+"/pose",Pose,doPose,queue_size=100)
    61. # 4、回调函数处理订阅到的消息(核心)
    62. # 5、spin()
    63. rospy.spin()

     4.订阅方(解析坐标信息并生成速度信息)

    1. #! /usr/bin/env python
    2. import rospy
    3. import tf2_ros
    4. import tf2_geometry_msgs
    5. # 不要使用 geometry_msgs,需要使用 tf2 内置的消息类型
    6. from tf2_geometry_msgs import PointStamped
    7. # from geometry_msgs.msg import PointStamped
    8. from geometry_msgs.msg import TransformStamped,Twist
    9. import math
    10. if __name__=="__main__":
    11. # 2、初始化
    12. rospy.init_node("static_sub_p")
    13. # 3、创建订阅对象
    14. #3-1 创建缓存对象
    15. buffer=tf2_ros.Buffer()
    16. #3-2 创建订阅对象(将缓存传入)
    17. sub=tf2_ros.TransformListener(buffer)
    18. # 创建速度消息发布对象
    19. pub=rospy.Publisher("/turtle2/cmd_vel",Twist,queue_size=100)
    20. # 5、转换逻辑实现,调用tf封装的算法
    21. rate=rospy.Rate(10)
    22. while not rospy.is_shutdown():
    23. try:
    24. #--------------------计算相son1相对于son2的坐标关系
    25. """
    26. 参数1:目标坐标系
    27. 参数2:源坐标系
    28. 参数3:rospy.Time(0)----------取时间间隔最近的两个坐标系帧(son1相对world与son2相对world)来计算结果
    29. 返回值:son1与son2的坐标关系
    30. """
    31. ts=buffer.lookup_transform("turtle2","turtle1",rospy.Time(0))
    32. rospy.loginfo("父级坐标系:%s,子级坐标系:%s,偏移量(%.2f,%.2f,%.2f)",ts.header.frame_id,ts.child_frame_id,
    33. ts.transform.translation.x,ts.transform.translation.y,ts.transform.translation.z)
    34. #组织Twist消息
    35. twist=Twist()
    36. #线速度=系数*坐标系原点的间距=系数*(x^2 +y^2 )再开方
    37. #角速度=系数*夹角 =系数*atan2(y,x)
    38. twist.linear.x=0.5*math.sqrt(math.pow(ts.transform.translation.x,2)
    39. + math.pow(ts.transform.translation.y,2))
    40. twist.angular.z=4*math.atan2(ts.transform.translation.y,ts.transform.translation.x)
    41. #发布消息
    42. pub.publish(twist)
    43. except Exception as e:
    44. rospy.logwarn("错误提示:%s",e)
    45. # 7、spain() |spinOnce()
    46. rate.sleep()

     使用 launch 文件组织需要运行的节点

    1. <launch>
    2. <!--
    3. 流程详解:
    4. 1.准备工作:启动乌龟的GUI节点和键盘控制节点
    5. 2、需要调用服务器生成一只新的乌龟
    6. 3、发布两只乌龟的坐标信息
    7. 4、订阅坐标信息,并转换成乌龟A相对于乌龟B 的坐标信息,最后再生成控制乌龟的速度信息
    8. -->
    9. <!--1.准备工作:启动乌龟的GUI节点和键盘控制节点-->
    10. <!--乌龟GUI-->
    11. <node pkg="turtlesim" type="turtlesim_node" name="turtle1" output="screen" />
    12. <!--键盘控制-->
    13. <node pkg="turtlesim" type="turtle_teleop_key" name="key" output="screen" />
    14. <!--2、需要调用服务器生成一只新的乌龟-->
    15. <node pkg="tf04_test" type="test01_new_turtle_p.py" name="turtle2" output="screen" />
    16. <!--3、发布两只乌龟的坐标信息
    17. A、复用之前的乌龟坐标发布功能
    18. B、调用节点时,以参数的方式传递乌龟名称,解析参数置换:订阅的话题消息和子级坐标系的名称
    19. -->
    20. <node pkg="tf04_test" type="test02_pub_turtle_p.py" name="pub1" args="turtle1" output="screen" />
    21. <node pkg="tf04_test" type="test02_pub_turtle_p.py" name="pub2" args="turtle2" output="screen" />
    22. <!--4、订阅坐标信息,并转换成乌龟A相对于乌龟B 的坐标信息,最后再生成控制乌龟的速度信息-->
    23. <node pkg="tf04_test" type="test03_control_turtle2_p.py" name="control" output="screen" />
    24. </launch>

     先修改py文件的权限并在CMakeList中加相应的配置

    运行launch文件

    参考链接:

    [1]207坐标变换实操Python01_生成乌龟-ROS常用组件_哔哩哔哩_bilibili

  • 相关阅读:
    【大数据分析】FordFulkerson算法(JAVA实现)
    Linux系统之部署Dailynotes个人笔记管理工具登录到Dailynotes首页,输入内容无法保存,如何解决?
    Spring Boot 3.x Web MVC实战:实现流缓存的request
    【Redis】简单动态字符串SDS
    C# 如何将使用的Dll嵌入到.exe应用程序中?
    GPTs 今日上线:OpenAI杀死了初创公司
    搜索引擎-01-概览
    sql 注入(1), union 联合注入
    Python灰帽编程——初识Python下(函数与文件)
    SQL笔记——数据库恢复技术
  • 原文地址:https://blog.csdn.net/El_ia_uk/article/details/138181682