前言
作为一个参加了两届智能车竞赛的老车手了,总想留下一些有用的东西,前段时间刚参加完讯飞智能车的线上赛,现提供一些经验总结,以及备赛思路。ps:如果有什么不对的地方欢迎一起讨论。
正文
想要让我们的智能车跑起来需要各种功能包的配合,建图,定位,导航缺一不可。具体功能包的配置古月居的讯飞智能车教程里有详细的步骤,有兴趣的同学可以去学习一下。在这里我就对我在调试过程中遇到的一些常见问题进行一些总结。
首先是局部导航控制的算法选择问题。无论是古月居的教程还是官方的教程里面使用的都是DWA速度算法,因此群里刚开始大部分人使用的都是此算法。确实DWA算法完美适用于差速车,全向车。但是在竞速比赛中此算法并不适用,因为他无法以直线通过较小的弯道,并且无法做到直线加速等。所以TEB算法在竞速方面是更好的选择。
其次就是在更新了模型以后,小车的转弯变得非常的笨重,导致无法转过来或者是转弯不及时等问题。刚开始的时候第一反应就是加大角速度和角加速度,结果却变得更糟糕。。后来通过rostopic echo来观察cmd_vel的输出以及根据角速度,线速度和半径的关系。减小了角加速度,角速度以及线加速度,并且减小了costmap中的膨胀系数,让全局规划线路更加贴近内道来缓解此问题。
还有就是停车问题,很多同学在停车的过程中由于速度太快或者是没有完全到达目标点导致撞墙以及小车在不停的调整姿态,浪费了很多时间。一开始我通过调整目标点以及增大目标的容忍度来解决此问题。虽然有所改善,但有时候还是会出现撞墙或者调整的现象,所以我采用了读取小车的坐标将其和目标点坐标进行比对,当小车的坐标和目标点的坐标小于阈值时,cmd_vel输出为0。但是teb还是会接着输出速度,可能会导致冲突。因此将teb的速度输出重映射,停车程序订阅teb输出的速度,根据和目标点的距离来发布cmd速度。
#!/usr/bin/env python
#-*-coding:utf-8-*-
# 加载ros的Python基础包
import rospy
# 加载topic话题 的 msg消息
from std_msgs.msg import Float64
from geometry_msgs.msg import Twist,Pose
from nav_msgs.msg import Odometry
from gazebo_msgs.msg import LinkStates
from std_msgs.msg import Header
import numpy as np
import math
a=0
def servo_commands():
# 创建node节点
rospy.init_node('motor_control', anonymous=True)
rospy.Subscriber('/gazebo/link_states', LinkStates, sub_robot_pose_update)
rospy.Subscriber("/teb_cmd_vel", Twist, callback)
# 阻塞等待
rospy.spin()
# 回调函数
def sub_robot_pose_update(msg):
global a
cmd = Twist()
global goal_posex
global goal_posey
goal_posex = -0.05
goal_posey = -5.10
# Find the index of the racecar 找到小车的索引
try:
#创建msg消息对象
arrayIndex = msg.name.index('robot::dummy')
except ValueError as e:
pass
else:
# Extract our current position information 提取我们当前的位置信息
last_received_pose = msg.pose[arrayIndex]
last_received_twist = msg.twist[arrayIndex]
if (abs(last_received_pose.position.x - goal_posex)<0.05) and (abs(last_received_pose.position.y - goal_posey)<0.05): #对比小车位置和目标点距离
a =1
else:
a=0
def callback(data):
global a
pub_cmd = rospy.Publisher('/cmd_vel', Twist, queue_size=10)
# 创建 msg 消息, 注意:ros的float64是一个结构体
vel_msg = Twist()
if a==1:
vel_msg.linear.x = 0
vel_msg.angular.z = 0
else:
vel_msg.linear.x = data.linear.x
vel_msg.angular.z =data.angular.z
pub_cmd.publish(vel_msg)
# 发布topic话题 —— 线速度输出
# 向topic话题 发送 msg消息
if __name__ == '__main__':
try:
servo_commands()
except rospy.ROSInterruptException:
pass
最后就是TEB参数的问题。下面我开始介绍TEB相关参数,以及个人的一些调参经验,可能不完全正确希望大家多多交流指正。
teb_autosize: True
dt_ref: 0.3
dt_hysteresis: 0.1
max_samples: 500
global_plan_overwrite_orientation: False
allow_init_with_backwards_motion: True
max_global_plan_lookahead_dist: 3.5
global_plan_viapoint_sep: -0.1
global_plan_prune_distance: 1
exact_arc_length: True
feasibility_check_no_poses: 5
publish_feedback: False
max_global_plan_lookahead_dist最大向前看距离。当数值小的时候对于U型弯道很友好但是对于其他的弯道不能很好的切内道,数值大时相反。其余参数没有过多改动。
max_vel_x: 1.85
max_vel_x_backwards: 0.5
max_vel_theta: 3.14 # the angular velocity is also bounded by min_turning_radius in case of a carlike robot (r = v / omega)
acc_lim_x: 0.35 #0.5############################3
acc_lim_theta: 1.57 #0.5######################
# ********************** Carlike robot parameters ********************
min_turning_radius: 0.0 #0.5 # Min turning radius of the carlike robot (compute value using a model or adjust with rqt_reconfigure manually)
cmd_angle_instead_rotvel: False # stage simulator takes the angle instead of the rotvel as input (twist message)
# ****************
上面参数是小车的速度参数,acc_lim_x和acc_lim_theta是线加速度和角加速度,其他的资料上说电机的性能好的时候可以为0,但是在仿真中,是不可以设置为0的,调参过程中可以先固定线速度和角速度,加速度可以从小一点一点往上加。其中对于差速车以及全向车min_turning_radius应该为0。
footprint_model: # types: "point", "circular", "two_circles", "line", "polygon"
type: "line"
radius: 0.1 # for type "circular"
line_start: [-0.1, 0.0] # for type "line"
line_end: [0.1, 0.0] # for type "line"
front_offset: 0.2 # for type "two_circles"
front_radius: 0.2 # for type "two_circles"
rear_offset: 0.2 # for type "two_circles"
rear_radius: 0.2 # for type "two_circles"
vertices: [ [0.25, -0.05], [0.18, -0.05], [0.18, -0.18], [-0.19, -0.18], [-0.25, 0], [-0.19, 0.18], [0.18, 0.18], [0.18, 0.05], [0.25, 0.05] ] # for type "polygon"
footprint_model 参数。我看很多人对这个参数没有太过于关注,直接用的默认值。然而小车的轮廓参数会影响到避障以及行驶的效果。对于类车机器人,一般可以设置为line,如果机器性能良好的话也可以设置为polygon。而具体的参数要根据小车的具体模型来写,根据roswiki的介绍和讯飞小车的模型,具体设置如上。
penalty_epsilon: 0.1 # 0.1##########################
obstacle_cost_exponent: 4
weight_max_vel_x: 10
weight_max_vel_theta: 1
weight_acc_lim_x: 1
weight_acc_lim_theta: 1
weight_kinematics_nh: 1000
weight_kinematics_forward_drive: 100 # 1 #############################
weight_kinematics_turning_radius: 0.1 #1 ###############################
weight_optimaltime: 5.5 #1 ##################### must be > 0
weight_shortest_path: 0
weight_obstacle: 10
weight_inflation: 0.2 #0.2#########################
weight_dynamic_obstacle: 10 # not in use yet
weight_dynamic_obstacle_inflation: 0.2
weight_viapoint: 1.5
weight_adapt_factor: 2
上面这些参数是对一些决策的权重设置。 其中线速度,角速度和加速度的权重决定了小车整体的运行速度,但是参数过大可能会导致不稳定,根据实际情况来,先调整线速度的权重,加速度的权重不推荐改动。weight_kinematics_forward_drive为前进行驶权重,权重越大越不容易倒车,但是并不代表不会完全禁止倒车,想让小车不倒车还需要其他参数的相互配合如速度参数,半径参数。weight_kinematics_nh:此参数如果是全向车则设置为1,因为全向车可以y方向移动。weight_optimaltime:此参数就是最优时间权重,当数值<1时车辆速度明显平稳,不会向最高速度加速,将远离障碍物行驶,当数值>1时小车在直线加速,更切内道,离障碍物更近,整体速度有显著的提升。我见过有的队伍能给到50的。
xy_goal_tolerance: 0.4
yaw_goal_tolerance: 1 # defualt 0.3
free_goal_vel: True # default False | 设为False时,车辆到达终点时的目标速度为0,否则即全速冲线
complete_global_plan: True
free_goal_vel参数其实个人觉得对速度影响蛮大的,因为如果设置为false时,小车在离终点还有段距离时就开始减速了,对于竞速比赛来说有些浪费时间了。因此这个参数可以设置为True,但是需要和停车程序相互配合,不然会出现停不下来撞墙的情况。
探讨
下面是我被问到的几个最多的问题,大家也可以一起探讨一下。
1:TEB功能包怎么配置
首先下载teb功能包 sudo apt-get install ros-melodic-teb-local-planner
<!--Launch the move base with time elastic band-->
<param name="/use_sim_time" value="true"/>
<node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen">
<remap from="/odom" to="$(arg odom_topic)"/>
<remap from="/scan" to="$(arg laser_topic)"/>
<remap from="/cmd_vel" to="$(arg cmd_vel_name)"/>
<rosparam file="$(find gazebo_nav)/config/costmap_common_params.yaml" command="load" ns="global_costmap" />
<rosparam file="$(find gazebo_nav)/config/costmap_common_params.yaml" command="load" ns="local_costmap" />
<rosparam file="$(find gazebo_nav)/config/local_costmap_params.yaml" command="load" />
<rosparam file="$(find gazebo_nav)/config/global_costmap_params.yaml" command="load" />
<rosparam file="$(find gazebo_nav)/config/teb_local_planner_params.yaml" command="load" />
<param name="base_global_planner" value="global_planner/GlobalPlanner" />
<param name="planner_frequency" value="10" />
<param name="planner_patience" value="15" />
<!--param name="use_dijkstra" value="false" /-->
<param name="base_local_planner" value="teb_local_planner/TebLocalPlannerROS" />
<param name="controller_frequency" value="20.0" />
<param name="controller_patience" value="20.0" />
<param name="clearing_rotation_allowed" value="false" />
如果想使用DWA包,就将上面的teb_local_planner_params改成dwa_local_planner_params并且teb_local_planner/TebLocalPlannerROS改为dwa_local_planner/DWAPlannerROS
2:出现base_link和map之间没有tf转换的错误提示。
查看tf树,其中map有没有和odom连接起来,若没有,则写一个tf变换将odom和map连起来间接的将map和base_link连接上了
<node pkg="tf" type="static_transform_publisher" name="map_to_odom" args="0.0 0.0 0.0 0 0 0.0 /map /odom 1000"/>
3:每次修改参数都要重新打开gazebo,有没有什么方便的方法,能快速调参。
首先可以使用rqt工具进行调参,不用手动一个一个在代码里修改。其次就是可以写一个恢复程序,在车模运行结束后运行恢复程序,发布锥桶和小车的原始坐标将其复位。
总结
这次参加智能车比赛算是一种情怀,毕竟智能车也算是陪伴了我整个青春的比赛,就像卓大大说的无飞卡不青春一样,这次也希望可以给我的青春留下一个开心的回忆。在这里给所有想参加讯飞智能车比赛的同学们一条个人建议的学习路线。古月21讲+ROS探索总结——turtlebot3仿真的demo需要完整跑一遍——teb算法的配置以及在turtlebot3的仿真实现——古月居的语音识别课程——ROS机器视觉学习。当然这些只是基础的,还可以学习yolo算法,深度学习算法,自己编写控制器等。线上部分就到此结束啦,等车模到了会更新线下赛的一些个人思路。最后,上视频~