ros 雷达 slam 导航 文件分析

ros 雷达 slam 导航 文件分析

    • robot_slam_laser.launch
      • robot_lidar.launch
      • lidar.launch
      • raplidar.launch
      • karto.launch
      • gmapping.launch
      • cartographer.launch
    • robot_navigation.launch
      • map.yaml
      • map.pgm
      • amcl_params.yaml
      • move_base.launch
      • costmap_common_params.yaml
      • local_costmap_params.yaml
      • global_costmap_params.yaml
      • move_base_params.yaml:
      • dwa_local_planner_params.yaml
      • teb_local_planner_params.yaml
    • 多点导航
      • multi_points_navigation.launch
      • multi_navigation.launch
      • multi_goal_point.py:
    • 多点全自动巡航
      • way_point.launch
      • multi_goal_point.py

lidar文件夹下面有多个雷达功能包。

还包括一个udev规则转换脚本。雷达使用cp2102usb转串口芯片。

多个设备通过usb连主机,很难分辨各个端口名所对应的设备。

给USB 转串口的芯片设定规则,根据芯片的 Vender ID 和 Product ID 进行筛选。读取到芯片后做一个符号链接,链接到对应端口。取名为rplidar等。

initenv.sh
#!/bin/bash
echo  'KERNEL=="ttyUSB*", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", MODE:="0777", SYMLINK+="hldslidar"' >/etc/udev/rules.d/hldslidar.rulesecho  'KERNEL=="ttyUSB*", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", MODE:="0777", GROUP:="dialout",  SYMLINK+="ydlidar"' >/etc/udev/rules.d/ydlidar.rulesecho  'KERNEL=="ttyUSB*", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", MODE:="0777", GROUP:="dialout",  SYMLINK+="iiilidar"' >/etc/udev/rules.d/iiilidar.rulesecho  'KERNEL=="ttyUSB*", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", MODE:="0777", GROUP:="dialout",  SYMLINK+="rplidar"' >/etc/udev/rules.d/rplidar.rulesecho  'KERNEL=="ttyUSB*", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", MODE:="0777", GROUP:="dialout",  SYMLINK+="sclidar"' >/etc/udev/rules.d/sclidar.rulessystemctl daemon-reload
service udev reload
sleep 1
service udev restart

robot_navigation这个文件夹很重要。

robot:
roslaunch robot_navigation lidar.launch  启动雷达
PC:
roslaunch robot_navigation lidar_rviz.launch  运行rviz工具robot:
roslaunch robot_navigation robot_slam_laser.launch  激光雷达slam建图
pc:
roslaunch robot_navigation slam_rviz.launchrobot:保存地图
roscd robot_navigation/maps
rosrun map_server map_saver -f maprobot:
roslaunch robot_navigation robot_navigation.launch   导航和避障
pc:
roslaunch robot_navigation navigation_rviz.launch  robot:
roslaunch robot_navigation robot_slam_laser.launch slam_methods:=hector 建图算法切换
pc:
roslaunch robot_navigation navigation_rviz.launchrobot:
roslaunch robot_navigation robot_navigation.launch planner:=dwa 路径规划算法切换robot:
roslaunch robot_navigation robot_slam_laser.launch planner:=teb 导航建图
pc:
roslaunch robot_navigation slam_rviz.launch

文件里面包含:

rviz文件夹:rviz相关配置文件
maps文件夹:地图文件和地图配置信息相关的文件
param文件夹:yaml文件
launch文件夹
script文件夹:脚本文件

launch文件夹里面:

includes文件夹:建图算法的launch文件
lidar文件夹:雷达的launch文件
rviz文件夹:rviz的launch文件

robot_slam_laser.launch

robot_slam_laser.launch:

这里面主要就是launch文件的循环调用,比如,通过robot_slam_laser.launch文件调用了robot_lidar.launch文件。

这样做的好处是具有模块化。可以避免重复启动多个节点。

<launch><!-- Arguments --><arg name="slam_methods" default="gmapping" doc="slam type [gmapping, hector, karto, cartographer]"/><!-- 可传入参数slam_methods,选择SLAM建图算法 --><arg name="open_rviz" default="false"/><arg name="simulation" default= "false"/> <!-- 判断启动实体机器人还是 stage 仿真器 --><arg name="planner"  default="" doc="opt: dwa, teb"/> <!-- 无地图环境下边建图边导航,可选参数为dwa、teb局部路径规划器 --><param name="/use_sim_time" value="$(arg simulation)" />  <!-- simulation robot with lidar and map --><!-- simulation为true 时启动仿真软件运行仿真机器人--><group if="$(arg simulation)"><include file="$(find robot_simulation)/launch/simulation_one_robot.launch"/></group><!-- robot with lidar --><!--simulation为false 启动launch文件夹下的robot_lidar.launch--><group unless="$(arg simulation)"><include file="$(find robot_navigation)/launch/robot_lidar.launch"/></group><!-- SLAM: Gmapping, Cartographer, Hector, Karto --><!-- 根据 slam_methods 参数选用不同的建图算法 --><include file="$(find robot_navigation)/launch/includes/$(arg slam_methods).launch"><arg name="simulation"            value="$(arg simulation)"/></include><!-- move_base 无地图环境下导航建图功能,传入planner参数为dwa或teb,就会启动导航堆栈--><group unless="$(eval planner == '')"><!--启动move_base.launch--><include file="$(find robot_navigation)/launch/move_base.launch" unless="$(eval planner == '')"><arg name="planner"            value="$(arg planner)"/></include></group><!--因为gmapping会发布map话题,这里不用启动地图服务器从本地读取地图也可以正常导航--><!-- rviz open_rviz 参数为 true时,打开rviz--><group if="$(arg open_rviz)"> <node pkg="rviz" type="rviz" name="rviz" required="true"args="-d $(find robot_navigation)/rviz/slam.rviz"/><!-- 使用rviz文件夹中预置的配置文件slam.rviz --></group></launch>

robot_lidar.launch

robot_lidar.launch:启动底盘和雷达这两个硬件

<launch><!-- config param --><arg name="pub_imu"       default="False" /><arg name="sub_ackermann"       default="False" /><arg name="lidar_frame" default="base_laser_link"/>  <!-- include 标签包含了两个 launch --><include file="$(find base_control)/launch/base_control.launch"><arg name="pub_imu"            value="$(arg pub_imu)"/>  <arg name="sub_ackermann"            value="$(arg sub_ackermann)"/>  </include><include file="$(find robot_navigation)/launch/lidar.launch"><arg name="lidar_frame"            value="$(arg lidar_frame)"/>  </include></launch>

lidar.launch

lidar.launch:这里面启动了雷达的launch文件

