当前位置:   article > 正文

ubuntu20.04 gazebo 仿真环境创建以及使用gazebo建图实现导航_ubuntu20.04gazebo安装教程

ubuntu20.04gazebo安装教程

1.gazebo 仿真环境创建

安装依赖:

  1. (base) hjb@jl16k:~$ sudo apt install ros-$ROS_DISTRO-gazebo-ros-control
  2. (base) hjb@jl16k:~$ sudo apt install ros-$ROS_DISTRO-effort-controllers
  3. (base) hjb@jl16k:~$ sudo apt install ros-$ROS_DISTRO-joint-state-controller
  4. (base) hjb@jl16k:~$ sudo apt install ros-$ROS_DISTRO-driver-base
  5. (base) hjb@jl16k:~$ sudo apt install ros-$ROS_DISTRO-ackermann-msgs
  6. (base) hjb@jl16k:~$ sudo apt install ros-$ROS_DISTRO-rtabmap-ros
  7. (base) hjb@jl16k:~$ sudo apt install ros-$ROS_DISTRO-teb-local-planner
  8. (base) hjb@jl16k:~$ sudo apt install tcl-dev tk-dev python3-tk

若在安装依赖的过程中出现以下错误:

通过输入以下代码尝试解决:

(base) hjb@jl16k:~$ sudo apt-get update

创建工作空间:

(base) hjb@jl16k:~$ mkdir -p catkin_gazebo_ws/src

下载git工程:

  1. (base) hjb@jl16k:~/catkin_gazebo_ws$ cd src
  2. (base) hjb@jl16k:~/catkin_gazebo_ws/src$ git clone https://github.com/soonuse/racecar.git

编译:

  1. (base) hjb@jl16k:~/catkin_gazebo_ws/src$ cd ..
  2. (base) hjb@jl16k:~/catkin_gazebo_ws$ catkin_make

如果编译过程中出现以下错误:

错误1:

 解决办法:

(base) hjb@jl16k:~/catkin_gazebo_ws$ catkin_make -DPYTHON_EXECUTABLE=/usr/bin/python3 

错误2:

解决办法:

找到自己的opencv安装路径,像我的就是 /usr/local/include/opencv4/opencv2

 然后打开提示错误的这个文件,当然这个文件只可读,可以使用以下命令打开并修改:

(base) hjb@jl16k:/opt/ros/noetic/share/cv_bridge/cmake$ sudo gedit cv_bridgeConfig.cmake 

 大概在90多行,将箭头所指修改成上面找到的opencv安装路径,注意修改两个地方

 修改完之后,重新编译:

(base) hjb@jl16k:~/catkin_gazebo_ws$ catkin_make -DPYTHON_EXECUTABLE=/usr/bin/python3 

把工作区的setup.bash文件写入.bashrc文件中,先执行以下命令,然后得到路径/home/hjb/catkin_gazebo_ws/devel

 打开新的终端:

(base) hjb@jl16k:~$ sudo gedit .bashrc

添加下图中的两行,路径就是上面所得到的路径并加上setup.bash文件名:

保存文件,为了使用配置,输入:

(base) hjb@jl16k:~$ . .bashrc

 需要注意的是,这个仿真环境是从网上下载的,也就是git那一步。需要了解这个工作空间中各个文件的用处:

像途中racecar_description里面存的是机器人模型(urdf)等,而racecar_gazebo里面是放与Gazebo有关的启动文件(launch)和世界文件(word)等。这个工作空间完全是可以自己创建的,其中图所示名字也是可以改变的,例如racecar_description完全可以改成myrobot_description,当然这是有能力自己创建的前提下。

2.gazebo的使用

初次使用gazebo的可以参考一下,里面有一些功能建的说明与使用:ROS入门(四)——Gazebo的基本使用 - 古月居

(1)简单创建一个地图模型

首先进入gazebo:

(base) hjb@jl16k:~/catkin_gazebo_ws$ gazebo

 然后左上角点击Edit,选择Buiding Editor,进入下图界面

可以看到左窗口有walls(墙)等,点击wall简单创建一个房子后,点击左上角File,然后选择Save,建议第一次使用保存在主目录(home)下。如下图所示,我保存下来的是house文件,命名是随意的。打开house文件会发现有两个文件。

 保存完之后,选择File Exit Building Editor,然后会有弹窗,选择Exit。就会回到原来的界面。如下图所示。

这个就属于world文件了,点击File,选择Save world,名字最好加上world(注意这个文件后缀记得加 .world,保存的时候不会默认是world文件的,需要自己加,图中我是保存错了)以区分这是一个world文件,然后也是先保存在主目录,以后熟练了可以根据需求保存。保存完之后目录下的文件如下图所示:

 到此,关掉gazebo。那又该如何打开之前建好的模型呢?

