├── config ├── config.yaml~ └── config.yaml ├── src ├── husky_highlevel_controller_node.cpp ├── emergency_stop_server.cpp ├── emergency_stop_client.cpp └── HuskyHighlevelController.cpp ├── package.xml ├── launch ├── husky_exercise.launch~ ├── husky_ekf_bag.launch └── husky_pillar.launch ├── include └── husky_highlevel_controller │ └── HuskyHighlevelController.hpp ├── worlds ├── singlePillar.world └── driveThrough.world ├── CMakeLists.txt ├── multiplot_config └── husky_position.xml └── README.md /config/config.yaml~: -------------------------------------------------------------------------------- 1 | husky: 2 | topic_name: /scan 3 | queue_size: 1 4 | -------------------------------------------------------------------------------- /config/config.yaml: -------------------------------------------------------------------------------- 1 | husky: 2 | topic_name: /scan 3 | queue_size: 1 4 | control_gain: 0.5 5 | -------------------------------------------------------------------------------- /src/husky_highlevel_controller_node.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "husky_highlevel_controller/HuskyHighlevelController.hpp" 3 | 4 | int main(int argc, char** argv) 5 | { 6 | ros::init(argc, argv, "husky_highlevel_controller"); 7 | ros::NodeHandle nodeHandle("~"); 8 | 9 | husky_highlevel_controller::HuskyHighlevelController huskyHighlevelController(nodeHandle); 10 | 11 | ros::spin(); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | husky_highlevel_controller 4 | 0.1.1 5 | The husky_highlevel_controller package 6 | Yimeng Lu 7 | BSD 8 | Yimeng Lu 9 | 10 | catkin 11 | 12 | roscpp 13 | sensor_msgs 14 | std_stvs 15 | 16 | -------------------------------------------------------------------------------- /launch/husky_exercise.launch~: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /launch/husky_ekf_bag.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/emergency_stop_server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "husky_highlevel_controller/HuskyHighlevelController.hpp" 3 | #include 4 | 5 | ros::Publisher start_stop_publ; 6 | 7 | bool stop_(std_srvs::SetBool::Request &request, std_srvs::SetBool::Response &response){ 8 | if(request.data == false){ 9 | start_stop_publ.publish(false); 10 | response.success = false; 11 | response.message = "False Todoki mashita! "; 12 | // ROS_DEBUG_STREAM("SokaSoka"); 13 | } 14 | if(request.data == true){ 15 | start_stop_publ.publish(true); 16 | response.success = true; 17 | response.message = "True Todoki mashita! "; 18 | // ROS_DEBUG_STREAM("NaniNani"); 19 | } 20 | return true; 21 | } 22 | 23 | int main(int argc, char **argv){ 24 | ros::init(argc, argv, "emergency_stop_server"); 25 | ros::NodeHandle nh; 26 | start_stop_publ = nh.advertise("/start_stop", 1); 27 | ros::ServiceServer service = nh.advertiseService("emergency_stop", stop_); 28 | ros::spin(); 29 | return 0; 30 | } 31 | 32 | 33 | -------------------------------------------------------------------------------- /launch/husky_pillar.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/emergency_stop_client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "husky_highlevel_controller/HuskyHighlevelController.hpp" 3 | #include 4 | #include 5 | 6 | int main(int argc, char **argv) { 7 | ros::init(argc, argv, "emergency_stop_client"); 8 | if (argc != 2) { 9 | ROS_INFO("usage incorrect"); 10 | return 1; 11 | } 12 | ros::NodeHandle nh; 13 | 14 | ros::ServiceClient client = nh.serviceClient("emergency_stop"); 15 | std_srvs::SetBool service; 16 | //for(int i = 0; i < argc; i++){ 17 | // ROS_INFO("%d %s \n", i, argv[i]); 18 | //} 19 | if(atoi(argv[1]) == 1){ 20 | ROS_INFO("It's true"); 21 | service.request.data = true; 22 | }else{ 23 | service.request.data = false; 24 | } 25 | if (client.call(service)) { 26 | // ROS_INFO("Result: %B", (bool)service.response.success); 27 | if(service.response.success == true){ 28 | 29 | ROS_DEBUG_STREAM(service.response.message); 30 | } else{ 31 | 32 | ROS_DEBUG_STREAM(service.response.message); 33 | } 34 | } else { 35 | ROS_ERROR("Failed to call service emergency_stop"); 36 | return 1; 37 | } 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /include/husky_highlevel_controller/HuskyHighlevelController.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #define pi 3.1419265358 13 | 14 | using std::vector; 15 | using std::string; 16 | 17 | namespace husky_highlevel_controller { 18 | 19 | /*! 20 | * Class containing the Husky Highlevel Controller 21 | */ 22 | class HuskyHighlevelController { 23 | public: 24 | /*! 25 | * Constructor. 26 | */ 27 | HuskyHighlevelController(ros::NodeHandle& nodeHandle); 28 | 29 | /*! 30 | * Destructor. 31 | */ 32 | virtual ~HuskyHighlevelController(); 33 | 34 | private: 35 | ros::NodeHandle nodeHandle_; 36 | void laser_scan_Callback( const sensor_msgs::LaserScan::ConstPtr& laser_scan_msgs ); 37 | vector laser_scan_distance; 38 | string topic_name; 39 | int queue_size; 40 | float minDistance; 41 | float pillarAngle; 42 | void husky_angle_controller(float speed, float angle); 43 | geometry_msgs::Twist cmd_vel_command; 44 | float control_gain; 45 | int direction_index; 46 | void pillar_vis_marker_func(); 47 | 48 | void start_stop_Callback( const std_msgs::Bool trigger ); 49 | bool status_; 50 | 51 | ros::Subscriber laser_scan_subs; 52 | ros::Publisher controlled_cmd_vel_publ; 53 | ros::Publisher pillar_vis_publ; 54 | ros::Subscriber start_stop_subs; 55 | 56 | }; 57 | 58 | } /* namespace */ 59 | -------------------------------------------------------------------------------- /worlds/singlePillar.world: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | model://sun 7 | 8 | 9 | 10 | model://ground_plane 11 | 12 | 13 | 14 | 20 5 0.5 0 -0 0 15 | 16 | 17 | 1 18 | 19 | 0.145833 20 | 0 21 | 0 22 | 0.145833 23 | 0 24 | 0.125 25 | 26 | 27 | 28 | 29 | 30 | 0.2 31 | 2 32 | 33 | 34 | 10 35 | 36 | 37 | 38 | 39 | 0.2 40 | 2 41 | 42 | 43 | 44 | 48 | 49 | 50 | 0 51 | 0 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.3) 2 | project(husky_highlevel_controller) 3 | 4 | ## Use C++11 5 | add_definitions(--std=c++11) 6 | 7 | ## Find catkin macros and libraries 8 | find_package(catkin REQUIRED COMPONENTS 9 | roscpp 10 | sensor_msgs 11 | std_srvs 12 | ) 13 | 14 | ################################### 15 | ## catkin specific configuration ## 16 | ################################### 17 | ## The catkin_package macro generates cmake config files for your package 18 | ## Declare things to be passed to dependent projects 19 | ## INCLUDE_DIRS: uncomment this if you package contains header files 20 | ## LIBRARIES: libraries you create in this project that dependent projects also need 21 | ## CATKIN_DEPENDS: catkin_packages dependent projects also need 22 | ## DEPENDS: system dependencies of this project that dependent projects also need 23 | catkin_package( 24 | INCLUDE_DIRS 25 | include 26 | # LIBRARIES 27 | CATKIN_DEPENDS 28 | roscpp 29 | sensor_msgs 30 | # DEPENDS 31 | ) 32 | 33 | ########### 34 | ## Build ## 35 | ########### 36 | 37 | ## Specify additional locations of header files 38 | ## Your package locations should be listed before other locations 39 | include_directories( 40 | include 41 | ${catkin_INCLUDE_DIRS} 42 | ) 43 | 44 | ## Declare a C++ executable 45 | add_executable(${PROJECT_NAME} 46 | src/${PROJECT_NAME}_node.cpp 47 | src/HuskyHighlevelController.cpp 48 | ) 49 | ## Specify libraries to link a library or executable target against 50 | target_link_libraries(${PROJECT_NAME} 51 | ${catkin_LIBRARIES} 52 | ) 53 | 54 | ## Declare a C++ executable 55 | add_executable(${PROJECT_NAME}_client 56 | src/emergency_stop_client.cpp 57 | ) 58 | ## Specify libraries to link a library or executable target against 59 | target_link_libraries(${PROJECT_NAME}_client 60 | ${catkin_LIBRARIES} 61 | ) 62 | 63 | ## Declare a C++ executable 64 | add_executable(${PROJECT_NAME}_server 65 | src/emergency_stop_server.cpp 66 | ) 67 | ## Specify libraries to link a library or executable target against 68 | target_link_libraries(${PROJECT_NAME}_server 69 | ${catkin_LIBRARIES} 70 | ) 71 | 72 | 73 | -------------------------------------------------------------------------------- /worlds/driveThrough.world: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | model://sun 7 | 8 | 9 | 10 | model://ground_plane 11 | 12 | 13 | 14 | 20 5 0.5 0 -0 0 15 | 16 | 17 | 1 18 | 19 | 0.145833 20 | 0 21 | 0 22 | 0.145833 23 | 0 24 | 0.125 25 | 26 | 27 | 28 | 29 | 30 | 0.2 31 | 2 32 | 33 | 34 | 10 35 | 36 | 37 | 38 | 39 | 0.2 40 | 2 41 | 42 | 43 | 44 | 48 | 49 | 50 | 0 51 | 0 52 | 53 | 54 | 55 | 56 | 57 | 20 8 0.5 0 -0 0 58 | 59 | 60 | 1 61 | 62 | 0.145833 63 | 0 64 | 0 65 | 0.145833 66 | 0 67 | 0.125 68 | 69 | 70 | 71 | 72 | 73 | 0.2 74 | 2 75 | 76 | 77 | 10 78 | 79 | 80 | 81 | 82 | 0.2 83 | 2 84 | 85 | 86 | 87 | 91 | 92 | 93 | 0 94 | 0 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /src/HuskyHighlevelController.cpp: -------------------------------------------------------------------------------- 1 | #include "husky_highlevel_controller/HuskyHighlevelController.hpp" 2 | 3 | namespace husky_highlevel_controller { 4 | 5 | HuskyHighlevelController::HuskyHighlevelController(ros::NodeHandle& nodeHandle) : 6 | nodeHandle_(nodeHandle) 7 | { 8 | nodeHandle.getParam("/husky_highlevel_controller/husky/topic_name", topic_name); 9 | nodeHandle.getParam("/husky_highlevel_controller/husky/queue_size", queue_size); 10 | nodeHandle.getParam("/husky_highlevel_controller/husky/control_gain", control_gain); 11 | ROS_INFO_STREAM(topic_name << " " << queue_size); 12 | laser_scan_subs = nodeHandle.subscribe( topic_name, queue_size, &HuskyHighlevelController::laser_scan_Callback, this); 13 | controlled_cmd_vel_publ = nodeHandle.advertise("/cmd_vel", 1); 14 | pillar_vis_publ = nodeHandle.advertise( "visualization_marker", 0 ); 15 | start_stop_subs = nodeHandle.subscribe( "/start_stop", 5, &HuskyHighlevelController::start_stop_Callback, this); 16 | } 17 | 18 | void HuskyHighlevelController::laser_scan_Callback( const sensor_msgs::LaserScan::ConstPtr& laser_scan_msgs ){ 19 | minDistance = 30; 20 | direction_index = 0; 21 | laser_scan_distance = laser_scan_msgs->ranges; 22 | ROS_INFO_STREAM("msgs received" << std::endl); 23 | for(int i = 0; i < laser_scan_distance.size(); i++){ 24 | 25 | if(minDistance > laser_scan_distance[i]) { 26 | minDistance = laser_scan_distance[i]; 27 | direction_index = i; 28 | } 29 | } 30 | pillarAngle = -0.785 + 1.5*pi/720 * direction_index; 31 | 32 | // should do sth here to stop/start 33 | if(status_ == true){ 34 | husky_angle_controller(2, pillarAngle); 35 | } else{ 36 | husky_angle_controller(0, pi/2); 37 | } 38 | 39 | 40 | controlled_cmd_vel_publ.publish(cmd_vel_command); 41 | 42 | 43 | pillar_vis_marker_func(); 44 | ROS_INFO_STREAM("The " << direction_index << "st message is chosen in " << laser_scan_distance.size() << " messages"); 45 | ROS_INFO_STREAM("minDistance detected by Lidar: " << minDistance << ", angle is " << pillarAngle); 46 | ROS_INFO_STREAM("Angle_min " << laser_scan_msgs->angle_min << ", Angle_increment " << laser_scan_msgs->angle_increment); 47 | } 48 | 49 | void HuskyHighlevelController::start_stop_Callback( const std_msgs::Bool trigger ){ 50 | if(trigger.data == true){ 51 | status_ = true; 52 | }else{ 53 | status_ = false; 54 | } 55 | } 56 | 57 | void HuskyHighlevelController::husky_angle_controller(float speed, float angle){ 58 | // let the husky go at a constant speed 59 | // read the angle towards the pillar, and use PID to turn the husky to the pillar (control the angle to 0) 60 | cmd_vel_command.linear.x = speed; 61 | cmd_vel_command.angular.z = control_gain * (0 - (angle-pi/2)); 62 | 63 | } 64 | 65 | void HuskyHighlevelController::pillar_vis_marker_func(){ 66 | visualization_msgs::Marker marker; 67 | marker.header.frame_id = "base_laser"; 68 | marker.header.stamp = ros::Time(); 69 | marker.ns = "my_namespace"; 70 | marker.id = 0; 71 | marker.type = visualization_msgs::Marker::SPHERE; 72 | marker.action = visualization_msgs::Marker::ADD; 73 | marker.pose.position.y = minDistance * cos(pillarAngle); 74 | marker.pose.position.x = minDistance * sin(pillarAngle); 75 | marker.pose.position.z = 1; 76 | marker.pose.orientation.x = 0.0; 77 | marker.pose.orientation.y = 0.0; 78 | marker.pose.orientation.z = 0.0; 79 | marker.pose.orientation.w = 0.0; 80 | marker.scale.x = 1; 81 | marker.scale.y = 1; 82 | marker.scale.z = 1; 83 | marker.color.a = 1.0; // Don't forget to set the alpha! 84 | marker.color.r = 0.0; 85 | marker.color.g = 0.0; 86 | marker.color.b = 1.0; 87 | //only if using a MESH_RESOURCE marker type: 88 | marker.mesh_resource = "package://pr2_description/meshes/base_v0/base.dae"; 89 | pillar_vis_publ.publish( marker ); 90 | } 91 | 92 | HuskyHighlevelController::~HuskyHighlevelController() 93 | { 94 | } 95 | 96 | } /* namespace */ 97 | -------------------------------------------------------------------------------- /multiplot_config/husky_position.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #ffffff 5 | #000000 6 | false 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | Untitled Axis 15 | 0 16 | true 17 | 18 | 19 | Untitled Axis 20 | 0 21 | true 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | pose/pose/position/x 30 | 0 31 | 32 | 1000 33 | 0 34 | 0 35 | -1000 36 | 0 37 | 38 | /odometry/filtered 39 | nav_msgs/Odometry 40 | 41 | 42 | pose/pose/position/y 43 | 0 44 | 45 | 1000 46 | 0 47 | 0 48 | -1000 49 | 0 50 | 51 | /odometry/filtered 52 | nav_msgs/Odometry 53 | 54 | 55 | 56 | #000000 57 | 0 58 | 59 | 60 | 1000 61 | 10 62 | 0 63 | 64 | 74 | 100 75 | Traj-XY 76 | 77 | 78 | 79 | true 80 | 81 | 30 82 | husky_position 83 | 84 | 85 | 86 | false 87 |
88 |
89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [ETHZ ROS lectures](http://www.rsl.ethz.ch/education-students/lectures/ros.html) 2 | 3 | ## Exercise 1 4 | 5 | - Way to publish to control gazebo husky (press Tab as much as you can) 6 | ``` 7 | rostopic pub /husky_velocity_controller/cmd_vel geometry_msgs/Twist -r 100 '[0.5,0,0]' '[0,0,0]' 8 | ``` 9 | - Need to rebuild and resource when codes get changed 10 | - If the program can't find something (eg., .world file), look at the launch file, use roscd to find correct path and put it there 11 | 12 | ## [Exercise 2](https://www.ethz.ch/content/dam/ethz/special-interest/mavt/robotics-n-intelligent-systems/rsl-dam/ROS2017/exercise2.pdf) 13 | 14 | - yaml file: mind the format and the way to write topic name (/scan with "/") 15 | - .hpp: include essential header files(msgs, C++ libraries, etc) 16 | - .cpp: 17 | - callback: use -> for vector 18 | - CORRECT WAY to write a subscriber and do some processing in callback function 19 | - .launch: rosparam format 20 | - Use RViz to visualize stuffs is better in Gazebo 21 | 22 | ## [Exercise 3](https://www.ethz.ch/content/dam/ethz/special-interest/mavt/robotics-n-intelligent-systems/rsl-dam/ROS2017/exercise3.pdf) 23 | 24 | ### Husky bumps into pillar 25 | - Check the exact value of the members in sensor_msgs/LaserScan, it might not be what you imagined. 26 | - Need to adjust control gain to get good performance. 27 | - CORRECT WAY to write a publisher. 28 | 29 | ### Visualize pillar observation 30 | 31 | - Another publisher 32 | - Include headers for message 33 | - Compute using angle and distance, remember to use correct tf link to visualize in Gazebo 34 | 35 | ## [Exercise 4](https://www.ethz.ch/content/dam/ethz/special-interest/mavt/robotics-n-intelligent-systems/rsl-dam/ROS2017/exercise4.pdf) 36 | 37 | - Make sure how nodes communicate by looking at their subs and pubs, and looking at topics as well 38 | - To launch ekf node, need to load parameters from somewhere. In this example it's in husky_control package 39 | - To use rqt_multiplot more efficiently, one can save the configuration file and load it directly next time 40 | - For time, if anything in gazebo is loaded(eg., a world), the time will be set to use_sim_time. otherwise this should be set in the launch file by 41 | 42 | ``` 43 | 44 | 45 | ``` 46 | 47 | And be sure to add --clock while playing rosbag 48 | 49 | ``` 50 | rosbag play data.bag --clock 51 | ``` 52 | 53 | - For husky visualization in RViz, because it's not loaded via gazebo, we need to do 2 things 54 | - Add a robot_state_publisher node in the launch file(If you notice that there's already /ekf_localization_node publishing to /tf topic. Here's one explanation: because frame and husky config info is not complete only by /ekf's publications) 55 | 56 | ``` 57 | 58 | ``` 59 | 60 | - Husky's configuration need to be written in launch file. Do this by imitating spawn_husky.launch. Namely, these lines: 61 | 62 | ``` 63 | 64 | 65 | 66 | 67 | 68 | 73 | ``` 74 | 75 | ## [Exercise 5](https://www.ethz.ch/content/dam/ethz/special-interest/mavt/robotics-n-intelligent-systems/rsl-dam/ROS2017/exercise5.pdf) 76 | 77 | - Implemented a service server to send bool info to a topic, /start_stop to control manual stop of husky 78 | ``` 79 | rosservice call /emergency_stop "data: true" 80 | ``` 81 | - Implemented a service client to communicate with service server, send command from the client and receive feedback as an addition. 82 | ``` 83 | rosrun husky_highlevel_controller husky_highlevel_controller_client 1 // for start, anything other than 1 for stop 84 | ``` 85 | - Because there are more than one executables now, need to do a lot of modifications to CMakeLists.txt, especially in 86 | - find_package 87 | - add_executable 88 | - target\_link\_libraries 89 | 90 | - Need to modify package.xml accordingly 91 | - Created a subs in Husky node to enable/disable husky by sending controller real signals or a constant 92 | - Corresponding publ is in service server, as it's running all the time 93 | - Don't know why but ROS\_DEBUG\_MESSAGE only shows while calling service server in console 94 | - Didn't write client in launch file now. Can be done in future or add this in other parts of the program(automatic stop) 95 | 96 | ## References 97 | 98 | - https://www.ethz.ch/content/dam/ethz/special-interest/mavt/robotics-n-intelligent-systems/rsl-dam/ROS2017/exercise5.pdf 99 | - https://github.com/fairlight1337/ros_service_examples/blob/master/CMakeLists.txt 100 | - http://docs.ros.org/jade/api/std_srvs/html/srv/SetBool.html 101 | - https://www.ethz.ch/content/dam/ethz/special-interest/mavt/robotics-n-intelligent-systems/rsl-dam/ROS2017/lecture4.pdf 102 | - http://wiki.ros.org/ROS/Tutorials/ExaminingServiceClient 103 | 104 | 105 | 106 | --------------------------------------------------------------------------------