<launch><!--robot bast type use different tf value--><!-- 读取机器人底盘类型 --><arg name="base_type"       default="$(env BASE_TYPE)" /><!-- robot frame --><arg name="base_frame"       default="/base_footprint" />    <!-- 读取机器人雷达类型 --><arg name="lidar_type"       default="$(env LIDAR_TYPE)" />   <arg name="lidar_frame" default="base_laser_link"/>  <!-- 根据lidar_type类型启动节点,如果是rplidar,就启动rplidar.launch --><include file="$(find robot_navigation)/launch/lidar/$(arg lidar_type).launch"><arg name="lidar_frame"            value="$(arg lidar_frame)"/></include><!-- TF 坐标转换 --><group if="$(eval base_type == 'NanoRobot')"><node pkg="tf" type="static_transform_publisher" name="base_footprint_to_laser"args="-0.01225 0.0 0.18 3.14159265 0.0 0.0 $(arg base_frame) $(arg lidar_frame) 20"></node></group><group if="$(eval base_type == 'NanoRobot_Pro')"><node pkg="tf" type="static_transform_publisher" name="base_footprint_to_laser"args="-0.0515 0.0 0.18 -1.5708 0.0 0.0 $(arg base_frame) $(arg lidar_frame) 20"></node></group><group if="$(eval base_type == '4WD')"><node pkg="tf" type="static_transform_publisher" name="base_footprint_to_laser"args="0.01 0.0 0.25 3.14159265 0.0 0.0 $(arg base_frame) $(arg lidar_frame) 20"></node></group><group if="$(eval base_type == '4WD_OMNI')"><node pkg="tf" type="static_transform_publisher" name="base_footprint_to_laser"args="0.01 0.0 0.25 3.14159265 0.0 0.0 $(arg base_frame) $(arg lidar_frame) 20"></node></group><group if="$(eval base_type == 'NanoCar')"><node pkg="tf" type="static_transform_publisher" name="base_footprint_to_laser"args="0.1037 0.0 0.115 3.14159265 0.0 0.0 $(arg base_frame) $(arg lidar_frame) 20"></node></group><group if="$(eval base_type == 'NanoCar_Pro')"><node pkg="tf" type="static_transform_publisher" name="base_footprint_to_laser"args="0.1037 0.0 0.165 3.14159265 0.0 0.0 $(arg base_frame) $(arg lidar_frame) 20"></node></group><group if="$(eval base_type == 'NanoCar_SE')"><node pkg="tf" type="static_transform_publisher" name="base_footprint_to_laser"args="0.0955 0.0 0.115 1.5708 0.0 0.0 $(arg base_frame) $(arg lidar_frame) 20"></node></group></launch>

raplidar.launch

raplidar.launch 雷达数据

端口名称使用了udev规则的名称。

<param name="serial_port"         type="string" value="/dev/rplidar"/>
<launch><arg name="lidar_frame" default="lidar"/>   <node name="rplidarNode"          pkg="rplidar_ros"  type="rplidarNode" output="screen"><param name="serial_port"         type="string" value="/dev/rplidar"/><param name="serial_baudrate"     type="int"    value="115200"/><!--A1/A2 --><!--param name="serial_baudrate"     type="int"    value="256000"--><!--A3 --><param name="frame_id"            type="string" value="$(arg lidar_frame)"/><param name="inverted"            type="bool"   value="false"/><param name="angle_compensate"    type="bool"   value="true"/></node>
</launch>

karto.launch

karto.launch文件,配置参数是通过yaml文件载入的。

<launch><arg name="simulation" default= "false"/> <param name="/use_sim_time" value="$(arg simulation)" />  <!-- slam_karto --><node pkg="slam_karto" type="slam_karto" name="slam_karto" output="screen"><rosparam command="load" file="$(find robot_navigation)/config/karto_params.yaml" /></node></launch>

karto_params.yaml

# General Parametersbase_frame: base_footprintmap_update_interval: 5use_scan_matching: trueuse_scan_barycenter: trueminimum_travel_distance: 0.12 minimum_travel_heading: 0.174             #in radiansscan_buffer_size: 70scan_buffer_maximum_scan_distance: 3.5link_match_minimum_response_fine: 0.12link_scan_maximum_distance: 3.5loop_search_maximum_distance: 3.5do_loop_closing: trueloop_match_minimum_chain_size: 10loop_match_maximum_variance_coarse: 0.4   # gets squared laterloop_match_minimum_response_coarse: 0.8loop_match_minimum_response_fine: 0.8# Correlation Parameters - Correlation Parameterscorrelation_search_space_dimension: 0.3correlation_search_space_resolution: 0.01correlation_search_space_smear_deviation: 0.03# Correlation Parameters - Loop Closure Parametersloop_search_space_dimension: 8.0loop_search_space_resolution: 0.05loop_search_space_smear_deviation: 0.03# Scan Matcher Parametersdistance_variance_penalty: 0.3      # gets squared laterangle_variance_penalty: 0.349       # in degrees (gets converted to radians then squared)fine_search_angle_offset: 0.00349   # in degrees (gets converted to radians)coarse_search_angle_offset: 0.349   # in degrees (gets converted to radians)coarse_angle_resolution: 0.0349     # in degrees (gets converted to radians)minimum_angle_penalty: 0.9minimum_distance_penalty: 0.5use_response_expansion: false

与上面对比的话,就是通过launch文件直接传参数。他们两个效果其实是一样的。

gmapping.launch

<launch><arg name="set_base_frame" default="base_footprint"/><arg name="set_odom_frame" default="odom"/><arg name="set_map_frame"  default="map"/><arg name="scan_topic" default="/scan" /><arg name="simulation" default= "false"/> <param name="/use_sim_time" value="$(arg simulation)" />  <!-- Gmapping --><node pkg="gmapping" type="slam_gmapping" name="gmapping" output="screen"><param name="base_frame" value="$(arg set_base_frame)"/><param name="odom_frame" value="$(arg set_odom_frame)"/><param name="map_frame"  value="$(arg set_map_frame)"/><param name="map_update_interval" value="2.0"/><param name="maxUrange" value="5.0"/><param name="sigma" value="0.05"/><param name="kernelSize" value="1"/><param name="lstep" value="0.05"/><param name="astep" value="0.05"/><param name="iterations" value="5"/><param name="lsigma" value="0.075"/><param name="ogain" value="3.0"/><param name="lskip" value="0"/><param name="minimumScore" value="50"/><param name="srr" value="0.1"/><param name="srt" value="0.2"/><param name="str" value="0.1"/><param name="stt" value="0.2"/><param name="linearUpdate" value="1.0"/><param name="angularUpdate" value="0.2"/><param name="temporalUpdate" value="0.5"/><param name="resampleThreshold" value="0.5"/><param name="particles" value="100"/><param name="xmin" value="-10.0"/><param name="ymin" value="-10.0"/><param name="xmax" value="10.0"/><param name="ymax" value="10.0"/><param name="delta" value="0.05"/><param name="llsamplerange" value="0.01"/><param name="llsamplestep" value="0.01"/><param name="lasamplerange" value="0.005"/><param name="lasamplestep" value="0.005"/><remap from="scan" to="$(arg scan_topic)"/></node>
</launch>

cartographer.launch

它里面使用了lua脚本语言载入配置文件。

-configuration_basename revo_lds_$(arg version).lua" 

revo_lds_melodic.lua