重新打开gazebo,点击左窗口的Insert,然后看到我下图箭头所指,这个就是上面所建的模型,点击即可拉到界面。

 当然值的注意的是,上面的例子是墙体模型,也有其他的模型,这需要慢慢去探索了。

(2)下载模型

         一般来说,如果有现成的图形模型的话,那就大大节省了自己的时间,可以使用有的模型来做实验。

        下载模型有两种反法,我用的是第二种方法,因为第一种方法可能是我的网不行下载的非常慢,虽然第二种方法我也下载了3个多小时。

        方法1:注意的是.gazebo是隐藏的文件,可以在主目录(home)使用ctrl+H来显示出来,此外使用该方法下载的文件名为gazebo_models,需要将这个文件名改为models

  1. (base) hjb@jl16k:~$ cd ~/.gazebo
  2. (base) hjb@jl16k:~/.gazebo$ git clone https://github.com/osrf/gazebo_models

        方法2:

  1. (base) hjb@jl16k:~$ cd ~/.gazebo
  2. (base) hjb@jl16k:~/.gazebo$ mkdir -p models
  3. (base) hjb@jl16k:~/.gazebo$ cd models/
  4. (base) hjb@jl16k:~/.gazebo/models$ wget http://file.ncnynl.com/ros/gazebo_models.txt
  5. (base) hjb@jl16k:~/.gazebo/models$ wget -i gazebo_models.txt
  6. (base) hjb@jl16k:~/.gazebo/models$ ls model.tar.g* | xargs -n1 tar xzvf

        如果到下图这一步,应该算成功了吧。我也是刚入门不太懂,从文字上看下载了135M,应该不止这么小的。重复方法2倒数第二条命令还可以下载,我浅浅试了一下,不过我终止了,以后有需要再尝试一下吧。 最后解压一下就Ok了。

 

        打开gazebo:

(base) hjb@jl16k:~/catkin_gazebo_ws$ gazebo

        进入下图界面会发现,本地模型有了,随便拉一个进入 ,有显示就成功了。我拉了cafe这个模型进去。 

(3)world模型的打开,使用launch文件

        此前建了一个模型,有house的地图模型,以及world模型。

        上面所示的打开是使用house模型,而建好的world场景又如何打开呢?这就得用launch文件打开了。

        如下图所示,一般仿真环境都会有保存launch文件的功能包,像我这个环境存放launch文件的是racecar_gazebo,并且在这个功能包中有worlds文件,所创建的world文件应该拉到这个地方。所以将上面创建的house.world文件拉到worlds文件中,方便写launch文件。

 

         写文件推荐使用vscode,用vscode打开catkin_gazebo_ws这个工作空间,然后在launch文件夹中创建一个launch文件,我创建的文件名为 display_house_world_in_gazebo.launch,这样创建可以知道这个launch文件是干嘛的,从意思上看我这个就是在gazebo中展示house.world。

        代码如下,唯一要修改的就是 <arg name="world_name" value="$(find racecar_gazebo)/worlds/house_world_as.world"/>这一行了,自己world文件的路径如果不同根据自己作修改,$(find racecar_gazebo)就是找到racecar_gazebo这个功能包的路径,如果创建的功能包名字不一样修改一下就好了:

  1. <?xml version="1.0"?>
  2. <launch>
  3. <!-- We resume the logic in empty_world.launch, changing only the name of the world to be launched -->
  4. <include file="$(find gazebo_ros)/launch/empty_world.launch">
  5. <arg name="world_name" value="$(find racecar_gazebo)/worlds/house_world_as.world"/>
  6. <arg name="paused" value="false"/>
  7. <arg name="use_sim_time" value="true"/>
  8. <arg name="gui" value="true"/>
  9. <arg name="headless" value="false"/>
  10. <arg name="debug" value="false"/>
  11. </include>
  12. </launch>

        然后执行一下命令即可看到打开的world文件:

  1. (base) hjb@jl16k:~/catkin_gazebo_ws$ source devel/setup.bash
  2. (base) hjb@jl16k:~/catkin_gazebo_ws$ roslaunch racecar_gazebo display_house_world_in_gazebo.launch

