引自《Real-time Motion Planning with Applications toAutonomous Urban Driving》
待解决:
物理公式推导
与传统方法相比有什么优势
CL-RRT以两种不同的方式使用控制器。一种是结合车辆模型的闭环预测,另一种是实时执行运动规划。在我们的实现中,控制器在执行过程中以25、Hz的频率运行,在闭环仿真中采用了相同的步长。相同的控制器代码用于执行和预测。
A steer controller
转向控制器是基于纯跟踪控制器的,它是一种非线性路径跟随器,广泛应用于地面机器人,以及最近在无人机中使用。它因其简单性而被采用,是一种直观的控制律,具有明确的几何意义。由于DUC规则将运行速度限制在每小时30英里以下,避免了极端机动(如高速紧转弯),因此在控制设计中忽略了侧滑等动态效应。
简化的自行车运动学模型如下:
其中,L为车轮轴距。
图中的转向角δ^对应于方程中的负δ。
利用基本的平面几何,将锚点放在前瞻点的航向上,这时所需要的转向角可被计算为:
以上,给出了修正的纯追踪控制律。
注意,通过设置LFW=0和LRV=0,锚点与后轴重合,恢复了传统的纯追踪控制器。
那么这种优化后的跟踪控制器优点在哪呢?
下面讨论实现:
η的计算:
//由汽车位姿得到当前偏航角
double L1Controller::getYawFromPose(const geometry_msgs::Pose& carPose)
{
float x = carPose.orientation.x;
float y = carPose.orientation.y;
float z = carPose.orientation.z;
float w = carPose.orientation.w;
double tmp,yaw;
tf::Quaternion q(x,y,z,w);
tf::Matrix3x3 quaternion(q);
quaternion.getRPY(tmp,tmp, yaw);//四元数转欧拉角
return yaw;
}
起点为车的位置终点为路径上一点的位置的向量旋转由carPose得到的偏航角后,得到该向量在路径的坐标系下的描述。如果该描述的x>0,意味该点在前进方向上。
当该点在前进方向上时,要判断两点距离是否大于Lfw,如果大于Lfw,该点为前瞻点。
bool L1Controller::isForwardWayPt(const geometry_msgs::Point& wayPt, const geometry_msgs::Pose& carPose)
{
float car2wayPt_x = wayPt.x - carPose.position.x;
float car2wayPt_y = wayPt.y - carPose.position.y;
double car_theta = getYawFromPose(carPose);
float car_car2wayPt_x = cos(car_theta)*car2wayPt_x + sin(car_theta)*car2wayPt_y;
float car_car2wayPt_y = -sin(car_theta)*car2wayPt_x + cos(car_theta)*car2wayPt_y;
if(car_car2wayPt_x >0) /*is Forward WayPt*/
return true;
else
return false;
}
bool L1Controller::isWayPtAwayFromLfwDist(const geometry_msgs::Point& wayPt, const geometry_msgs::Point& car_pos)
{
double dx = wayPt.x - car_pos.x;
double dy = wayPt.y - car_pos.y;
double dist = sqrt(dx*dx + dy*dy);
if(dist < Lfw)
return false;
else if(dist >= Lfw)
return true;
}
geometry_msgs::Point L1Controller::get_odom_car2WayPtVec(const geometry_msgs::Pose& carPose)
{
geometry_msgs::Point carPose_pos = carPose.position;
double carPose_yaw = getYawFromPose(carPose);
geometry_msgs::Point forwardPt;
geometry_msgs::Point odom_car2WayPtVec;
foundForwardPt = false;
if(!goal_reached){
for(int i =0; i< map_path.poses.size(); i++)
{
geometry_msgs::PoseStamped map_path_pose = map_path.poses[i];
geometry_msgs::PoseStamped odom_path_pose;
try
{
tf_listener.transformPose("odom", ros::Time(0) , map_path_pose, "map" ,odom_path_pose);
geometry_msgs::Point odom_path_wayPt = odom_path_pose.pose.position;
bool _isForwardWayPt = isForwardWayPt(odom_path_wayPt,carPose);
if(_isForwardWayPt)
{
bool _isWayPtAwayFromLfwDist = isWayPtAwayFromLfwDist(odom_path_wayPt,carPose_pos);
if(_isWayPtAwayFromLfwDist)
{
forwardPt = odom_path_wayPt;
foundForwardPt = true;
break;
}
}
}
catch(tf::TransformException &ex)
{
ROS_ERROR("%s",ex.what());
ros::Duration(1.0).sleep();
}
}
}
else if(goal_reached)
{
forwardPt = odom_goal_pos;
foundForwardPt = false;
//ROS_INFO("goal REACHED!");
}
/*Visualized Target Point on RVIZ*/
/*Clear former target point Marker*/
points.points.clear();
line_strip.points.clear();
if(foundForwardPt && !goal_reached)
{
points.points.push_back(carPose_pos);
points.points.push_back(forwardPt);
line_strip.points.push_back(carPose_pos);
line_strip.points.push_back(forwardPt);
}
marker_pub.publish(points);
marker_pub.publish(line_strip);
odom_car2WayPtVec.x = cos(carPose_yaw)*(forwardPt.x - carPose_pos.x) + sin(carPose_yaw)*(forwardPt.y - carPose_pos.y);
odom_car2WayPtVec.y = -sin(carPose_yaw)*(forwardPt.x - carPose_pos.x) + cos(carPose_yaw)*(forwardPt.y - carPose_pos.y);
return odom_car2WayPtVec;
}
double L1Controller::getEta(const geometry_msgs::Pose& carPose)
{
geometry_msgs::Point odom_car2WayPtVec = get_odom_car2WayPtVec(carPose);
double eta = atan2(odom_car2WayPtVec.y,odom_car2WayPtVec.x);
return eta;
}