include "map_builder.lua"
include "trajectory_builder.lua"options = {map_builder = MAP_BUILDER,trajectory_builder = TRAJECTORY_BUILDER,map_frame = "map",
-- tracking_frame = "base_laser_link",
-- published_frame = "base_laser_link",
-- odom_frame = "odom",
-- provide_odom_frame = true,
-- use_odometry = false,tracking_frame = "base_footprint",published_frame = "odom",odom_frame = "odom",provide_odom_frame = false,publish_frame_projected_to_2d = false,use_odometry = true,use_nav_sat = false,use_landmarks = false,num_laser_scans = 1,num_multi_echo_laser_scans = 0,num_subdivisions_per_laser_scan = 1,num_point_clouds = 0,lookup_transform_timeout_sec = 0.2,submap_publish_period_sec = 0.3,pose_publish_period_sec = 5e-3,trajectory_publish_period_sec = 30e-3,rangefinder_sampling_ratio = 1.,odometry_sampling_ratio = 1.,fixed_frame_pose_sampling_ratio = 1.,imu_sampling_ratio = 1.,landmarks_sampling_ratio = 1.,
}MAP_BUILDER.use_trajectory_builder_2d = trueTRAJECTORY_BUILDER_2D.submaps.num_range_data = 35
TRAJECTORY_BUILDER_2D.min_range = 0.3
TRAJECTORY_BUILDER_2D.max_range = 8.
TRAJECTORY_BUILDER_2D.missing_data_ray_length = 1.
TRAJECTORY_BUILDER_2D.use_imu_data = false
TRAJECTORY_BUILDER_2D.use_online_correlative_scan_matching = true
TRAJECTORY_BUILDER_2D.real_time_correlative_scan_matcher.linear_search_window = 0.1
TRAJECTORY_BUILDER_2D.real_time_correlative_scan_matcher.translation_delta_cost_weight = 10.
TRAJECTORY_BUILDER_2D.real_time_correlative_scan_matcher.rotation_delta_cost_weight = 1e-1POSE_GRAPH.optimization_problem.huber_scale = 1e2
POSE_GRAPH.optimize_every_n_nodes = 35
POSE_GRAPH.constraint_builder.min_score = 0.65return options

cartographer.launch

<launch>  <arg name="simulation" default= "true"/> <arg name="version" default="$(env ROS_DISTRO)"/><!-- 读取环境变量ROS_DISTRO的版本 --><param name="/use_sim_time" value="$(arg simulation)" />  <!-- cartographer_node --><node name="cartographer_node" pkg="cartographer_ros"  type="cartographer_node" args="  -configuration_directory $(find robot_navigation)/config  -configuration_basename revo_lds_$(arg version).lua"  output="screen">  <!--根据version的参数选择配置文件--></node>  <group if="$(eval version == 'melodic')"><!--对于melodic版本,手动启动下面这个节点--><node pkg="cartographer_ros" type="cartographer_occupancy_grid_node" name="cartographer_occupancy_grid_node"/></group>
</launch>

robot_navigation.launch

两个group,每个都,实现了机器人、雷达、地图、amcl四个节点的启动。

<launch><!-- Arguments --><!--传入地图文件所在的路径--><arg name="map_file" default="$(find robot_navigation)/maps/map.yaml"/><!--参数map_file传入maps文件下的map.yaml文件 --><!--如果地图名称和保存路径是自定义的,只需修改传入的map_file的值--><arg name="simulation" default= "false"/> <arg name="planner"  default="teb" doc="opt: dwa, teb"/> <arg name="open_rviz" default="false"/><arg name="use_dijkstra" default= "true"/> <group if="$(arg simulation)"><!-- simulation robot with lidar and map--><!-- simulation 参数为 true,在那个launch文件中启动地图服务 --><include file="$(find robot_simulation)/launch/simulation_one_robot_with_map.launch"/></group><group unless="$(arg simulation)"><!-- robot with lidar 启动底盘和雷达--><include file="$(find robot_navigation)/launch/robot_lidar.launch"><!--<arg name="robot_name"            value="$(arg robot_name)"/>--></include><!-- Map server 启动地图服务器读入map_file参数获取地图文件--><node pkg="map_server" name="map_server" type="map_server" args="$(arg map_file)"><param name="frame_id" value="map"/></node><!-- AMCL,启动一个定位节点 amcl --><node pkg="amcl" type="amcl" name="amcl" output="screen"><!-- 通过yaml文件读取配置文件--><rosparam file="$(find robot_navigation)/param/$(env BASE_TYPE)/amcl_params.yaml" command="load" /><!-- 通过下面的设置,机器人启动时候默认就是位于地图零点 --><!-- 如果机器人从一个固定位置启动且不是原点,可以修改参数 --><param name="initial_pose_x"            value="0.0"/><param name="initial_pose_y"            value="0.0"/><param name="initial_pose_a"            value="0.0"/><!--航向--></node></group><!-- move_base 导航堆栈--><include file="$(find robot_navigation)/launch/move_base.launch" ><!--传入的参数--><arg name="planner"            value="$(arg planner)"/><arg name="simulation"            value="$(arg simulation)"/><arg name="use_dijkstra"     value="$(arg use_dijkstra)"/></include><!-- rviz --><group if="$(arg open_rviz)"> <node pkg="rviz" type="rviz" name="rviz" required="true"args="-d $(find robot_navigation)/rviz/navigation.rviz"/></group></launch>

map.yaml

包括了地图的一些基本配置信息。

image: map.pgm
resolution: 0.050000
origin: [-10.000000, -10.000000, 0.000000]
negate: 0
occupied_thresh: 0.65
free_thresh: 0.196

里面的参数含义:可以在维基里面找对应的功能包,有英文介绍里面的参数。

image:地图名称。
resolution:地图分辨率,米/像素
origin:地图中左下角像素的二维姿态,为x,y,yaw。偏航为逆时针旋转,偏航=0表示无旋转
occupied_thresh:大于此阈值的像素,完全占用。
free_thresh:小于此阈值的像素,完全空闲。
negate:
像素的COLOR值x在[0,255]范围内。
首先,将整数x转换为一个浮点数p,转换公式具体取决于yaml中negate标志的定义。
如果negate=0,则p =255 - x)/ 255.0。这意味着黑色(0)现在具有最高值(1.0)而白色(255)具有最低值(0.0)。
如果negate=1,则p = x / 255.0

map.pgm

pgm是一个图像的格式。

在这里插入图片描述

amcl_params.yaml

可以通过amcl的ros wiki介绍页面查看参数信息。

use_map_topic: trueodom_frame_id: "odom"base_frame_id: "base_footprint"global_frame_id: "map"## Publish scans from best pose at a max of 10 Hzodom_model_type: "diff"odom_alpha5: 0.1gui_publish_rate: 10.0laser_max_beams: 60laser_max_range: 12.0min_particles: 500max_particles: 2000kld_err: 0.05kld_z: 0.99odom_alpha1: 0.2odom_alpha2: 0.2## translation std dev, m odom_alpha3: 0.2odom_alpha4: 0.2laser_z_hit: 0.5aser_z_short: 0.05laser_z_max: 0.05laser_z_rand: 0.5laser_sigma_hit: 0.2laser_lambda_short: 0.1laser_model_type: "likelihood_field" # "likelihood_field" or "beam"laser_likelihood_max_dist: 2.0update_min_d: 0.25update_min_a: 0.2resample_interval: 1## Increase tolerance because the computer can get quite busy transform_tolerance: 1.0recovery_alpha_slow: 0.001recovery_alpha_fast: 0.1

move_base.launch

move_base.launch:ros下的导航堆栈