(4)world模型+小车模型的打开,使用launch文件

        world+小车的打开也就是在world场景下增加了一辆小车,这就需要一个小车模型了。一般来说从网上下载的仿真环境都会有小车模型 。像下图中所示,我的小车模型就是在/catkin_gazebo_ws/src/racecar/racecar_description/urdf,其中我要使用的就是racecar_xacro。

         同样需要在launch文件夹中创建一个launch文件,一般来说展示world场景和小车的launch文件较为复杂,我是通过复制所下载的环境中的一个launch文件修改得到的。

        如下图所示,我所下载的仿真环境中小车+world展示的launch文件是racecar.launch,但是这个文件所指定的world文件并不是我想要的场景,因此负责该文件的内容创建一个launch文件,然后修改一下world文件的名字。 

        代码修改说明:

        像我这个文件的话,其实以及很友好了,如果想要换world文件,只需要在<arg name="world_name" default="house_world_as" />这一行中修改default的值就可以了,像我的world文件名是house_world_as。注意这个地方不需要加文件名的后缀.world。当然在include的那部分中找world_name的路径也是需要注意的,如果和我的仿真环境不同需要自己改。我的launch文件名是display_house_world_and_car_in_gazebo.launch

  1. <?xml version="1.0"?>
  2. <launch>
  3. <arg name="world_name" default="house_world_as" />
  4. <arg name="gui" default="true" />
  5. <arg name="run_camera" default="false"/>
  6. <arg name="x_pos" default="-5.388334"/>
  7. <arg name="y_pos" default="-4.094883"/>
  8. <arg name="z_pos" default="0.0"/>
  9. <include file="$(find gazebo_ros)/launch/empty_world.launch">
  10. <arg name="world_name" value="$(find racecar_gazebo)/worlds/$(arg world_name).world"/>
  11. <arg name="gui" value="$(arg gui)"/>
  12. </include>
  13. <!-- urdf xml robot description loaded on the Parameter Server, converting the xacro into a proper urdf file-->
  14. <param name="robot_description" command="$(find xacro)/xacro --inorder '$(find racecar_description)/urdf/racecar.xacro'" />
  15. <!-- push robot_description to factory and spawn robot in gazebo -->
  16. <node name="racecar_spawn" pkg="gazebo_ros" type="spawn_model" output="screen" args="-urdf -param robot_description -model racecar -x $(arg x_pos) -y $(arg y_pos) -z $(arg z_pos)" />
  17. <!-- ros_control racecar launch file -->
  18. <include file="$(find racecar_control)/launch/racecar_control.launch" ns="/"/>
  19. <!-- Spawn the MUXs -->
  20. <arg name="racecar_version" default="racecar-v2" />
  21. <include file="$(find racecar)/launch/mux.launch" ns="vesc" />
  22. <!-- Publish "better odom" topic that is normally generated by the particle filter -->
  23. <node name="better_odom" pkg="topic_tools" type="relay"
  24. args="/vesc/odom /pf/pose/odom" />
  25. <!--Launch the simulation joystick control-->
  26. <rosparam command="load" file="$(find racecar_gazebo)/config/keyboard_teleop.yaml" />
  27. <node pkg="racecar_gazebo" type="keyboard_teleop.py" name="keyboard_teleop" />
  28. </launch>

        执行,会发现多了个小车:

(base) hjb@jl16k:~/catkin_gazebo_ws$ roslaunch racecar_gazebo display_house_world_and_car_in_gazebo.launch

 (5)使用这个world模型+小车模型来建一下图(gmapping)

        这一步需要gmapping功能包,这个功能包的下载可以网上找教程下载,下载好之后就可以不管了。launch文件还是创在仿真环境中的,在launch文件中指定gmapping的功能包名字会自动寻找并使用。

        一般来说网上下载的仿真环境一般都会有gmapping的launch文件,如果没有也没有关系。启动gmapping建图需要创建两个launch文件,这些launch文件全都是放在图中上面所示的路径中,此后不在赘述。并且这两个文件的内容是不需要改就可以直接使用了,当然前提是有gmapping功能包。

        slam_gmapping.launch(这个文件的作用是调用gmapping,如果调用的文件名不一样注意修改文件路径):

  1. <launch>
  2. <include file="$(find racecar_gazebo)/launch/gmapping.launch"/>
  3. <!-- 启动rviz -->
  4. <node pkg="rviz" type="rviz" name="rviz" args="-d $(find racecar_gazebo)/config/new_gmapping.rviz"/>
  5. </launch>

        gmapping.launch:

  1. <launch>
  2. <node pkg="gmapping" type="slam_gmapping" name="slam_gmapping" output="screen">
  3. <param name="base_frame" value="base_link"/> <!--机器人底盘坐标系基框架,附带在移动底盘的框架,原点-->
  4. <param name="odom_frame" value="odom"/> <!--里程计坐标系里程计框架,附带在里程计的框架-->
  5. <param name="map_frame" value="map"/> <!--地图坐标系地图框架,附带在地图上的框架-->
  6. <param name="map_update_interval" value="0.01"/><!--地图更新速度,秒0.01-->
  7. <param name="maxUrange" value="10.0"/><!--激光最大可用距离-->
  8. <param name="maxRange" value="12.0"/><!--zuida juli-->
  9. <param name="sigma" value="0.05"/>
  10. <param name="kernelSize" value="3"/><!--moren:1-->
  11. <param name="lstep" value="0.05"/>
  12. <param name="astep" value="0.05"/>
  13. <param name="iterations" value="5"/>
  14. <param name="lsigma" value="0.075"/>
  15. <param name="ogain" value="3.0"/>
  16. <param name="lskip" value="0"/>
  17. <param name="srr" value="0.1"/>
  18. <param name="srt" value="0.2"/>
  19. <param name="str" value="0.1"/>
  20. <param name="stt" value="0.2"/>
  21. <param name="minimumScore" value="0"/>
  22. <param name="linearUpdate" value="0.05"/><!--线速度角速度在地图的更新-->
  23. <param name="angularUpdate" value="0.0436"/>
  24. <param name="temporalUpdate" value="-1"/><!--moren:-1-->
  25. <param name="resampleThreshold" value="0.5"/>
  26. <param name="particles" value="8"/><!--moren:30 gaicheng:8-->
  27. <param name="xmin" value="-50.0"/>
  28. <param name="ymin" value="-50.0"/>
  29. <param name="xmax" value="50.0"/>
  30. <param name="ymax" value="50.0"/>
  31. <param name="delta" value="0.05"/>
  32. <param name="llsamplerange" value="0.01"/>
  33. <param name="llsamplestep" value="0.01"/>
  34. <param name="lasamplerange" value="0.005"/>
  35. <param name="lasamplestep" value="0.005"/>
  36. <!--param name="transform_publish_period" value="0.01"/-->
  37. </node>
  38. </launch>

        有了建图启动文件就可以建图了。

        一般来说,我一旦使用ros,都会先roscore一下,建议也roscore一下:

