赞
踩
• 使用接收到的车辆控制信号(v,ω)模拟理想的自身位置和速度
<!-- --> <launch> <arg name="initialize_source" default="Rviz"/> <arg name="accel_rate" default="1.0"/> <arg name="angle_error" default="0.0"/> <arg name="position_error" default="0.0"/> <arg name="lidar_height" default="1.0"/> <arg name="use_ctrl_cmd" default="false" /> <node pkg="waypoint_follower" type="wf_simulator" name="wf_simulator" output="screen"> <param name="initialize_source" value="$(arg initialize_source)"/> <param name="accel_rate" value="$(arg accel_rate)"/> <param name="angle_error" value="$(arg angle_error)"/> <param name="position_error" value="$(arg position_error)"/> <param name="lidar_height" value="$(arg lidar_height)"/> <param name="use_ctrl_cmd" value="$(arg use_ctrl_cmd)"/> </node> </launch>
launch文件只有一个waypoint_follower
功能包下的wf_simulator
节点,设置了一些参数并对其进行赋值。
节点的主函数如下所示:
int main(int argc, char** argv) { ros::init(argc, argv, "wf_simulator"); ros::NodeHandle nh; ros::NodeHandle private_nh("~"); std::string initialize_source; private_nh.getParam("initialize_source", initialize_source); ROS_INFO_STREAM("initialize_source : " << initialize_source); double accel_rate; private_nh.param("accel_rate", accel_rate, double(1.0)); ROS_INFO_STREAM("accel_rate : " << accel_rate); private_nh.param("position_error", position_error_, double(0.0)); private_nh.param("angle_error", angle_error_, double(0.0)); private_nh.param("lidar_height", lidar_height_, double(1.0)); private_nh.param("use_ctrl_cmd", use_ctrl_cmd, false); nh.param("vehicle_info/wheel_base", wheel_base_, double(2.7)); // publish topic odometry_publisher_ = nh.advertise<geometry_msgs::PoseStamped>("sim_pose", 10); velocity_publisher_ = nh.advertise<geometry_msgs::TwistStamped>("sim_velocity", 10); vehicle_status_publisher_ = nh.advertise<autoware_msgs::VehicleStatus>("/vehicle_status", 10); // subscribe topic ros::Subscriber cmd_subscriber = nh.subscribe<autoware_msgs::VehicleCmd>("vehicle_cmd", 10, boost::bind(CmdCallBack, _1, accel_rate)); ros::Subscriber waypoint_subcscriber = nh.subscribe("base_waypoints", 10, waypointCallback); ros::Subscriber closest_sub = nh.subscribe("closest_waypoint", 10, callbackFromClosestWaypoint); ros::Subscriber initialpose_subscriber; if (initialize_source == "Rviz") { initialpose_subscriber = nh.subscribe("initialpose", 10, initialposeCallback); } else if (initialize_source == "lidar_localizer") { initialpose_subscriber = nh.subscribe("ndt_pose", 10, callbackFromPoseStamped); } else if (initialize_source == "GNSS") { initialpose_subscriber = nh.subscribe("gnss_pose", 10, callbackFromPoseStamped); } else { ROS_INFO("Set pose initializer!!"); } ros::Rate loop_rate(LOOP_RATE); while (ros::ok()) { ros::spinOnce(); // check subscribe topic if (!initial_set_) { loop_rate.sleep(); continue; } updateVelocity(); publishOdometry(); loop_rate.sleep(); } return 0; }
函数中创建两个句柄,然后声明参数并对参数值进行设置,参数值如launch文件中所示。
建立发布者分别用来发布如下类型的消息:
<geometry_msgs::PoseStamped>"sim_pose"
<geometry_msgs::TwistStamped>"sim_velocity"
<autoware_msgs::VehicleStatus>"/vehicle_status"
并返回对应的发布对象。
建立接收者来接收如下类型消息:<autoware_msgs::VehicleCmd>"vehicle_cmd"
、"base_waypoints"
、"closest_waypoint"
并返回对应的接收对象。
还单声明了一个initialpose_subscriber
接收对象,通过判断位置初始化方式initialize_source
的值来确定其接收内容。
如代码中有如下三种种位姿初始化的方式:"Rviz"
、"lidar_localizer"
、"GNSS"
当初始位姿方式是"lRVIZ"
时(可视化界面中人为估计位置通过2D Pose Estimate工具),其接收的是"initialpose"
话题消息回调函数如下:
void initialposeCallback(const geometry_msgs::PoseWithCovarianceStampedConstPtr& input)
{
tf::StampedTransform transform;
getTransformFromTF(MAP_FRAME, input->header.frame_id, transform);
initial_pose_.position.x = input->pose.pose.position.x + transform.getOrigin().x();
initial_pose_.position.y = input->pose.pose.position.y + transform.getOrigin().y();
initial_pose_.position.z = input->pose.pose.position.z + transform.getOrigin().z();
initial_pose_.orientation = input->pose.pose.orientation;
initial_set_ = true;
pose_set_ = false;
}
新建了一个transform
对象。然后调用 getTransformFromTF()
函数,函数内容如下:
void getTransformFromTF(const std::string parent_frame, const std::string child_frame, tf::StampedTransform& transform) { static tf::TransformListener listener; while (1) { try { listener.lookupTransform(parent_frame, child_frame, ros::Time(0), transform); break; } catch (tf::TransformException ex) { ROS_ERROR("%s", ex.what()); ros::Duration(1.0).sleep(); } } }
函数中新建一个TransformListener
对象listener
,一旦创建了侦听器,它将开始通过网络接收tf转换接收对象,并通过while循环,使用try()
和catch()
用来捕捉可能的异常,lookupTransform()
函数访问tf树中最新可用的从RVIZ输入的坐标到MAP_FRAME的转换。
再回到initialposeCallback
回调函数看,接着计算初始化位置在整体坐标系中的位置和放向。
对initial_set_
参数设置为 true
、 pose_set_
设为 false
;
当初始位姿方式是"lidar_localizer"
时(激光雷达定位方式),其接收的是"ndt_pose"
话题消息回调函数如下:
void callbackFromPoseStamped(const geometry_msgs::PoseStampedConstPtr& msg)
{
initial_pose_ = msg->pose;
initial_set_ = true;
}
设定initial_pose
为接收的消息值,并设定initial_set_
参数为true
.
当初始位姿方式是"GNSS"
时(一般是GPS定位),其接收的是"gnss_pose"
话题消息回调函数如下:
void callbackFromPoseStamped(const geometry_msgs::PoseStampedConstPtr& msg)
{
initial_pose_ = msg->pose;
initial_set_ = true;
}
同lidar_localizer的定位方式是相同的回调处理函数设定initial_set_ = true
。
初始位置的获取到此便完成了。
回到主函数下while(ros::ok)
接着处理订阅的消息有如下回调函数:
订阅"vehicle_cmd"
的回调函数
void CmdCallBack(const autoware_msgs::VehicleCmdConstPtr& msg, double accel_rate) { if (use_ctrl_cmd == true) { linear_acceleration_ = msg->ctrl_cmd.linear_acceleration; steering_angle_ = msg->ctrl_cmd.steering_angle; } else { static double previous_linear_velocity = 0; if (current_velocity_.linear.x < msg->twist_cmd.twist.linear.x) { current_velocity_.linear.x = previous_linear_velocity + accel_rate / (double)LOOP_RATE; if (current_velocity_.linear.x > msg->twist_cmd.twist.linear.x) { current_velocity_.linear.x = msg->twist_cmd.twist.linear.x; } } else { current_velocity_.linear.x = previous_linear_velocity - accel_rate / (double)LOOP_RATE; if (current_velocity_.linear.x < msg->twist_cmd.twist.linear.x) { current_velocity_.linear.x = msg->twist_cmd.twist.linear.x; } } previous_linear_velocity = current_velocity_.linear.x; current_velocity_.angular.z = msg->twist_cmd.twist.angular.z; //current_velocity_ = msg->twist; } }
接收话题"vehicle_cmd"
的autoware_msgs::VehicleCmd
消息。如果use_ctrl_cmd
的值为true
(使用控制命令),则直接把接受的控制命令的相关之赋给 linear_acceleration_
(加速度)和steering_angle_
(转角),但是此节点中不使用控制命令,接着判断当前速度与指令速度之间的关系,按条件赋值/(有点不明白的地方请大佬指教,current_velocity_.linear.x当前速度哪里来的)
订阅"base_waypoints"
的回调函数:
void waypointCallback(const autoware_msgs::LaneConstPtr& msg)
{
current_waypoints_.setPath(*msg);
waypoint_set_ = true;
}
void setPath(const autoware_msgs::Lane &waypoints)
{
current_waypoints_ = waypoints;
}
把接收到的路径信息直接用做当前lu’jing’d路径点,并设置参数waypoint_set_
为true
。
订阅closest_waypoint
最近点的回调函数
void callbackFromClosestWaypoint(const std_msgs::Int32ConstPtr& msg)
{
closest_waypoint_ = msg->data;
is_closest_waypoint_subscribed_ = true;
}
同样直接用了接收的信息,并把is_closest_waypoint_subscribed_
参数设置为true
void updateVelocity()
{
if (use_ctrl_cmd == false)
return;
current_velocity_.linear.x += linear_acceleration_ / (double)LOOP_RATE;
current_velocity_.angular.z = current_velocity_.linear.x * std::sin(steering_angle_) / wheel_base_;
}
上面是实现速度的更新,和角速度更新.
void publishOdometry() { static ros::Time current_time = ros::Time::now(); static ros::Time last_time = ros::Time::now(); static geometry_msgs::Pose pose; static double th = 0; static tf::TransformBroadcaster tf_broadcaster; static double steering_angle = 0.0; if (!pose_set_) { pose.position = initial_pose_.position; pose.orientation = initial_pose_.orientation; th = tf::getYaw(pose.orientation); ROS_INFO_STREAM("pose set : (" << pose.position.x << " " << pose.position.y << " " << pose.position.z << " " << th << ")"); pose_set_ = true; } if (waypoint_set_ && is_closest_waypoint_subscribed_) pose.position.z = current_waypoints_.getWaypointPosition(closest_waypoint_).z; double vx = current_velocity_.linear.x; double vth = current_velocity_.angular.z; current_time = ros::Time::now(); // compute odometry in a typical way given the velocities of the robot std::random_device rnd; std::mt19937 mt(rnd()); std::uniform_real_distribution<double> rnd_dist(0.0, 2.0); double rnd_value_x = rnd_dist(mt) - 1.0; double rnd_value_y = rnd_dist(mt) - 1.0; double rnd_value_th = rnd_dist(mt) - 1.0; double dt = (current_time - last_time).toSec(); double delta_x = (vx * cos(th)) * dt + rnd_value_x * position_error_; double delta_y = (vx * sin(th)) * dt + rnd_value_y * position_error_; double delta_th = vth * dt + rnd_value_th * angle_error_ * M_PI / 180; pose.position.x += delta_x; pose.position.y += delta_y; th += delta_th; pose.orientation = tf::createQuaternionMsgFromYaw(th); // first, we'll publish the transform over tf geometry_msgs::TransformStamped odom_trans; odom_trans.header.stamp = current_time; odom_trans.header.frame_id = MAP_FRAME; odom_trans.child_frame_id = SIMULATION_FRAME; odom_trans.transform.translation.x = pose.position.x; odom_trans.transform.translation.y = pose.position.y; odom_trans.transform.translation.z = pose.position.z; odom_trans.transform.rotation = pose.orientation; // send odom transform tf_broadcaster.sendTransform(odom_trans); geometry_msgs::TransformStamped lidar_trans; lidar_trans.header.stamp = odom_trans.header.stamp; lidar_trans.header.frame_id = SIMULATION_FRAME; lidar_trans.child_frame_id = LIDAR_FRAME; lidar_trans.transform.translation.z += lidar_height_; lidar_trans.transform.rotation.w = 1; // send lidar transform tf_broadcaster.sendTransform(lidar_trans); // next, we'll publish the odometry message over ROS std_msgs::Header h; h.stamp = current_time; h.frame_id = MAP_FRAME; geometry_msgs::PoseStamped ps; ps.header = h; ps.pose = pose; geometry_msgs::TwistStamped ts; ts.header = h; ts.twist.linear.x = vx; ts.twist.angular.z = vth; autoware_msgs::VehicleStatus vs; vs.header = h; vs.header.frame_id = "/can"; vs.speed = vx * 3.6; // [m/s] to [km/h] if (std::fabs(vx) > 1.0E-2) { steering_angle = std::atan(vth * wheel_base_ / vx); // [rad] } vs.angle = steering_angle; // publish the message odometry_publisher_.publish(ps); velocity_publisher_.publish(ts); vehicle_status_publisher_.publish(vs); last_time = current_time; }
主要内容如函数中介绍计算里程计的发布位置,发送一些transform,最终发布出位姿和速度及汽车状态的内容.
总的看来wf_simulator节点是用来计算模拟速度和位置的,但是我在看代码时遇到一些问题,如current_velocity_
的获取及"vehicle_cmd"
的回调函数中的公式原理
由于水平有限以上内容仅是自己学习理解过程,各位读者朋友发现问题之处还请帮忙提出,不胜感激。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。