默认传入的 planner 参数是 dwa,但是robot_navigation.launch调用move_base.launch时,传入的参数是teb。所以这里planner参数置为teb。

<launch><!-- Arguments --><arg name="cmd_vel_topic" default="cmd_vel" /><arg name="odom_topic" default="odom" /><arg name="planner"  default="dwa" doc="opt: dwa, teb"/> <!--默认传入的 planner 参数是 dwa--><arg name="simulation" default= "false"/> <arg name="use_dijkstra" default= "true"/>  <arg name="base_type" default= "$(env BASE_TYPE)"/> <!--不同车型不同配置--><arg name="move_forward_only" default="false"/><!--使用深度相机导航和建图,只有前向视野没有后向视野--><!--禁止车的后向运行保证运行安全--><!-- 使用了两个 group 区分DWA和TEB两个不同的局部路径规划算法 --><!-- move_base use DWA planner--><group if="$(eval planner == 'dwa')"><!-- 启动move_base节点 --><node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen"><!-- use global_planner replace default navfn as global planner ,global_planner support A* and dijkstra algorithm--><!--设置传入节点的参数--><param name="base_global_planner" value="global_planner/GlobalPlanner"/><param name="base_local_planner" value="dwa_local_planner/DWAPlannerROS" /><!-- <rosparam file="$(find robot_navigation)/param/$(env BASE_TYPE)/global_planner_params.yaml" command="load" />       --><rosparam file="$(find robot_navigation)/param/$(env BASE_TYPE)/costmap_common_params.yaml" command="load" ns="global_costmap" /><rosparam file="$(find robot_navigation)/param/$(env BASE_TYPE)/costmap_common_params.yaml" command="load" ns="local_costmap" /><rosparam file="$(find robot_navigation)/param/$(env BASE_TYPE)/local_costmap_params.yaml" command="load" /><rosparam file="$(find robot_navigation)/param/$(env BASE_TYPE)/global_costmap_params.yaml" command="load" /><rosparam file="$(find robot_navigation)/param/$(env BASE_TYPE)/move_base_params.yaml" command="load" /><rosparam file="$(find robot_navigation)/param/$(env BASE_TYPE)/dwa_local_planner_params.yaml" command="load" /><remap from="cmd_vel" to="$(arg cmd_vel_topic)"/><remap from="odom" to="$(arg odom_topic)"/><!-- move_forward_only时,不让有负速度(后向运动) --><param name="DWAPlannerROS/min_vel_x" value="0.0" if="$(arg move_forward_only)" /><!--default is True,use dijkstra algorithm;set to False,usd A* algorithm--><param name="GlobalPlanner/use_dijkstra " value="$(arg use_dijkstra)" />    </node></group><!-- move_base use TEB planner--><group if="$(eval planner == 'teb')"><node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen"><!-- use global_planner replace default navfn as global planner ,global_planner support A* and dijkstra algorithm--><param name="base_global_planner" value="global_planner/GlobalPlanner"/><param name="base_local_planner" value="teb_local_planner/TebLocalPlannerROS" /><!-- <rosparam file="$(find robot_navigation)/param/$(env BASE_TYPE)/global_planner_params.yaml" command="load" /> --><rosparam file="$(find robot_navigation)/param/$(env BASE_TYPE)/costmap_common_params.yaml" command="load" ns="global_costmap" /><rosparam file="$(find robot_navigation)/param/$(env BASE_TYPE)/costmap_common_params.yaml" command="load" ns="local_costmap" /><rosparam file="$(find robot_navigation)/param/$(env BASE_TYPE)/local_costmap_params.yaml" command="load" /><rosparam file="$(find robot_navigation)/param/$(env BASE_TYPE)/global_costmap_params.yaml" command="load" /><rosparam file="$(find robot_navigation)/param/$(env BASE_TYPE)/move_base_params.yaml" command="load" /><rosparam file="$(find robot_navigation)/param/$(env BASE_TYPE)/teb_local_planner_params.yaml" command="load" /><remap from="cmd_vel" to="$(arg cmd_vel_topic)"/><remap from="odom" to="$(arg odom_topic)"/><param name="TebLocalPlannerROS/max_vel_x_backwards" value="0.0" if="$(arg move_forward_only)" /><!--default is True,use dijkstra algorithm;set to False,usd A* algorithm--><param name="GlobalPlanner/use_dijkstra " value="$(arg use_dijkstra)" /><!--stage simulator takes the angle instead of the rotvel as input (twist message)--></node></group><!-- move_base -->
</launch>

costmap_common_params.yaml

其中一个车型的costmap_common_params.yaml文件如下。

#---(in meters)---
#单位是米footprint: [ [-0.035,-0.1], [0.18,-0.1], [0.18,0.1], [-0.035,0.1] ]
#机器人的矩形轮廓尺寸
#以机器人中心为坐标轴原点,这四个坐标点(x,y)分别对应机器人的四个角
#路径规划器获取到机器人轮廓大小从而规划相应的路径#参数含义通过move_base的ros wiki查看
transform_tolerance: 0.2obstacle_layer:enabled: trueobstacle_range: 2.5raytrace_range: 3.0inflation_radius: 0.05track_unknown_space: falsecombination_method: 1observation_sources: laser_scan_sensorlaser_scan_sensor: {data_type: LaserScan, topic: scan, marking: true, clearing: true}inflation_layer:enabled:              truecost_scaling_factor:  10.0  # exponential rate at which the obstacle cost drops off (default: 10)inflation_radius:     0.1  # max. distance from an obstacle at which costs are incurred for planning paths.static_layer:enabled:              truemap_topic:            "/map"

local_costmap_params.yaml

机器人避障功能通过local costmap实现。全局地图不存在障碍,通过局部地图发现障碍然后重新规划路线实现。

#指定发布频率和更新频率
local_costmap:global_frame: maprobot_base_frame: base_footprintupdate_frequency: 5.0publish_frequency: 5.0rolling_window: true#滑动窗口#以机器人所在位置为中心的 3*3 滑动窗口#这个框设置得越小路径规划效果越差,越大机器人负担的运算就越大width: 3height: 3resolution: 0.05transform_tolerance: 0.5plugins:#设置了一个地图的层#静态层- {name: static_layer,        type: "costmap_2d::StaticLayer"}#障碍物层,地图中不存在而实际扫描到的障碍物,会被识别在障碍物层。- {name: obstacle_layer,      type: "costmap_2d::ObstacleLayer"}

global_costmap_params.yaml

#设置一些发布频率等信息
global_costmap:global_frame: maprobot_base_frame: base_footprintupdate_frequency: 1.0publish_frequency: 0.5# static_map: true transform_tolerance: 0.5plugins:- {name: static_layer,            type: "costmap_2d::StaticLayer"}- {name: obstacle_layer,          type: "costmap_2d::VoxelLayer"}- {name: inflation_layer,         type: "costmap_2d::InflationLayer"}

move_base_params.yaml:

#进行一些频率设置#对底盘的控制频率
controller_frequency: 2.0
controller_patience: 15.0#路径规划频率
planner_frequency: 0.2
planner_patience: 5.0conservative_reset_dist: 3.0#机器人在一个点持续震荡时最大允许震荡时间
oscillation_timeout: 10.0
oscillation_distance: 0.2clearing_rotation_allowed: false #clearing_rotation_allowed为True时,机器人被困住,会原地 360 度转一圈重新扫描找到一条出路(激光雷达不是360才需要用)
#阿克曼结构,不具备原地转向的能力,需要禁用