(base) hjb@jl16k:~$ roscore

        首先,先打开之前自己创建的场景,也就是world+小车的场景,打开一个新的终端,使用launch文件启动:

  1. (base) hjb@jl16k:$ cd catkin_gazebo_ws
  2. (base) hjb@jl16k:~/catkin_gazebo_ws$ source devel/setup.bash
  3. (base) hjb@jl16k:~/catkin_gazebo_ws$ roslaunch racecar_gazebo display_house_world_and_car_in_gazebo.launch

        有真实地图和机器人了,就可以控制机器人使用算法建图了,使用launch文件启动建图和键盘控制,这个键盘控制是我这个仿真环境写好的,至于其他仿真环境的话,那我只能说我无能为力:

        打开新终端:

  1. (base) hjb@jl16k:~$ cd catkin_gazebo_ws/
  2. (base) hjb@jl16k:~/catkin_gazebo_ws$ source devel/setup.bash
  3. (base) hjb@jl16k:~/catkin_gazebo_ws$ roslaunch racecar_gazebo slam_gmapping.launch

        键盘控制移动扫描完整个地图完成建图,在建图想要移动必须点击下图所示的这个界面才能移动:

        

        建好图的效果:

        打开新终端,保存地图,可以根据自己需求改路径:

  1. (base) hjb@jl16k:~$ rosrun map_server map_saver -f /home/hjb/catkin_gazebo_ws/src/racecar/racecar_gazebo/map/house

 

  (6)使用上步所保存的图来导航

        有了建图自然少不了导航,先说一下导航的前提条件,一般来说导航需要使用move_base功能包,这个包一般在navigation包中。而这个navigation包一般安装gmapping算法的时候会自带,如果没有需要自己下载这个navigation包,这个包网上教程参考下载即可。

        到这一步,之前打开的终端可以都关了。实现导航需要两个launch文件,一个rivz配置文件,以及一个路径追踪的脚本。

        racecar_house_and_car_world_navigation.launch :

        有两处有根据自己修改:

<arg name="world_name" value="house_world_as"/> 的house_world_as要改成自己的文件名。