dwa_local_planner_params.yaml

DWAPlannerROS:# Robot Configuration Parameters
#设置机器人导航的最大最小速度
#x方向最大速度的绝对值,单位m/smax_vel_x: 0.25
#x方向最小值绝对值,如果为负值表示可以后退min_vel_x: -0.25
#y轴速度,麦克纳姆轮才用max_vel_y: 0.0min_vel_y: 0.0# The velocity when robot is moving in a straight line
#最大角速度,单位rad/smax_vel_trans:  0.26min_vel_trans:  0.13max_vel_theta: 1.0min_vel_theta: 0.5#x方向的加速度绝对值,m/s2acc_lim_x: 2.5
#y方向的加速度绝对值,该值只有全向移动的机器人才需配置.acc_lim_y: 0.0
#旋转加速度的绝对值.rad/s2acc_lim_theta: 3.2 # Goal Tolerance Parametes
#允许机器人所到目标的坐标(以米为单位)偏差,过小可能导致机器人在目标位置附近不断调整到精确的目标位置xy_goal_tolerance: 0.1
#角度的容忍yaw_goal_tolerance: 0.2latch_xy_goal_tolerance: false# Forward Simulation Parameterssim_time: 2.0vx_samples: 20vy_samples: 0vtheta_samples: 40controller_frequency: 10.0# Trajectory Scoring Parameterspath_distance_bias: 32.0goal_distance_bias: 20.0occdist_scale: 0.02forward_point_distance: 0.325stop_time_buffer: 0.2scaling_speed: 0.25max_scaling_factor: 0.2# Oscillation Prevention Parametersoscillation_reset_dist: 0.05# Debuggingpublish_traj_pc : truepublish_cost_grid_pc: trueprune_plan: falseuse_dwa: truerestore_defaults: false

teb_local_planner_params.yaml

TebLocalPlannerROS:odom_topic: odom# Trajectoryteb_autosize: Truedt_ref: 0.3dt_hysteresis: 0.1max_samples: 500global_plan_overwrite_orientation: Trueallow_init_with_backwards_motion: Truemax_global_plan_lookahead_dist: 3.0global_plan_viapoint_sep: -1global_plan_prune_distance: 1exact_arc_length: Falsefeasibility_check_no_poses: 2publish_feedback: False# Robot       
#最大x向前速度max_vel_x: 0.2
#最大x后退速度max_vel_x_backwards: 0.2max_vel_y: 0.0
#最大转向角速度max_vel_theta: 0.53 # the angular velocity is also bounded by min_turning_radius in case of a carlike robot (r = v / omega)
#最大x加速度acc_lim_x: 0.5
#最大角速度acc_lim_theta: 0.5# ********************** Carlike robot parameters ********************
#设置最小转向半径的参数min_turning_radius: 0.375        # Min turning radius of the carlike robot (compute value using a model or adjust with rqt_reconfigure manually)
#轮距参数wheelbase: 0.145                 # Wheelbase of our robot#将收到的角度消息转换为操作上的角度变化cmd_angle_instead_rotvel: False  # stage simulator takes the angle instead of the rotvel as input (twist message)
#使用仿真器仿真阿克曼车型,将cmd_angle_instead_rotvel改为true,
#stage 仿真器中”Car”车型接受的 cmd_vel 话题里面的z轴角速度并非给定机器人角速度,而是指转向结构的转向角度。
#cmd_angle_instead_rotvel: False,角速度就是定机器人真实的角速度# ********************************************************************footprint_model: # types: "point", "circular", "two_circles", "line", "polygon"type: "line"line_start: [0.05, 0.0] # for type "line"line_end: [0.10, 0.0] # for type "line"# GoalTolerancexy_goal_tolerance: 0.1yaw_goal_tolerance: 0.2free_goal_vel: Falsecomplete_global_plan: True# Obstaclesmin_obstacle_dist: 0.12 # This value must also include our robot's expansion, since footprint_model is set to "line".inflation_dist: 0.6include_costmap_obstacles: Truecostmap_obstacles_behind_robot_dist: 1.5obstacle_poses_affected: 15dynamic_obstacle_inflation_dist: 0.6include_dynamic_obstacles: True costmap_converter_plugin: ""costmap_converter_spin_thread: Truecostmap_converter_rate: 5# Optimizationno_inner_iterations: 5no_outer_iterations: 4optimization_activate: Trueoptimization_verbose: Falsepenalty_epsilon: 0.1obstacle_cost_exponent: 4weight_max_vel_x: 2weight_max_vel_theta: 1weight_acc_lim_x: 1weight_acc_lim_theta: 1weight_kinematics_nh: 1000weight_kinematics_forward_drive: 1weight_kinematics_turning_radius: 1weight_optimaltime: 1 # must be > 0weight_shortest_path: 0weight_obstacle: 100weight_inflation: 0.2weight_dynamic_obstacle: 10 # not in use yetweight_dynamic_obstacle_inflation: 0.2weight_viapoint: 1weight_adapt_factor: 2# Homotopy Class Plannerenable_homotopy_class_planning: Falseenable_multithreading: Truemax_number_classes: 4selection_cost_hysteresis: 1.0selection_prefer_initial_plan: 0.95selection_obst_cost_scale: 1.0selection_alternative_time_cost: Falseroadmap_graph_no_samples: 15roadmap_graph_area_width: 5roadmap_graph_area_length_scale: 1.0h_signature_prescaler: 0.5h_signature_threshold: 0.1obstacle_heading_threshold: 0.45switching_blocking_period: 0.0viapoints_all_candidates: Truedelete_detours_backwards: Truemax_ratio_detours_duration_best_duration: 3.0visualize_hc_graph: Falsevisualize_with_time_as_z_axis_scale: False# Recoveryshrink_horizon_backup: Trueshrink_horizon_min_duration: 10oscillation_recovery: Falseoscillation_v_eps: 0.1oscillation_omega_eps: 0.1oscillation_recovery_min_duration: 10oscillation_filter_duration: 10

多点导航

多点导航:
普通的导航只能发布一个目标点。
在抵达目标点前,如果发布了新的目标点,则会舍弃原本的目标点直接前往新的目标点。
多目标点导航则是按照目标点发布顺序依次前往。stage 仿真:roslaunch robot_navigation robot_navigation.launch simulation:=true
实体机器人上运行则无需传入 simulation:=true启动多点导航工具:roslaunch robot_navigation multi_points_navigation.launch
启动rviz:roslaunch robot_navigation multi_navigation.launchrviz多出了一个2D Nav Goal按钮,可以连续给机器人指定多个目标点,机器人会依次去往各个目标点

multi_points_navigation.launch

multi_points_navigation.launch,多点导航

<launch><!--<node name="robot_pose_publisher" pkg="robot_pose_publisher" type="robot_pose_publisher" />--><!--启动了一个 multi_goal_point.py 的节点--><node name="multi_mark" pkg="robot_navigation" type="multi_goal_point.py" output="screen" /></launch>

multi_navigation.launch

<launch><!-- rviz --><node pkg="rviz" type="rviz" name="rviz" required="true"args="-d $(find robot_navigation)/rviz/multi_navigation.rviz"/>
</launch>

multi_goal_point.py:

核心思路:

通过rviz按钮,发布goal话题,而不是发布/move_base_simple/goal话题,因为/move_base_simple/goal话题会直接发送给movebase 直接产生一个新的导航目标。

通过markerArray把goal缓存起来,通过move_base对每一个点导航的结果,触发status callback,执行下一个目标点。

循环往复。

#!/usr/bin/pythonfrom visualization_msgs.msg import Marker
from visualization_msgs.msg import MarkerArray
import rospy
import math
from geometry_msgs.msg import PointStamped,PoseStamped
import actionlib
from move_base_msgs.msg import *
import tfdef status_callback(msg):#收到move base导航结果时,判断是读取下一个点继续发布;或是当前已是最后一个点做标记标识global goal_pub, index,markerArrayglobal add_more_point,try_againif(msg.status.status == 3):#状态是3,导航成功到目标点try_again = 1if add_more_point == 0:print 'Goal reached'if index < count:pose = PoseStamped()pose.header.frame_id = "map"pose.header.stamp = rospy.Time.now()pose.pose.position.x = markerArray.markers[index].pose.position.x#给pose一个新的值#从markerArray读取一个新的目标点pose.pose.position.y = markerArray.markers[index].pose.position.ypose.pose.orientation.w = 1goal_pub.publish(pose)#发布给move baseindex += 1elif index == count:#执行到 最后一个点add_more_point = 1else:# status值及其含义# uint8 PENDING         = 0   # The goal has yet to be processed by the action server# uint8 ACTIVE          = 1   # The goal is currently being processed by the action server# uint8 PREEMPTED       = 2   # The goal received a cancel request after it started executing#                             #   and has since completed its execution (Terminal State)# uint8 SUCCEEDED       = 3   # The goal was achieved successfully by the action server (Terminal State)# uint8 ABORTED         = 4   # The goal was aborted during execution by the action server due#                             #    to some failure (Terminal State)# uint8 REJECTED        = 5   # The goal was rejected by the action server without being processed,#                             #    because the goal was unattainable or invalid (Terminal State)# uint8 PREEMPTING      = 6   # The goal received a cancel request after it started executing#                             #    and has not yet completed execution# uint8 RECALLING       = 7   # The goal received a cancel request before it started executing,#                             #    but the action server has not yet confirmed that the goal is canceled# uint8 RECALLED        = 8   # The goal received a cancel request before it started executing#                             #    and was successfully cancelled (Terminal State)# uint8 LOST            = 9   # An action client can determine that a goal is LOST. This should not be#                             #    sent over the wire by an action server#如果是失败print 'Goal cannot reached has some error :',msg.status.status," try again!!!!"if try_again == 1:#把目标点再发给move base,让它重试pose = PoseStamped()pose.header.frame_id = "map"pose.header.stamp = rospy.Time.now()pose.pose.position.x = markerArray.markers[index-1].pose.position.xpose.pose.position.y = markerArray.markers[index-1].pose.position.ypose.pose.orientation.w = 1goal_pub.publish(pose)try_again = 0else:#已经重试过但还是到不了,就跳过它执行下个点if index < len(markerArray.markers):pose = PoseStamped()pose.header.frame_id = "map"pose.header.stamp = rospy.Time.now()pose.pose.position.x = markerArray.markers[index].pose.position.xpose.pose.position.y = markerArray.markers[index].pose.position.ypose.pose.orientation.w = 1goal_pub.publish(pose)index += 1def click_callback(msg):#rviz给定一个多目标点,会触发global markerArray,countglobal goal_pub,indexglobal add_more_pointmarker = Marker()marker.header.frame_id = "map"marker.header.stamp = rospy.Time.now()# marker.type = marker.TEXT_VIEW_FACINGmarker.type = marker.CYLINDERmarker.action = marker.ADDmarker.scale.x = 0.2marker.scale.y = 0.2marker.scale.z = 0.5marker.color.a = 1.0marker.color.r = 0.0marker.color.g = 1.0marker.color.b = 0.0marker.pose.orientation.x = 0.0marker.pose.orientation.y = 0.0marker.pose.orientation.z = 0.0marker.pose.orientation.w = 1.0marker.pose.position.x = msg.pose.position.x#读出msg中的位置信息,传到marker消息中marker.pose.position.y = msg.pose.position.ymarker.pose.position.z = msg.pose.position.zmarkerArray.markers.append(marker)# Renumber the marker IDsid = 0for m in markerArray.markers:m.id = idid += 1# Publish the MarkerArraymark_pub.publish(markerArray)#发布#first goal,第一次发布多目标点的时候,直接把话题发布出去if count==0:#没有目标点的时候pose = PoseStamped()#把话题读到pose消息pose.header.frame_id = "map"pose.header.stamp = rospy.Time.now()pose.pose.position.x = msg.pose.position.xpose.pose.position.y = msg.pose.position.ypose.pose.position.z = msg.pose.position.zpose.pose.orientation.x = msg.pose.orientation.xpose.pose.orientation.y = msg.pose.orientation.ypose.pose.orientation.z = msg.pose.orientation.zpose.pose.orientation.w = msg.pose.orientation.wgoal_pub.publish(pose)#通过goal_pub发布#goal_pub发布的就是move base需要的goal话题。包含了目标点的位置和角度信息index += 1#add_more_point=1时,在ros发布一个status=3,触发status_callback,这样能再次发布新的目标点#为解决当所有导航点完成后想要再发布新的导航点的问题if add_more_point and count > 0:add_more_point = 0move =MoveBaseActionResult()move.status.status = 3move.header.stamp = rospy.Time.now()goal_status_pub.publish(move)quaternion = (msg.pose.orientation.x, msg.pose.orientation.y, msg.pose.orientation.z, msg.pose.orientation.w)theta = tf.transformations.euler_from_quaternion( quaternion)[2]count += 1#每点击一次,count加1# print 'add a path goal point %f %f %f'%(msg.pose.position.x,msg.pose.position.y,theta*180.0/3.14)markerArray = MarkerArray()#定义一个变量count = 0       #total goal num 统计发布的目标点数量
index = 0       #current goal point index 统计当前执行到哪个目标点
add_more_point = 0 # after all goal arrive, if add some more goal 标志位
try_again = 1  # try the fail goal once again 尝试次数rospy.init_node('multi_goal_point_demo') #创建节点mark_pub = rospy.Publisher('/path_point_array', MarkerArray,queue_size=100)#发布器发布MarkARRAY类型数据
click_goal_sub = rospy.Subscriber('/goal',PoseStamped,click_callback)#订阅器订阅goal话题,在rviz中发布多目标点所发布的话题,每次接收到goal进入click_callback
goal_pub = rospy.Publisher('/move_base_simple/goal',PoseStamped,queue_size=1)#发布器发布/move_base_simple/goal话题。
#触发move_base进行导航的话题
goal_status_sub = rospy.Subscriber('/move_base/result',MoveBaseActionResult,status_callback)#订阅movebase导航的结果。
#after all goal arrive, if add some more goal
#we deleberate pub a topic to trig the goal sent
goal_status_pub = rospy.Publisher('/move_base/result',MoveBaseActionResult,queue_size=1)
rospy.spin()#在节点中发布/move_base/result
#