<node name="map_server" pkg="map_server" type="map_server" args="$(find racecar_gazebo)/map/house .yaml" />要house.yaml文件的路径要根据自己保存的修改,这个文件就是(5)保存地图的文件。

  1. <?xml version="1.0"?>
  2. <launch>
  3. <!-- Launch the racecar -->
  4. <include file="$(find racecar_gazebo)/launch/racecar.launch">
  5. <arg name="world_name" value="house_world_as"/>
  6. </include>
  7. <!-- Launch the built-map -->
  8. <node name="map_server" pkg="map_server" type="map_server" args="$(find racecar_gazebo)/map/house .yaml" />
  9. <!--Launch the move base with time elastic band-->
  10. <param name="/use_sim_time" value="true"/>
  11. <node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen">
  12. <rosparam file="$(find racecar_gazebo)/config/costmap_common_params.yaml" command="load" ns="global_costmap" />
  13. <rosparam file="$(find racecar_gazebo)/config/costmap_common_params.yaml" command="load" ns="local_costmap" />
  14. <rosparam file="$(find racecar_gazebo)/config/local_costmap_params.yaml" command="load" />
  15. <rosparam file="$(find racecar_gazebo)/config/global_costmap_params.yaml" command="load" />
  16. <rosparam file="$(find racecar_gazebo)/config/teb_local_planner_params.yaml" command="load" />
  17. <param name="base_global_planner" value="global_planner/GlobalPlanner" />
  18. <param name="planner_frequency" value="0.01" />
  19. <param name="planner_patience" value="5.0" />
  20. <!--param name="use_dijkstra" value="false" /-->
  21. <param name="base_local_planner" value="teb_local_planner/TebLocalPlannerROS" />
  22. <param name="controller_frequency" value="5.0" />
  23. <param name="controller_patience" value="15.0" />
  24. <param name="clearing_rotation_allowed" value="false" />
  25. </node>
  26. </launch>

        racecar_rviz.launch :

  1. <launch>
  2. <!-- 启动rviz -->
  3. <node pkg="rviz" type="rviz" name="rviz" args="-d $(find racecar_gazebo)/config/racecar_rviz.rviz"/>
  4. </launch>

         因为这个rviz配置是我所下载的这个仿真环境自带有的,如果使用的不是我这个仿真环境需要自己写一个rviz配置文件,并将上面的racecar_rviz.launch文件的路径改为这个rviz文件的路径。。

        racecar_rviz.rviz :

  1. Panels:
  2. - Class: rviz/Displays
  3. Help Height: 78
  4. Name: Displays
  5. Property Tree Widget:
  6. Expanded:
  7. - /Global Options1
  8. - /Status1
  9. - /LaserScan1
  10. - /PoseArray1
  11. Splitter Ratio: 0.5
  12. Tree Height: 775
  13. - Class: rviz/Selection
  14. Name: Selection
  15. - Class: rviz/Tool Properties
  16. Expanded:
  17. - /2D Pose Estimate1
  18. - /2D Nav Goal1
  19. - /Publish Point1
  20. Name: Tool Properties
  21. Splitter Ratio: 0.588679016
  22. - Class: rviz/Views
  23. Expanded:
  24. - /Current View1
  25. Name: Views
  26. Splitter Ratio: 0.5
  27. - Class: rviz/Time
  28. Experimental: false
  29. Name: Time
  30. SyncMode: 0
  31. SyncSource: LaserScan
  32. Toolbars:
  33. toolButtonStyle: 2
  34. Visualization Manager:
  35. Class: ""
  36. Displays:
  37. - Alpha: 0.5
  38. Cell Size: 1
  39. Class: rviz/Grid
  40. Color: 160; 160; 164
  41. Enabled: true
  42. Line Style:
  43. Line Width: 0.0299999993
  44. Value: Lines
  45. Name: Grid
  46. Normal Cell Count: 0
  47. Offset:
  48. X: 0
  49. Y: 0
  50. Z: 0
  51. Plane: XY
  52. Plane Cell Count: 10
  53. Reference Frame: <Fixed Frame>
  54. Value: true
  55. - Alpha: 0.699999988
  56. Class: rviz/Map
  57. Color Scheme: map
  58. Draw Behind: false
  59. Enabled: true
  60. Name: Map
  61. Topic: /map
  62. Unreliable: false
  63. Use Timestamp: false
  64. Value: true
  65. - Alpha: 1
  66. Axes Length: 1
  67. Axes Radius: 0.100000001
  68. Class: rviz/Pose
  69. Color: 255; 25; 0
  70. Enabled: true
  71. Head Length: 0.300000012
  72. Head Radius: 0.100000001
  73. Name: Pose
  74. Shaft Length: 1
  75. Shaft Radius: 0.0500000007
  76. Shape: Arrow
  77. Topic: /move_base_simple/goal
  78. Unreliable: false
  79. Value: true
  80. - Angle Tolerance: 0.100000001
  81. Class: rviz/Odometry
  82. Covariance:
  83. Orientation:
  84. Alpha: 0.5
  85. Color: 255; 255; 127
  86. Color Style: Unique
  87. Frame: Local
  88. Offset: 1
  89. Scale: 1
  90. Value: true
  91. Position:
  92. Alpha: 0.300000012
  93. Color: 204; 51; 204
  94. Scale: 1
  95. Value: true
  96. Value: true
  97. Enabled: true
  98. Keep: 100
  99. Name: Odometry
  100. Position Tolerance: 0.100000001
  101. Shape:
  102. Alpha: 1
  103. Axes Length: 1
  104. Axes Radius: 0.100000001
  105. Color: 255; 25; 0
  106. Head Length: 0.300000012
  107. Head Radius: 0.100000001
  108. Shaft Length: 1
  109. Shaft Radius: 0.0500000007
  110. Value: Arrow
  111. Topic: /vesc/odom
  112. Unreliable: false
  113. Value: true
  114. - Alpha: 1
  115. Autocompute Intensity Bounds: true
  116. Autocompute Value Bounds:
  117. Max Value: 10
  118. Min Value: -10
  119. Value: true
  120. Axis: Z
  121. Channel Name: intensity
  122. Class: rviz/LaserScan
  123. Color: 255; 255; 255
  124. Color Transformer: Intensity
  125. Decay Time: 0
  126. Enabled: true
  127. Invert Rainbow: false
  128. Max Color: 255; 255; 255
  129. Max Intensity: 0
  130. Min Color: 0; 0; 0
  131. Min Intensity: 0
  132. Name: LaserScan
  133. Position Transformer: XYZ
  134. Queue Size: 10
  135. Selectable: true
  136. Size (Pixels): 3
  137. Size (m): 0.100000001
  138. Style: Flat Squares
  139. Topic: /scan
  140. Unreliable: false
  141. Use Fixed Frame: true
  142. Use rainbow: true
  143. Value: true
  144. - Alpha: 1
  145. Buffer Length: 1
  146. Class: rviz/Path
  147. Color: 25; 255; 0
  148. Enabled: true
  149. Head Diameter: 0.300000012
  150. Head Length: 0.200000003
  151. Length: 0.300000012
  152. Line Style: Lines
  153. Line Width: 0.0299999993
  154. Name: Path
  155. Offset:
  156. X: 0
  157. Y: 0
  158. Z: 0
  159. Pose Color: 255; 85; 255
  160. Pose Style: None
  161. Radius: 0.0299999993
  162. Shaft Diameter: 0.100000001
  163. Shaft Length: 0.100000001
  164. Topic: /move_base/TebLocalPlannerROS/global_plan
  165. Unreliable: false
  166. Value: true
  167. - Alpha: 0.699999988
  168. Class: rviz/Map
  169. Color Scheme: map
  170. Draw Behind: false
  171. Enabled: true
  172. Name: Map
  173. Topic: /move_base/local_costmap/costmap
  174. Unreliable: false
  175. Use Timestamp: false
  176. Value: true
  177. - Alpha: 1
  178. Axes Length: 1
  179. Axes Radius: 0.100000001
  180. Class: rviz/Pose
  181. Color: 255; 25; 0
  182. Enabled: true
  183. Head Length: 0.300000012
  184. Head Radius: 0.100000001
  185. Name: Pose
  186. Shaft Length: 1
  187. Shaft Radius: 0.0500000007
  188. Shape: Arrow
  189. Topic: /move_base/current_goal
  190. Unreliable: false
  191. Value: true
  192. - Alpha: 1
  193. Arrow Length: 0.300000012
  194. Axes Length: 0.300000012
  195. Axes Radius: 0.00999999978
  196. Class: rviz/PoseArray
  197. Color: 255; 25; 0
  198. Enabled: true
  199. Head Length: 0.0700000003
  200. Head Radius: 0.0299999993
  201. Name: PoseArray
  202. Shaft Length: 0.230000004
  203. Shaft Radius: 0.00999999978
  204. Shape: Arrow (Flat)
  205. Topic: /move_base/TebLocalPlannerROS/teb_poses
  206. Unreliable: false
  207. Value: true
  208. Enabled: true
  209. Global Options:
  210. Background Color: 48; 48; 48
  211. Default Light: true
  212. Fixed Frame: map
  213. Frame Rate: 30
  214. Name: root
  215. Tools:
  216. - Class: rviz/Interact
  217. Hide Inactive Objects: true
  218. - Class: rviz/MoveCamera
  219. - Class: rviz/Select
  220. - Class: rviz/FocusCamera
  221. - Class: rviz/Measure
  222. - Class: rviz/SetInitialPose
  223. Topic: /initialpose
  224. - Class: rviz/SetGoal
  225. Topic: /move_base_simple/goal
  226. - Class: rviz/PublishPoint
  227. Single click: true
  228. Topic: /clicked_point
  229. Value: true
  230. Views:
  231. Current:
  232. Class: rviz/Orbit
  233. Distance: 4.41030741
  234. Enable Stereo Rendering:
  235. Stereo Eye Separation: 0.0599999987
  236. Stereo Focal Distance: 1
  237. Swap Stereo Eyes: false
  238. Value: false
  239. Focal Point:
  240. X: -0.749346972
  241. Y: -3.23726511
  242. Z: 2.81896186
  243. Focal Shape Fixed Size: true
  244. Focal Shape Size: 0.0500000007
  245. Invert Z Axis: false
  246. Name: Current View
  247. Near Clip Distance: 0.00999999978
  248. Pitch: 1.13039768
  249. Target Frame: <Fixed Frame>
  250. Value: Orbit (rviz)
  251. Yaw: 6.17858267
  252. Saved: ~
  253. Window Geometry:
  254. Displays:
  255. collapsed: false
  256. Height: 1056
  257. Hide Left Dock: false
  258. Hide Right Dock: false
  259. QMainWindow State: 000000ff00000000fd00000004000000000000016a00000396fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000006100fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000002800000396000000d700fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f00000396fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000002800000396000000ad00fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e100000197000000030000073f0000003efc0100000002fb0000000800540069006d006501000000000000073f0000030000fffffffb0000000800540069006d00650100000000000004500000000000000000000004ba0000039600000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
  260. Selection:
  261. collapsed: false
  262. Time:
  263. collapsed: false
  264. Tool Properties:
  265. collapsed: false
  266. Views:
  267. collapsed: false
  268. Width: 1855
  269. X: 65
  270. Y: 24

        最后的脚本文件,用来让机器人根据规划好的路径移动到终点。

        path_pursuit.py :

  1. #!/usr/bin/env python
  2. import rospy
  3. from nav_msgs.msg import Path, Odometry
  4. from ackermann_msgs.msg import AckermannDriveStamped
  5. from geometry_msgs.msg import PoseStamped, PoseArray
  6. import math
  7. import numpy as np
  8. from numpy import linalg as LA
  9. from tf.transformations import euler_from_quaternion, quaternion_from_euler
  10. import csv
  11. import os
  12. class following_path:
  13. def __init__(self):
  14. self.current_pose = rospy.Subscriber('/pf/pose/odom', Odometry, self.callback_read_current_position, queue_size=1)
  15. self.Pose = []
  16. self.path_pose = rospy.Subscriber('/move_base/TebLocalPlannerROS/global_plan', Path, self.callback_read_path, queue_size=1)
  17. self.path_info = []
  18. self.Goal = []
  19. self.navigation_input = rospy.Publisher('/vesc/low_level/ackermann_cmd_mux/input/navigation', AckermannDriveStamped, queue_size=1)
  20. self.reach_goal = False
  21. self.MAX_VELOCITY = 0.5
  22. self.MIN_VELOCITY = 0
  23. self.max_angle = 1
  24. self.steering_velocity = 1
  25. self.jerk = 0.0
  26. self.acceleration = 0.0
  27. self.LOOKAHEAD_DISTANCE = 0.4
  28. self.Low_Speed_Mode = False
  29. def callback_read_path(self, data):
  30. # Organize the pose message and only ask for (x,y) and orientation
  31. # Read the Real time pose message and load them into path_info
  32. self.path_info = []
  33. path_array = data.poses
  34. for path_pose in path_array:
  35. path_x = path_pose.pose.position.x
  36. path_y = path_pose.pose.position.y
  37. path_qx = path_pose.pose.orientation.x
  38. path_qy = path_pose.pose.orientation.y
  39. path_qz = path_pose.pose.orientation.z
  40. path_qw = path_pose.pose.orientation.w
  41. path_quaternion = (path_qx, path_qy, path_qz, path_qw)
  42. path_euler = euler_from_quaternion(path_quaternion)
  43. path_yaw = path_euler[2]
  44. self.path_info.append([float(path_x), float(path_y), float(path_yaw)])
  45. self.Goal = list(self.path_info[-1]) # Set the last pose of the global path as goal location
  46. def callback_read_current_position(self, data):
  47. if self.reach_goal: # Stop updating the information.
  48. self.path_info = []
  49. self.Pose = []
  50. ackermann_control = AckermannDriveStamped()
  51. ackermann_control.drive.speed = 0.0
  52. ackermann_control.drive.steering_angle = 0.0
  53. ackermann_control.drive.steering_angle_velocity = 0.0
  54. if not len(self.path_info) == 0:
  55. # Read the path information to path_point list
  56. path_points_x = [float(point[0]) for point in self.path_info]
  57. path_points_y = [float(point[1]) for point in self.path_info]
  58. path_points_w = [float(point[2]) for point in self.path_info]
  59. # Read the current pose of the car from particle filter
  60. x = data.pose.pose.position.x
  61. y = data.pose.pose.position.y
  62. qx = data.pose.pose.orientation.x
  63. qy = data.pose.pose.orientation.y
  64. qz = data.pose.pose.orientation.z
  65. qw = data.pose.pose.orientation.w
  66. # Convert the quaternion angle to eular angle
  67. quaternion = (qx,qy,qz,qw)
  68. euler = euler_from_quaternion(quaternion)
  69. yaw = euler[2]
  70. self.Pose = [float(x), float(y), float(yaw)]
  71. if self.dist(self.Goal, self.Pose) < 1.0:
  72. self.Low_Speed_Mode = True
  73. if self.dist(self.Goal, self.Pose) < 0.3:
  74. self.reach_goal = True
  75. print('Goal Reached!')
  76. else:
  77. print('Low Speed Mode ON!')
  78. else:
  79. self.Low_Speed_Mode = False
  80. # 2. Find the path point closest to the vehichle tat is >= 1 lookahead distance from vehicle's current location.
  81. dist_array = np.zeros(len(path_points_x))
  82. for i in range(len(path_points_x)):
  83. dist_array[i] = self.dist((path_points_x[i], path_points_y[i]), (x,y))
  84. goal = np.argmin(dist_array) # Assume the closet point as the goal point at first
  85. goal_array = np.where((dist_array < (self.LOOKAHEAD_DISTANCE + 0.3)) & (dist_array > (self.LOOKAHEAD_DISTANCE - 0.3)))[0]
  86. for id in goal_array:
  87. v1 = [path_points_x[id] - x, path_points_y[id] - y]
  88. v2 = [math.cos(yaw), math.sin(yaw)]
  89. diff_angle = self.find_angle(v1,v2)
  90. if abs(diff_angle) < np.pi/4: # Check if the one that is the cloest to the lookahead direction
  91. goal = id
  92. break
  93. L = dist_array[goal]
  94. # 3. Transform the goal point to vehicle coordinates.
  95. glob_x = path_points_x[goal] - x
  96. glob_y = path_points_y[goal] - y
  97. goal_x_veh_coord = glob_x*np.cos(yaw) + glob_y*np.sin(yaw)
  98. goal_y_veh_coord = glob_y*np.cos(yaw) - glob_x*np.sin(yaw)
  99. # 4. Calculate the curvature = 1/r = 2x/l^2
  100. # The curvature is transformed into steering wheel angle by the vehicle on board controller.
  101. # Hint: You may need to flip to negative because for the VESC a right steering angle has a negative value.
  102. diff_angle = path_points_w[goal] - yaw # Find the turning angle
  103. r = L/(2*math.sin(diff_angle)) # Calculate the turning radius
  104. angle = 2 * math.atan(0.4/r) # Find the wheel turning radius
  105. angle = np.clip(angle, -self.max_angle, self.max_angle) # 0.4189 radians = 24 degrees because car can only turn 24 degrees max
  106. angle = (0 if abs(angle) < 0.1 else angle)
  107. VELOCITY = self.speed_control(angle, self.MIN_VELOCITY, self.MAX_VELOCITY)
  108. # Write the Velocity and angle data into the ackermann message
  109. ackermann_control = AckermannDriveStamped()
  110. ackermann_control.drive.speed = VELOCITY
  111. ackermann_control.drive.steering_angle = angle
  112. ackermann_control.drive.steering_angle_velocity = self.steering_velocity
  113. else:
  114. ackermann_control = AckermannDriveStamped()
  115. ackermann_control.drive.speed = 0.0
  116. ackermann_control.drive.steering_angle = 0.0
  117. ackermann_control.drive.steering_angle_velocity = 0.0
  118. self.navigation_input.publish(ackermann_control)
  119. # Computes the Euclidean distance between two 2D points p1 and p2
  120. def dist(self, p1, p2):
  121. try:
  122. return np.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)
  123. except:
  124. return 0.5
  125. # Compute the angle between car direction and goal direction
  126. def find_angle(self, v1, v2):
  127. cos_ang = np.dot(v1, v2)
  128. sin_ang = LA.norm(np.cross(v1, v2))
  129. return np.arctan2(sin_ang, cos_ang)
  130. # Control the speed of the car within the speed limit
  131. def speed_control(self, angle, MIN_VELOCITY, MAX_VELOCITY):
  132. # Assume the speed change linearly with respect to yaw angle
  133. if self.Low_Speed_Mode:
  134. Velocity = 0.5
  135. else:
  136. k = (MIN_VELOCITY - MAX_VELOCITY)/self.max_angle + 0.5
  137. Velocity = k * abs(angle) + MAX_VELOCITY
  138. return Velocity
  139. if __name__ == "__main__":
  140. rospy.init_node("pursuit_path")
  141. following_path()
  142. rospy.spin()

        都完成后打开新终端,启动导航的launch文件:

  1. (base) hjb@jl16k:~$ cd catkin_gazebo_ws/
  2. (base) hjb@jl16k:~/catkin_gazebo_ws$ source devel/setup.bash
  3. (base) hjb@jl16k:~/catkin_gazebo_ws$ roslaunch racecar_gazebo racecar_house_and_car_world_navigation.launch

 

        再打开一个终端,启动rivz: 

  1. (base) hjb@jl16k:~$ cd catkin_gazebo_ws/
  2. (base) hjb@jl16k:~/catkin_gazebo_ws$ source devel/setup.bash
  3. (base) hjb@jl16k:~/catkin_gazebo_ws$ roslaunch racecar_gazebo racecar_rviz.launch

        在rviz中点击下图箭头所指的2D Nav Goal,然后将箭头方向拉成像我一样的 ,当然其他方向也ok的。就会看到算法所得到的路径,绿色那条线。 

         再打开一个新终端:

  1. (base) hjb@jl16k:~$ cd catkin_gazebo_ws/
  2. (base) hjb@jl16k:~/catkin_gazebo_ws$ source devel/setup.bash
  3. (base) hjb@jl16k:~/catkin_gazebo_ws$ rosrun racecar_gazebo path_pursuit.py

 

 3.结束

        一次完整的仿真。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/359217
推荐阅读
相关标签
  

闽ICP备14008679号