多点全自动巡航

仿真:roslaunch robot_navigation robot_navigation.launch simulation:=true
实体机器人上运行则无需传入 simulation:=true 参数rviz: roslaunch robot_navigation navigation_rviz.launchpc开终端:rostopic echo /move_base_simple/goal
(订阅/move_base_simple/goal 话题)通过“2D Nav Goal”按钮设置巡航的点,记录输出的内容。
pose:position:x:y:zorizntation:xvz:打开robot_navigation 功能包下的 way_point.launch 文件修改goallistx,y,z巡航次数:
roslaunch robot_navigation way_point.launch loopTimes loopTimes:=2
loopTimes参数为巡航次数,2,机器人在巡航点来回巡航两遍后停止;0,一直巡航。巡航中每到达一个巡航点,输出下一巡航点的目标ID,巡航次数到达后会输出Loop: 2 Times Finshed 信息。

way_point.launch

<launch><!-- For Simulation --><arg name="sim_mode" default="false" /><param name="/use_sim_time" value="$(arg sim_mode)"/><arg name="loopTimes"       default="0" /><!-- move base --><!--launch 文件启动 way_point.py节点--><node pkg="robot_navigation" type="way_point.py" respawn="false" name="way_point" output="screen"><!-- params for move_base 编写好预设的地点--><param name="goalListX" value="[2.0, 4.0, 3.0]" /><param name="goalListY" value="[3.0, 2.0, 2.0]" /><param name="goalListZ" value="[0.0, 0.0, 0.0]" /><param name="loopTimes" value="$(arg loopTimes)"/><param name="map_frame" value="map" /></node></launch>

multi_goal_point.py

核心思路:

先预置一系列路径点,然后依次发布,只有在上一个点到达后,再发下一个点。

#!/usr/bin/pythonimport rospy
import string
import math
import time
import sysfrom std_msgs.msg import String
from move_base_msgs.msg import MoveBaseActionResult
from actionlib_msgs.msg import GoalStatusArray
from geometry_msgs.msg import PoseStamped
import tf
class MultiGoals:def __init__(self, goalListX, goalListY, goalListZ,loopTimes, map_frame):#订阅器,订阅move_base/result,move base导航结果话题发布,每次收到导航结果话题就会进入statusCB回调self.sub = rospy.Subscriber('move_base/result', MoveBaseActionResult, self.statusCB, queue_size=10)#发布器,发布move_base_simple/goal,move_base真正接收的目标点话题self.pub = rospy.Publisher('move_base_simple/goal', PoseStamped, queue_size=10)   # params & variablesself.goalListX = goalListXself.goalListY = goalListYself.goalListZ = goalListZself.loopTimes = loopTimesself.loop = 1self.wayPointFinished = False self.goalId = 0#执行当前目标点的序列号self.goalMsg = PoseStamped()self.goalMsg.header.frame_id = map_frameself.goalMsg.pose.orientation.z = 0.0self.goalMsg.pose.orientation.w = 1.0# Publish the first goaltime.sleep(1)#把goalList中xyz分别付给goalMsg消息self.goalMsg.header.stamp = rospy.Time.now()self.goalMsg.pose.position.x = self.goalListX[self.goalId]self.goalMsg.pose.position.y = self.goalListY[self.goalId]self.goalMsg.pose.orientation.x = 0.0self.goalMsg.pose.orientation.y = 0.0if abs(self.goalListZ[self.goalId]) > 1.0:self.goalMsg.pose.orientation.z = 0.0self.goalMsg.pose.orientation.w = 1.0else:w = math.sqrt(1 - (self.goalListZ[self.goalId]) ** 2)self.goalMsg.pose.orientation.z = self.goalListZ[self.goalId]self.goalMsg.pose.orientation.w = wself.pub.publish(self.goalMsg) #赋值完goalMsg,通过发布器,发布给move aserospy.loginfo("Current Goal ID is: %d", self.goalId)  #输出日志self.goalId = self.goalId + 1 #目标点发步出去就会等待statusCB回调#机器人到达一个目标点就进入statusCB函数def statusCB(self, data):if self.loopTimes and (self.loop > self.loopTimes):rospy.loginfo("Loop: %d Times Finshed", self.loopTimes)self.wayPointFinished = Trueif data.status.status == 3 and (not self.wayPointFinished): # reached,而且路径巡航没结束self.goalMsg.header.stamp = rospy.Time.now()                self.goalMsg.pose.position.x = self.goalListX[self.goalId]self.goalMsg.pose.position.y = self.goalListY[self.goalId]if abs(self.goalListZ[self.goalId]) > 1.0:self.goalMsg.pose.orientation.z = 0.0self.goalMsg.pose.orientation.w = 1.0else:w = math.sqrt(1 - (self.goalListZ[self.goalId]) ** 2)self.goalMsg.pose.orientation.z = self.goalListZ[self.goalId]self.goalMsg.pose.orientation.w = wself.pub.publish(self.goalMsg) #读取下一个点然后发布出去rospy.loginfo("Current Goal ID is: %d", self.goalId)              if self.goalId < (len(self.goalListX)-1):self.goalId = self.goalId + 1else:self.goalId = 0self.loop += 1if __name__ == "__main__":try:    # ROS Init    rospy.init_node('way_point', anonymous=True)# Get params 获取launch文件中传入的goalListXYZgoalListX = rospy.get_param('~goalListX', '[2.0, 2.0]')goalListY = rospy.get_param('~goalListY', '[2.0, 4.0]')goalListZ = rospy.get_param('~goalListZ', '[0.0, 0.0]')map_frame = rospy.get_param('~map_frame', 'map' )loopTimes = int(rospy.get_param('~loopTimes', '0')) goalListX = goalListX.replace("[","").replace("]","")goalListY = goalListY.replace("[","").replace("]","")goalListZ = goalListZ.replace("[","").replace("]","")goalListX = [float(x) for x in goalListX.split(",")]goalListY = [float(y) for y in goalListY.split(",")]goalListZ = [float(z) for z in goalListZ.split(",")]#处理成数组的形式if len(goalListX) == len(goalListY) == len(goalListZ) & len(goalListY) >=2:  #判断三个数组长度等# Constract MultiGoals Obj rospy.loginfo("Multi Goals Executing...")mg = MultiGoals(goalListX, goalListY, goalListZ, loopTimes, map_frame)#循环次数 地图基座标rospy.spin()else:rospy.errinfo("Lengths of goal lists are not the same")except KeyboardInterrupt:print("shutting down")

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/560172.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Apprentissage du français partie 1

Apprentissage du franais partie 1 键盘转换图&#xff1a; 字母&#xff1a;26个 元音字母&#xff1a;a、e、i、o、u、y b浊辅音(声带)-p清辅音 d-t 音符 音符&#xff1a;改变字母发音。 &#xff1a;闭音符 [e] &#xff1a;开音符 /ε/ &#xff1a;长音符 /ε/…

stm32基本定时器

定时器分类 stm32f1系列&#xff0c;8个定时器&#xff0c;基本定时器(TIM6,7)、通用定时器(TIM2,3,4,5)、高级定时器(TIM1,8)。 基本定时器&#xff1a;16位&#xff0c;只能向上计数的定时器&#xff0c;只能定时&#xff0c;没有外部IO 通用定时器&#xff1a;16位&#…

stm32高级定时器 基础知识

stm32高级定时器 高级定时器时基单元&#xff1a; 包含一个16位自动重装载寄存器 ARR 一个16位的计数器CNT&#xff0c;可向上/下计数 一个16位可编程预分频器PSC&#xff0c;预分频器时钟源有多种可选&#xff0c;有内部的时钟、外部时钟。 一个8位的重复计数器 RCR&…

stm32 PWM互补输出

stm32高级定时器例子—stm32 PWM互补输出 定时器初始化结构体 TIM_TimeBaseInitTypeDef 时基结构体&#xff0c;用于定时器基础参数设置&#xff0c;与TIM_TimeBaseInit函数配合使用&#xff0c;完成配置。 typedef struct { TIM_Prescaler /*定时器预分频器设置&…

stm32 输入捕获 测量脉宽

选用通用定时器TIM5的CH1。 PA0接一个按键&#xff0c;默认接GND&#xff0c;当按键按下时&#xff0c;IO口被拉高&#xff0c;此时&#xff0c;可利用定时器的输入捕获功能&#xff0c;测量按键按下的这段高电平的时间。 宏定义方便程序升级、移植&#xff0c;举个例子&#…

stm32 PWM输入捕获

普通的输入捕获&#xff0c;可使用定时器的四个通道&#xff0c;一路捕获占用一个捕获寄存器. PWM输入&#xff0c;只能使用两个通道&#xff0c;通道1和通道2。 一路PWM输入占用两个捕获寄存器&#xff0c;一个捕获周期&#xff0c;一个捕获占空比。 这里&#xff0c;用通用…

直流有刷减速电机结构及其工作原理

寒假无聊拆了个直流有刷减速电机。下面介绍一下它的结构和工作原理 直流电机 直流电机和直流减速电机&#xff1a; 构造上相差的是一个减速齿轮组。 普通的直流电机当空载时&#xff0c;电机的转速由电压决定&#xff0c;直流减速电机的转速由齿轮组和电压决定。 齿轮组作…

数据库基础概念

postgreSQL设置只允许本地机器连接 在D:\program files\PostgreSQL\14\data里面设置postgresql.conf&#xff1a; listen_addresses ‘localhost’ 然后在服务窗口重新启动postgresql。 PostgreSQL执行SQL语句 PostgreSQL的psql工具可通过命令行执行SQL语句。 psql -U po…

电机和驱动的种类

电机种类 直流电机 分为普通的直流电机、直流减速电机、有刷、无刷。 直流有刷减速电机参数&#xff1a; 空载转速&#xff0c;正常工作电压&#xff0c;电机不带任何负载的转速。 空载电流&#xff0c;正常工作电压&#xff0c;电机不带任何负载的工作电流。单位mA。 负载…

Linux shell基础知识

Shell简介 Shell是一个应用程序&#xff0c;接收用户输入的命令&#xff0c;根据命令做出相应动作。 Shell负责将应用层或者用户输入的命令&#xff0c;传递给系统内核。由操作系统内核&#xff0c;来完成相应的工作。然后将结果反馈给应用层或者用户。 shell命令格式&#…

Linux APT VIM 的一些指令

APT APT下载工具&#xff0c;可以实现软件自动下载、配置、安装二进制或源码功能。 APT采用客户端/服务器模式。 sudo apt-get update 更新软件 sudo apt-get check 检查依赖关系 sudo apt-get install package-name 安装软件 apt-get负责下载软件&#xff0c;install负责安…

CATIA 界面介绍

窗口介绍 窗口主要有&#xff1a;菜单栏、工具栏、特征树、罗盘、信息栏、图形区。 菜单栏&#xff0c;开始里面有CATIA的各个功能模块。 图形区&#xff0c;进行3D、2D设计的图形创建、编辑区域。 信息栏&#xff0c;显示用户即将进行操作的文字提示。 工具栏&#xff0c;…

Linux C文件编译

设置编辑器 1.TAB键设置为4空格。 首先用vim打开/etc/vim/vimrc文件&#xff0c;这里面发现root用户才能修改vimrc文件&#xff0c;所以用sudo命令暂时切换到root用户。 在vimrc文件最后面&#xff0c;输入set ts 4完成设置。 然后保存&#xff0c;关闭文件。 2.VIM编辑器显…

catia 草图设计

草图设计界面 进入草图编辑器环境&#xff1a; 菜单栏&#xff0c;文件-新建&#xff0c;然后类型选择Part。 工具栏&#xff0c;点击下面草图按钮。 然后选择草图平面。 然后就进入草图界面。 草图界面主要由菜单栏、工具栏、特征树、信息栏、罗盘、图形区组成。 菜单栏&…

CATIA连接盘实体设计

文件-新建-part。 点击草图按钮&#xff0c;选择xy平面&#xff0c;绘制如下草图。 然后用橡皮擦修改一下。 然后&#xff0c;点击凸台按钮。选择刚刚定义的草图&#xff0c;拉伸20mm。 选择拉伸实体上端面&#xff0c;点击草图按钮&#xff0c;进入草图编辑器。 在草图编辑器…

PostgreSQL 表的创建、删除、更新

psql工具 psql工具&#xff0c;可通过命令行执行SQL语句。 D:\program files\PostgreSQL\14\bin>psql -U postgres 用户 postgres 的口令&#xff1a; psql (14.2) 输入 "help" 来获取帮助信息.postgres#创建数据库 创建表之前&#xff0c;先创建存储表的数据库…

PostgreSQL 简单的查询

查询列 SELECT语句&#xff0c;用于从表中选取数据。 格式&#xff1a; SELECT <列名>,... FROM <表名>;从Product表中&#xff0c;查询三列。 SELECT product_id, product_name, purchase_price FROM Product;查询所有列&#xff0c;格式&#xff1a; SELECT …

PostgreSQL 聚合、分组、排序

聚合函数 用于汇总的函数。 COUNT COUNT&#xff0c;计算表中的行数(记录数)。 计算全部数据的行数&#xff1a; SELECT COUNT(*)FROM Product;NULL之外的数据行数&#xff1a; SELECT COUNT(purchase_price)FROM Product;结果如下图。 对于一个含NULL的表&#xff1a; …

PostgreSQL 插入、删除、更新、事务

INSERT 使用INSERT语句可以向表中插入数据。 创建一个表&#xff1a; CREATE TABLE ProductIns (product_id CHAR(4) NOT NULL,product_name VARCHAR(100) NOT NULL,product_type VARCHAR(32) NOT NULL,sale_price INTEGER DEFAULT 0,purchase_p…

PostgreSQL 视图、子查询

视图 表里面保存的是实际数据&#xff0c;视图里面保存的是SELECT语句(视图本身不存储数据)。 从视图中读取数据&#xff0c;此时视图在内部执行SELECT语句&#xff0c;创建一张临时表。 使用视图的好处&#xff1a;其一&#xff0c;视图不保存数据&#xff0c;节省存储设备…